Block Chain Introduction Course eth Source Code Analysis p2p-udp.go Source Code Analysis (I)

Keywords: Blockchain network REST Mac React

Brotherly Block Chain Introduction Course eth Source Code Analysis p2p-udp.go Source Code Analysis (I)
The network discovery protocol of p2p uses Kademlia protocol to deal with network node discovery. Node lookup and update. Kademlia protocol uses UDP protocol for network communication.

Reading the code in this section suggests looking at the Kademlia protocol profile in references to see what the Kademlia protocol is.

First, look at the data structure. The network transmits four kinds of data packets (UDP protocol is based on message protocol). The transmission is a single packet, ping,pong,findnode and neighbors. Four message formats are defined below.

    // RPC packet types
    const (
        pingPacket = iota + 1 // zero is 'reserved'
        pongPacket
        findnodePacket
        neighborsPacket
    )
    // RPC request structures
    type (
        ping struct {
            Version uint //Protocol Version
            From, To rpcEndpoint      //Source IP Address Destination IP Address
            Expiration uint64           //timeout
            // Ignore additional fields (for forward compatibility).
            //Ignorable fields. For forward compatibility
            Rest []rlp.RawValue `rlp:"tail"`
        }
    
        // pong is the reply to ping.
        // Response of ping package
        pong struct {
            // This field should mirror the UDP envelope address
            // of the ping packet, which provides a way to discover the
            // the external address (after NAT).
            // Destination IP Address
            To rpcEndpoint
            // This indicates that the pong package responds to the ping package. hash value containing ping package
            ReplyTok []byte // This contains the hash of the ping packet.
            //The absolute time when the package timed out. If the time of receiving a package exceeds this time, the package is considered to be overtime.
            Expiration uint64 // Absolute timestamp at which the packet becomes invalid.
            // Ignore additional fields (for forward compatibility).
            Rest []rlp.RawValue `rlp:"tail"`
        }
        // findnode is used to query nodes closer to target
        // findnode is a query for nodes close to the given target.
        findnode struct {
            // destination node
            Target NodeID // doesn't need to be an actual public key
            Expiration uint64
            // Ignore additional fields (for forward compatibility).
            Rest []rlp.RawValue `rlp:"tail"`
        }
    
        // reply to findnode
        // findnode's response
        neighbors struct {
            //Node values closer to target.
            Nodes []rpcNode
            Expiration uint64
            // Ignore additional fields (for forward compatibility).
            Rest []rlp.RawValue `rlp:"tail"`
        }
    
        rpcNode struct {
            IP net.IP // len 4 for IPv4 or 16 for IPv6
            UDP uint16 // for discovery protocol
            TCP uint16 // for RLPx protocol
            ID NodeID
        }
    
        rpcEndpoint struct {
            IP net.IP // len 4 for IPv4 or 16 for IPv6
            UDP uint16 // for discovery protocol
            TCP uint16 // for RLPx protocol
        }
    )


//Two interface types are defined, and the package interface type should assign different handle methods to four different types of packages. The conn interface defines the function of a udp connection.


    type packet interface {
        handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
        name() string
    }
    
    type conn interface {
        ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
        WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error)
        Close() error
        LocalAddr() net.Addr
    }


udp The last field to note is the structure*Table yes go The anonymous section inside. In other words udp Anonymous fields can be invoked directly Table Method.


    // udp implements the RPC protocol.
    type udp struct {
        conn conn                    //network connections
        netrestrict *netutil.Netlist
        priv *ecdsa.PrivateKey       //Private key, its ID is generated through this.
        ourEndpoint rpcEndpoint
    
        addpending chan *pending            //Used to apply for a pending
        gotreply chan reply               //Queues used to get responses
    
        closing chan struct{}               //Queues to close
        nat nat.Interface               
    
        *Table
    }

pending and reply Structure. These two structures are internal to users go routine A structure for communicating with each other.

    // pending represents a pending reply.
    // some implementations of the protocol wish to send more than one
    // reply packet to findnode. in general, any neighbors packet cannot
    // be matched up with a specific findnode packet.
    // our implementation handles this by storing a callback function for
    // each pending reply. incoming packets from a node are dispatched
    // to all the callback functions for that node.
    // pending structure represents waiting for a reply
    // We implement this by storing a callback for each pending reply. All data packets from a node are assigned to the corresponding callback of that node.
    type pending struct {
        // these fields must match in the reply.
        from NodeID
        ptype byte
    
        // time when the request must complete
        deadline time.Time
    
        // callback is called when a matching reply arrives. if it returns
        // true, the callback is removed from the pending reply queue.
        // if it returns false, the reply is considered incomplete and
        // the callback will be invoked again for the next matching reply.
        //If the return value is true. callback is then removed from the queue. If false is returned, the reply is considered incomplete and will continue to wait for the next reply.
        callback func(resp interface{}) (done bool)
    
        // errc receives nil when the callback indicates completion or an
        // error if no further reply is received within the timeout.
        errc chan<- error
    }
    
    type reply struct {
        from NodeID
        ptype byte
        data interface{}
        // loop indicates whether there was
        // a matching request by sending on this channel.
        //A request is matched by sending a message to the channel.
        matched chan<- bool
    }


UDP Creation

    // ListenUDP returns a new table that listens for UDP packets on laddr.
    func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, error) {
        addr, err := net.ResolveUDPAddr("udp", laddr)
        if err != nil {
            return nil, err
        }
        conn, err := net.ListenUDP("udp", addr)
        if err != nil {
            return nil, err
        }
        tab, _, err := newUDP(priv, conn, natm, nodeDBPath, netrestrict)
        if err != nil {
            return nil, err
        }
        log.Info("UDP listener up", "self", tab.self)
        return tab, nil
    }
    
    func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, *udp, error) {
        udp := &udp{
            conn: c,
            priv: priv,
            netrestrict: netrestrict,
            closing: make(chan struct{}),
            gotreply: make(chan reply),
            addpending: make(chan *pending),
        }
        realaddr := c.LocalAddr().(*net.UDPAddr)
        if natm != nil { //natm nat mapping is used to obtain an external network address
            if !realaddr.IP.IsLoopback() { //If the address is a local loopback address
                go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
            }
            // TODO: react to external IP changes over time.
            if ext, err := natm.ExternalIP(); err == nil {
                realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
            }
        }
        // TODO: separate TCP port
        udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
        //Creating a table will be introduced later. Kademlia's main logic is implemented in this class.
        tab, err := newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath)
        if err != nil {
            return nil, nil, err
        }
        udp.Table = tab //Assignment of anonymous segments
        
        go udp.loop()       //go routine
        go udp.readLoop()   //Used for network data reading.
        return udp.Table, udp, nil
    }


 

Posted by johnkelly on Fri, 01 Feb 2019 00:54:15 -0800