github.com/phillinzzz/newBsc@v1.1.6/eth/protocols/diff/handler.go (about) 1 package diff 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/phillinzzz/newBsc/core" 8 "github.com/phillinzzz/newBsc/metrics" 9 "github.com/phillinzzz/newBsc/p2p" 10 "github.com/phillinzzz/newBsc/p2p/enode" 11 "github.com/phillinzzz/newBsc/p2p/enr" 12 "github.com/phillinzzz/newBsc/rlp" 13 ) 14 15 const ( 16 // softResponseLimit is the target maximum size of replies to data retrievals. 17 softResponseLimit = 2 * 1024 * 1024 18 19 // maxDiffLayerServe is the maximum number of diff layers to serve. 20 maxDiffLayerServe = 1024 21 ) 22 23 var requestTracker = NewTracker(time.Minute) 24 25 // Handler is a callback to invoke from an outside runner after the boilerplate 26 // exchanges have passed. 27 type Handler func(peer *Peer) error 28 29 type Backend interface { 30 // Chain retrieves the blockchain object to serve data. 31 Chain() *core.BlockChain 32 33 // RunPeer is invoked when a peer joins on the `eth` protocol. The handler 34 // should do any peer maintenance work, handshakes and validations. If all 35 // is passed, control should be given back to the `handler` to process the 36 // inbound messages going forward. 37 RunPeer(peer *Peer, handler Handler) error 38 39 PeerInfo(id enode.ID) interface{} 40 41 Handle(peer *Peer, packet Packet) error 42 } 43 44 // MakeProtocols constructs the P2P protocol definitions for `diff`. 45 func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol { 46 // Filter the discovery iterator for nodes advertising diff support. 47 dnsdisc = enode.Filter(dnsdisc, func(n *enode.Node) bool { 48 var diff enrEntry 49 return n.Load(&diff) == nil 50 }) 51 52 protocols := make([]p2p.Protocol, len(ProtocolVersions)) 53 for i, version := range ProtocolVersions { 54 version := version // Closure 55 56 protocols[i] = p2p.Protocol{ 57 Name: ProtocolName, 58 Version: version, 59 Length: protocolLengths[version], 60 Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 61 return backend.RunPeer(NewPeer(version, p, rw), func(peer *Peer) error { 62 defer peer.Close() 63 return Handle(backend, peer) 64 }) 65 }, 66 NodeInfo: func() interface{} { 67 return nodeInfo(backend.Chain()) 68 }, 69 PeerInfo: func(id enode.ID) interface{} { 70 return backend.PeerInfo(id) 71 }, 72 Attributes: []enr.Entry{&enrEntry{}}, 73 DialCandidates: dnsdisc, 74 } 75 } 76 return protocols 77 } 78 79 // Handle is the callback invoked to manage the life cycle of a `diff` peer. 80 // When this function terminates, the peer is disconnected. 81 func Handle(backend Backend, peer *Peer) error { 82 for { 83 if err := handleMessage(backend, peer); err != nil { 84 peer.Log().Debug("Message handling failed in `diff`", "err", err) 85 return err 86 } 87 } 88 } 89 90 // handleMessage is invoked whenever an inbound message is received from a 91 // remote peer on the `diff` protocol. The remote connection is torn down upon 92 // returning any error. 93 func handleMessage(backend Backend, peer *Peer) error { 94 // Read the next message from the remote peer, and ensure it's fully consumed 95 msg, err := peer.rw.ReadMsg() 96 if err != nil { 97 return err 98 } 99 if msg.Size > maxMessageSize { 100 return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) 101 } 102 defer msg.Discard() 103 start := time.Now() 104 // Track the emount of time it takes to serve the request and run the handler 105 if metrics.Enabled { 106 h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) 107 defer func(start time.Time) { 108 sampler := func() metrics.Sample { 109 return metrics.ResettingSample( 110 metrics.NewExpDecaySample(1028, 0.015), 111 ) 112 } 113 metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(time.Since(start).Microseconds()) 114 }(start) 115 } 116 // Handle the message depending on its contents 117 switch { 118 case msg.Code == GetDiffLayerMsg: 119 res := new(GetDiffLayersPacket) 120 if err := msg.Decode(res); err != nil { 121 return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) 122 } 123 diffs := answerDiffLayersQuery(backend, res) 124 125 p2p.Send(peer.rw, FullDiffLayerMsg, &FullDiffLayersPacket{ 126 RequestId: res.RequestId, 127 DiffLayersPacket: diffs, 128 }) 129 return nil 130 131 case msg.Code == DiffLayerMsg: 132 // A batch of trie nodes arrived to one of our previous requests 133 res := new(DiffLayersPacket) 134 if err := msg.Decode(res); err != nil { 135 return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) 136 } 137 return backend.Handle(peer, res) 138 case msg.Code == FullDiffLayerMsg: 139 // A batch of trie nodes arrived to one of our previous requests 140 res := new(FullDiffLayersPacket) 141 if err := msg.Decode(res); err != nil { 142 return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) 143 } 144 if fulfilled := requestTracker.Fulfil(peer.id, peer.version, FullDiffLayerMsg, res.RequestId); fulfilled { 145 return backend.Handle(peer, res) 146 } 147 return fmt.Errorf("%w: %v", errUnexpectedMsg, msg.Code) 148 default: 149 return fmt.Errorf("%w: %v", errInvalidMsgCode, msg.Code) 150 } 151 } 152 153 func answerDiffLayersQuery(backend Backend, query *GetDiffLayersPacket) []rlp.RawValue { 154 // Gather blocks until the fetch or network limits is reached 155 var ( 156 bytes int 157 diffLayers []rlp.RawValue 158 ) 159 // Need avoid transfer huge package 160 for lookups, hash := range query.BlockHashes { 161 if bytes >= softResponseLimit || len(diffLayers) >= maxDiffLayerServe || 162 lookups >= 2*maxDiffLayerServe { 163 break 164 } 165 if data := backend.Chain().GetDiffLayerRLP(hash); len(data) != 0 { 166 diffLayers = append(diffLayers, data) 167 bytes += len(data) 168 } 169 } 170 return diffLayers 171 } 172 173 // NodeInfo represents a short summary of the `diff` sub-protocol metadata 174 // known about the host peer. 175 type NodeInfo struct{} 176 177 // nodeInfo retrieves some `diff` protocol metadata about the running host node. 178 func nodeInfo(_ *core.BlockChain) *NodeInfo { 179 return &NodeInfo{} 180 }