Block Chain Introduction Course: Source Code Analysis p2p-dial.go

Keywords: Blockchain network

In the second half of 2018, the block chain industry is gradually fading away from the impetuosity and rationality at the beginning of its development. On the surface, it seems that the demand and status of relevant talents are declining. But in fact, it is the gradual decline of the initial bubble that gives people more attention to the real technology of the block chain.
dial.go is mainly responsible for establishing links in p2p. For example, discover nodes that establish links. Establish links with nodes. Find the address of the specified node by discover. And other functions.

dial.go uses a dailstate data structure to store intermediate state, which is the core data structure of dial function.

    // dialstate schedules dials and discovery lookups.
    // it get's a chance to compute new tasks on every iteration
    // of the main loop in Server.run.
    type dialstate struct {
        maxDynDials int                     //Maximum number of dynamic node links
        ntab        discoverTable           //Discovery Table is used for node queries
        netrestrict *netutil.Netlist
    
        lookupRunning bool
        dialing       map[discover.NodeID]connFlag      //Linking Node
        lookupBuf     []*discover.Node // current discovery lookup results//Current discovery query results
        randomNodes   []*discover.Node // filled from Table//nodes randomly queried from discoverTable
        static        map[discover.NodeID]*dialTask  //Static nodes. 
        hist          *dialHistory
    
        start     time.Time        // time when the dialer was first used
        bootnodes []*discover.Node // default dials when there are no peers // This is a built-in node. If no other node is found. Then use links to these nodes.
    }

The creation process of dailstate.

    func newDialState(static []*discover.Node, bootnodes []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
        s := &dialstate{
            maxDynDials: maxdyn,
            ntab:        ntab,
            netrestrict: netrestrict,
            static:      make(map[discover.NodeID]*dialTask),
            dialing:     make(map[discover.NodeID]connFlag),
            bootnodes:   make([]*discover.Node, len(bootnodes)),
            randomNodes: make([]*discover.Node, maxdyn/2),
            hist:        new(dialHistory),
        }
        copy(s.bootnodes, bootnodes)
        for _, n := range static {
            s.addStatic(n)
        }
        return s
    }
  

The most important method of dail is the newTasks method. This method is used to generate tasks. task is an interface. There's a Do approach.


  
    type task interface {
        Do(*Server)
    }

    func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
        if s.start == (time.Time{}) {
            s.start = now
        }
    
        var newtasks []task
        //AdDial is an internal method, first checkDial checks the nodes. Then set the state, and finally add the nodes to the newtasks queue.
        addDial := 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
        }
    
        // Compute number of dynamic dials necessary at this point.
        needDynDials := s.maxDynDials
        //First, determine the type of connection that has been established. If it is a dynamic type. Then the number of dynamic links needed to be established is reduced.
        for _, p := range peers {
            if p.rw.is(dynDialedConn) {
                needDynDials--
            }
        }
        //Then determine the link being established. If it is a dynamic type. Then the number of dynamic links needed to be established is reduced.
        for _, flag := range s.dialing {
            if flag&dynDialedConn != 0 {
                needDynDials--
            }
        }
    
        // Expire the dial history on every invocation.
        s.hist.expire(now)
    
        // Create dials for static nodes if they are not connected.
        //View all static types. Create links if you can.
        for id, t := range s.static {
            err := s.checkDial(t.dest, peers)
            switch err {
            case errNotWhitelisted, errSelf:
                log.Warn("Removing static dial candidate", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP, Port: int(t.dest.TCP)}, "err", err)
                delete(s.static, t.dest.ID)
            case nil:
                s.dialing[id] = t.flags
                newtasks = append(newtasks, t)
            }
        }
        // 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 there are no links at present. And no links were created in 20 seconds. Then use bootnode to create links.
        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--
            }
        }
        // Use random nodes from the table for half of the necessary
        // dynamic dials.
        //Otherwise, use 1/2 of the random nodes to create links.
        randomCandidates := needDynDials / 2
        if randomCandidates > 0 {
            n := s.ntab.ReadRandomNodes(s.randomNodes)
            for i := 0; i < randomCandidates && i < n; i++ {
                if addDial(dynDialedConn, s.randomNodes[i]) {
                    needDynDials--
                }
            }
        }
        // Create dynamic dials from random lookup results, removing tried
        // items from the result buffer.
        i := 0
        for ; i < len(s.lookupBuf) && needDynDials > 0; i++ {
            if addDial(dynDialedConn, s.lookupBuf[i]) {
                needDynDials--
            }
        }
        s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])]
        // Launch a discovery lookup if more candidates are needed.
        // Even then, you can't create enough dynamic links. Then create a discoverTask to find other nodes on the network. Put in lookup Buf
        if len(s.lookupBuf) < needDynDials && !s.lookupRunning {
            s.lookupRunning = true
            newtasks = append(newtasks, &discoverTask{})
        }
    
        // Launch a timer to wait for the next node to expire if all
        // candidates have been tried and no task is currently active.
        // This should prevent cases where the dialer logic is not ticked
        // because there are no pending events.
        // If there is currently no task to do, create a sleep task to return.
        if nRunning == 0 && len(newtasks) == 0 && s.hist.Len() > 0 {
            t := &waitExpireTask{s.hist.min().exp.Sub(now)}
            newtasks = append(newtasks, t)
        }
        return newtasks
    }

The checkDial method checks whether a task needs to create a link.

DiaTask. Do method, different task s have different Do methods. dailTask is primarily responsible for establishing links. If t.dest does not have an ip address. Then try to query the ip address through resolve. Then call the dial method to create the link. For static nodes. If the first failure occurs, the resolve static node is attempted again. Then try dial (because the ip of the static node is configured). If the ip address of the static node changes. So let's try the new address of the resolve static node, and then call the link.)

Resolve method. This method mainly calls the Resolve method of discover network. If it fails, try again overtime

dial method, which is the actual network connection operation. This method is mainly accomplished by srv.SetupConn method, which will be analyzed when Server.go is analyzed later.

The Do method of discoverTask and waitExpireTask,


 

Posted by abhilashdas on Fri, 25 Jan 2019 06:42:13 -0800