Brother even blockchain Getting started tutorial ETH source code Analysis P2p-udp.go Source Analysis (ii)

Source: Internet
Author: User

The ping method with pending is addressed before the pending is waiting for a reply. Here the code to analyze is how to implement waiting reply.
The pending method sends the pending structure to addpending. Then wait for the message to be processed and received.

Ping sends a PING message to the given node and waits for a reply.func (T *udp) Ping (toid NodeID, Toaddr *net. UDPADDR) Error {//Todo:maybe check for ReplyTo field in callback to measure RTT ERRC: = t.pending (Toid, Pongpacke T, func (interface{}) bool {return true}) T.send (Toaddr, Pingpacket, &ping{version:version, from: T.ourendpoint, To:makeendpoint (toaddr, 0),//Todo:maybe use known TCP port from DB Expiration:uint64 (Tim E.now (). ADD (Expiration).  Unix ()),}) return <-errc}//pending adds a reply callback to the pending reply queue.//see the documentation of Type pending for a detailed explanation.func (T *udp) pending (ID NodeID, PType Byte, callback func (interface{}) bool) &lt    ;-chan Error {ch: = make (chan error, 1) P: = &pending{from:id, Ptype:ptype, Callback:callback, errc:ch}    Select {Case t.addpending <-P://loop would handle it case <-t.closing:ch <-errclosed } RetuRN CH} 

The processing of the

addpending message. The Newudp method was called before UDP was created. Two goroutine were launched inside. The loop () is used to process pending messages.

Loop runs in its own goroutine. It keeps track of//the refresh timer and the pending reply Queue.func (t *udp) loop () {var (plist = list). New () Timeout = time. Newtimer (0) Nexttimeout *pending//Head of plist when timeout is last reset conttimeouts = 0//Number of Continuous timeouts to do NTP checks ntpwarntime = time. Unix (0, 0)) <-timeout. C//Ignore first timeout defer timeout. Stop () Resettimeout: = Func () {//The main function of this method is to see if there are pending messages in the queue that need to be timed out. If there is.        Then//Wake up time-out based on the first timeout. If plist. Front () = = Nil | | Nexttimeout = = plist. Front ().        Value {return}//Start the timer so it fires when the next pending reply have expired. Now: = time. Now () for el: = plist. Front (); El! = nil; el = el. Next () {nexttimeout = el. Value. (*pending) If dist: = NextTimeout.deadline.Sub (now); Dist < 2*resptimeout {timeout.     Reset (Dist) return       }//Remove pending replies whose deadline is too far in the//future.            These can occur if the system clock jumped//backwards after the deadline is assigned.            If there is a message that the deadline is in a far future, then the direct setting is timed out and then removed.            This situation can occur when the system time is modified, and if not handled may cause the blockage to take too long. NEXTTIMEOUT.ERRC <-errclockwarp plist. Remove (EL)} nexttimeout = nil timeout.        Stop ()} for {resettimeout ()//first processing time-out. Select {Case <-t.closing://Receive closing information. Timeout all blocked queues for el: = plist. Front (); El! = nil; el = el. Next () {el. Value. (*pending). ERRC <-errclosed} return case P: = <-t.addpending://Add a pending setting Deadlin E P.deadline = time. Now (). ADD (resptimeout) plist. Pushback (p) case r: = <-t.gotreply://Receive a reply to find the matching pending var matched bool for el: = pl Ist. Front (); El! = nil; el = el.               Next () { P: = el. Value. (*pending) If P.from = = R.from && P.ptype = = R.ptype {//if from the same person.                    And the same type matched = TRUE//Remove the Matcher if its callback indicates That all replies has been received.                    This is//required-packet types that expect multiple//reply packets. If P.callback (r.data) {//If the callback return value is true. Description pending has been completed. Write nil to P.ERRC.                        Pending completed. P.ERRC <-Nil plist.                    Remove (EL)}//Reset The Continuous timeout counter (Time drift detection)  conttimeouts = 0}} r.matched <-matched//reply case written to matched Now: = <-timeout.            C://processing timeout information nexttimeout = nil//Notify and remove callbacks whose deadline is in the past. For el: = plist. Front (); El! = nil; el = el. Next () {p: = el. Value. (*pending) if now. After (p.deadline) | | Now. Equal (p.deadline) {//If time-out writes timeout information and removes P.ERRC <-errtimeout plist. Remove (EL) conttimeouts++}}//If we ' ve accumulated too many Tim Eouts, do an NTP time sync check if conttimeouts > ntpfailurethreshold {//If the continuous timeout is multiple times. Then check whether the time is out of sync.                Synchronization with the NTP server. If time. Since (ntpwarntime) >= Ntpwarningcooldown {ntpwarntime = time.  Now () Go Checkclockdrift ()} conttimeouts = 0}}}

The

above sees the processing of pending. However, the loop () method also has a gotreply treatment. This is really readloop () in this goroutine.

Readloop runs in its own goroutine. It handles incoming UDP Packets.func (t *udp) Readloop () {defer t.conn.close ()//Discovery packets is defined to    be no larger than bytes.    Packets larger than this size would be cut at the end and treated//as invalid because their hash won ' t match. BUF: = Make ([]byte, 1280x720) for {nbytes, from, err: = T.CONN.READFROMUDP (BUF) if Netutil.            Istemporaryerror (ERR) {//Ignore temporary read errors. Log. Debug ("Temporary UDP read error", "Err", err) Continue} else if err! = Nil {//Shut down th            e loop for permament errors. Log. Debug ("UDP read error", "Err", err) return} t.handlepacket (from, Buf[:nbytes])}}func (t *udp ) Handlepacket (from *net. UDPADDR, buf []byte) error {packet, Fromid, hash, err: = Decodepacket (BUF) if err! = Nil {log. Debug ("Bad DISCV4 Packet", "addr", From, "err", err) return err}   Err = Packet.handle (t, from, Fromid, hash) log. Trace ("<<" +packet.name (), "addr", From, "err", err) return Err}func (req *ping) handle (T-*udp, from *net. UDPADDR, Fromid NodeID, Mac []byte) error {if expired (req. Expiration) {return errexpired} t.send (from, Pongpacket, &pong{to:makeendpoint (from, req. FROM.TCP), Replytok:mac, Expiration:uint64 (time. Now (). ADD (Expiration). Unix ()),}) if!t.handlereply (Fromid, Pingpacket, req) {//Note:we ' re ignoring the provided IP address righ T now go T.bond (true, Fromid, from, req.  FROM.TCP)} return Nil}func (t *udp) handlereply (from NodeID, PType Byte, req packet) bool {matched: = Make (chan BOOL, 1) Select {case t.gotreply <-reply{from, PType, req, matched}://loop would handle it Retu RN <-matched case <-t.closing:return false}}

The process of handling UDP roughly is described above. The following is a description of the main processing business under UDP. UDP mainly sends two kinds of requests, the corresponding will also receive the two requests sent by others, corresponding to these two kinds of requests will produce two kinds of responses.

Ping request, you can see the ping request to get a pong answer. and then return.

// ping sends a ping message to the given node and waits for a reply.func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {    // TODO: maybe check for ReplyTo field in callback to measure RTT    errc := t.pending(toid, pongPacket, func(interface{}) bool { return true })    t.send(toaddr, pingPacket, &ping{        Version: Version,        From: t.ourEndpoint,        To: makeEndpoint(toaddr, 0), // TODO: maybe use known TCP port from DB        Expiration: uint64(time.Now().Add(expiration).Unix()),    })    return <-errc}

Pong answer if the pong answer does not match to a corresponding ping request. Then return the errunsolicitedreply exception.

func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {    if expired(req.Expiration) {        return errExpired    }    if !t.handleReply(fromID, pongPacket, req) {        return errUnsolicitedReply    }    return nil}

Findnode requests, sends a FINDNODE request, and waits for node to respond to K neighbors.

 //Findnode sends a FINDNODE request to the given node and waits until//the node have sent up to K Neighbors.fu NC (t *udp) Findnode (toid NodeID, Toaddr *net. UDPADDR, Target NodeID) ([]*node, error) {nodes: = make ([]*node, 0, bucketsize) nreceived: = 0 ERRC: = t.pending (Toid, Neighborspacket, func (R interface{}) bool {reply: = R. (*neighbors) for _, RN: = range reply. Nodes {nreceived++ N, err: = T.nodefromrpc (TOADDR, RN) if err! = Nil {lo G.trace ("Invalid neighbor Node received", "IP", RN. IP, "addr", toaddr, "err", err) Continue} nodes = Append (nodes, N)} r Eturn nreceived >= Bucketsize}) t.send (Toaddr, Findnodepacket, &findnode{Target:target, EXPI Ration:uint64 (time. Now (). ADD (Expiration). Unix ()),}) Err: = <-ERRC return nodes, err}  

Neighbors response, very simple. Send the response to the gotreply queue. If no matching findnode request is found. Return errunsolicitedreply Error

func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {    if expired(req.Expiration) {        return errExpired    }    if !t.handleReply(fromID, neighborsPacket, req) {        return errUnsolicitedReply    }    return nil}

Receive a ping request sent by another node and send a Pong answer. If there is no match to the previous pending (the result is not a request from your own party). The Bond method is called to add the node to its bucket cache. (This part of the principle in Table.go will be described in detail)

func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {    if expired(req.Expiration) {        return errExpired    }    t.send(from, pongPacket, &pong{        To: makeEndpoint(from, req.From.TCP),        ReplyTok: mac,        Expiration: uint64(time.Now().Add(expiration).Unix()),    })    if !t.handleReply(fromID, pingPacket, req) {        // Note: we‘re ignoring the provided IP address right now        go t.bond(true, fromID, from, req.From.TCP)    }    return nil}

Receive a Findnode request sent by someone else. This request wants to send a K-node that is close to the target. For details of the algorithm, refer to the PDF document under the references directory.

Func (req *findnode) handle (t *udp, from *net. UDPADDR, Fromid NodeID, Mac []byte) error {if expired (req. Expiration) {return errexpired} if T.db.node (fromid) = = Nil {//No bond exists, we don ' t process th E packet.  This prevents//attack vector where the discovery protocol could is used//to amplify traffic in a DDOS Attack. A malicious actor//would send a Findnode request with the IP address and UDP//port of the target as the Source address.  The recipient of//the Findnode packet would then send a neighbors packet//(which is a much bigger packet        than Findnode) to the victim. Return Errunknownnode} target: = Crypto. Keccak256hash (req. target[:]) T.mutex.lock ()//Get bucketsize nodes with target distances close to each other. This method is implemented inside Table.go. The following will detail closest: = T.closest (target, bucketsize). Entries T.mutex.unlock () P: = Neighbors{expiration:uint64 (time. Now (). ADD (Expiration). Unix ())}//Send neighbors in chunks WITH at most maxneighbors per packet//to stay below the 1280x720 byte limit. For I, N: = range Closest {if netutil. Checkrelayip (from. IP, N.IP)! = nil {continue} p.nodes = Append (P.nodes, NODETORPC (n)) if Len (p.nodes) = = m axneighbors | | i = = Len (closest)-1 {t.send (from, Neighborspacket, &p) p.nodes = p.nodes[:0]}} r Eturn Nil}
UDP information Encryption and security issues

Discover protocol because it does not host any sensitive data, so the data is transmitted in clear text, but in order to ensure the integrity of the data and not to be tampered with, so the packet header with a digital signature.

Func encodepacket (Priv *ecdsa. Privatekey, PType byte, req interface{}) ([]byte, error) {b: = new (bytes. Buffer) B.write (headspace) b.writebyte (ptype) If err: = RLP. Encode (b, req); Err! = Nil {log. Error ("Can ' t encode discv4 packet", "err", err) return nil, err} packet: = B.bytes () sig, err: = Crypto.s IGN (Crypto. KECCAK256 (packet[headsize:]), PRIV) if err! = Nil {log. Error ("Can ' t sign discv4 packet", "err", err) return nil, err} copy (packet[macsize:], SIG)//Add the Have H to the front. Note:this doesn ' t protect the//packet in any.    Our public key would be part of this hash in//the future. Copy (packet, crypto. KECCAK256 (packet[macsize:])) return packet, Nil}func decodepacket (buf []byte) (Packet, NodeID, []byte, error) {if l  En (BUF) < headsize+1 {return nil, nodeid{}, nil, errpackettoosmall} hash, sig, sigdata: = Buf[:macsize], Buf[macsize:headsize], Buf[headsize:] Shouldhash: = Crypto. KECCAK256 (buf[macsize:]) if!bytes. Equal (hash, shouldhash) {return nil, nodeid{}, nil, errbadhash} fromid, err: = Recovernodeid (crypto.  KECCAK256 (buf[headsize:]), SIG) if err! = Nil {return nil, nodeid{}, hash, err} var req packet switch PType: = Sigdata[0];        ptype {Case pingpacket:req = new (ping) Case pongpacket:req = new (Pong) case Findnodepacket: req = new (findnode) Case neighborspacket:req = new (neighbors) Default:return nil, fromid, hash, F Mt. Errorf ("Unknown type:%d", PType)} s: = RLP. Newstream (bytes. Newreader (sigdata[1:]), 0) Err = S.decode (req) return req, Fromid, hash, err}

Brother chain Getting Started tutorial ETH source code Analysis P2p-udp.go Source Analysis (ii)

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.