github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/swarm/network/stream/delivery.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package stream 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 24 "github.com/ethereum/go-ethereum/metrics" 25 "github.com/ethereum/go-ethereum/p2p/enode" 26 "github.com/ethereum/go-ethereum/swarm/log" 27 "github.com/ethereum/go-ethereum/swarm/network" 28 "github.com/ethereum/go-ethereum/swarm/spancontext" 29 "github.com/ethereum/go-ethereum/swarm/storage" 30 "github.com/ethereum/go-ethereum/swarm/tracing" 31 opentracing "github.com/opentracing/opentracing-go" 32 olog "github.com/opentracing/opentracing-go/log" 33 ) 34 35 const ( 36 swarmChunkServerStreamName = "RETRIEVE_REQUEST" 37 deliveryCap = 32 38 ) 39 40 var ( 41 processReceivedChunksCount = metrics.NewRegisteredCounter("network.stream.received_chunks.count", nil) 42 handleRetrieveRequestMsgCount = metrics.NewRegisteredCounter("network.stream.handle_retrieve_request_msg.count", nil) 43 retrieveChunkFail = metrics.NewRegisteredCounter("network.stream.retrieve_chunks_fail.count", nil) 44 45 requestFromPeersCount = metrics.NewRegisteredCounter("network.stream.request_from_peers.count", nil) 46 requestFromPeersEachCount = metrics.NewRegisteredCounter("network.stream.request_from_peers_each.count", nil) 47 ) 48 49 type Delivery struct { 50 chunkStore storage.SyncChunkStore 51 kad *network.Kademlia 52 getPeer func(enode.ID) *Peer 53 } 54 55 func NewDelivery(kad *network.Kademlia, chunkStore storage.SyncChunkStore) *Delivery { 56 return &Delivery{ 57 chunkStore: chunkStore, 58 kad: kad, 59 } 60 } 61 62 // SwarmChunkServer implements Server 63 type SwarmChunkServer struct { 64 deliveryC chan []byte 65 batchC chan []byte 66 chunkStore storage.ChunkStore 67 currentLen uint64 68 quit chan struct{} 69 } 70 71 // NewSwarmChunkServer is SwarmChunkServer constructor 72 func NewSwarmChunkServer(chunkStore storage.ChunkStore) *SwarmChunkServer { 73 s := &SwarmChunkServer{ 74 deliveryC: make(chan []byte, deliveryCap), 75 batchC: make(chan []byte), 76 chunkStore: chunkStore, 77 quit: make(chan struct{}), 78 } 79 go s.processDeliveries() 80 return s 81 } 82 83 // processDeliveries handles delivered chunk hashes 84 func (s *SwarmChunkServer) processDeliveries() { 85 var hashes []byte 86 var batchC chan []byte 87 for { 88 select { 89 case <-s.quit: 90 return 91 case hash := <-s.deliveryC: 92 hashes = append(hashes, hash...) 93 batchC = s.batchC 94 case batchC <- hashes: 95 hashes = nil 96 batchC = nil 97 } 98 } 99 } 100 101 // SessionIndex returns zero in all cases for SwarmChunkServer. 102 func (s *SwarmChunkServer) SessionIndex() (uint64, error) { 103 return 0, nil 104 } 105 106 // SetNextBatch 107 func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) { 108 select { 109 case hashes = <-s.batchC: 110 case <-s.quit: 111 return 112 } 113 114 from = s.currentLen 115 s.currentLen += uint64(len(hashes)) 116 to = s.currentLen 117 return 118 } 119 120 // Close needs to be called on a stream server 121 func (s *SwarmChunkServer) Close() { 122 close(s.quit) 123 } 124 125 // GetData retrives chunk data from db store 126 func (s *SwarmChunkServer) GetData(ctx context.Context, key []byte) ([]byte, error) { 127 chunk, err := s.chunkStore.Get(ctx, storage.Address(key)) 128 if err != nil { 129 return nil, err 130 } 131 return chunk.Data(), nil 132 } 133 134 // RetrieveRequestMsg is the protocol msg for chunk retrieve requests 135 type RetrieveRequestMsg struct { 136 Addr storage.Address 137 SkipCheck bool 138 HopCount uint8 139 } 140 141 func (d *Delivery) handleRetrieveRequestMsg(ctx context.Context, sp *Peer, req *RetrieveRequestMsg) error { 142 log.Trace("received request", "peer", sp.ID(), "hash", req.Addr) 143 handleRetrieveRequestMsgCount.Inc(1) 144 145 var osp opentracing.Span 146 ctx, osp = spancontext.StartSpan( 147 ctx, 148 "stream.handle.retrieve") 149 150 osp.LogFields(olog.String("ref", req.Addr.String())) 151 152 s, err := sp.getServer(NewStream(swarmChunkServerStreamName, "", true)) 153 if err != nil { 154 return err 155 } 156 streamer := s.Server.(*SwarmChunkServer) 157 158 var cancel func() 159 // TODO: do something with this hardcoded timeout, maybe use TTL in the future 160 ctx = context.WithValue(ctx, "peer", sp.ID().String()) 161 ctx = context.WithValue(ctx, "hopcount", req.HopCount) 162 ctx, cancel = context.WithTimeout(ctx, network.RequestTimeout) 163 164 go func() { 165 select { 166 case <-ctx.Done(): 167 case <-streamer.quit: 168 } 169 cancel() 170 }() 171 172 go func() { 173 defer osp.Finish() 174 chunk, err := d.chunkStore.Get(ctx, req.Addr) 175 if err != nil { 176 retrieveChunkFail.Inc(1) 177 log.Debug("ChunkStore.Get can not retrieve chunk", "peer", sp.ID().String(), "addr", req.Addr, "hopcount", req.HopCount, "err", err) 178 return 179 } 180 if req.SkipCheck { 181 syncing := false 182 osp.LogFields(olog.Bool("skipCheck", true)) 183 184 err = sp.Deliver(ctx, chunk, s.priority, syncing) 185 if err != nil { 186 log.Warn("ERROR in handleRetrieveRequestMsg", "err", err) 187 } 188 osp.LogFields(olog.Bool("delivered", true)) 189 return 190 } 191 osp.LogFields(olog.Bool("skipCheck", false)) 192 select { 193 case streamer.deliveryC <- chunk.Address()[:]: 194 case <-streamer.quit: 195 } 196 197 }() 198 199 return nil 200 } 201 202 //Chunk delivery always uses the same message type.... 203 type ChunkDeliveryMsg struct { 204 Addr storage.Address 205 SData []byte // the stored chunk Data (incl size) 206 peer *Peer // set in handleChunkDeliveryMsg 207 } 208 209 //...but swap accounting needs to disambiguate if it is a delivery for syncing or for retrieval 210 //as it decides based on message type if it needs to account for this message or not 211 212 //defines a chunk delivery for retrieval (with accounting) 213 type ChunkDeliveryMsgRetrieval ChunkDeliveryMsg 214 215 //defines a chunk delivery for syncing (without accounting) 216 type ChunkDeliveryMsgSyncing ChunkDeliveryMsg 217 218 // chunk delivery msg is response to retrieverequest msg 219 func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *ChunkDeliveryMsg) error { 220 var osp opentracing.Span 221 ctx, osp = spancontext.StartSpan( 222 ctx, 223 "handle.chunk.delivery") 224 225 processReceivedChunksCount.Inc(1) 226 227 // retrieve the span for the originating retrieverequest 228 spanId := fmt.Sprintf("stream.send.request.%v.%v", sp.ID(), req.Addr) 229 span := tracing.ShiftSpanByKey(spanId) 230 231 log.Trace("handle.chunk.delivery", "ref", req.Addr, "from peer", sp.ID()) 232 233 go func() { 234 defer osp.Finish() 235 236 if span != nil { 237 span.LogFields(olog.String("finish", "from handleChunkDeliveryMsg")) 238 defer span.Finish() 239 } 240 241 req.peer = sp 242 log.Trace("handle.chunk.delivery", "put", req.Addr) 243 err := d.chunkStore.Put(ctx, storage.NewChunk(req.Addr, req.SData)) 244 if err != nil { 245 if err == storage.ErrChunkInvalid { 246 // we removed this log because it spams the logs 247 // TODO: Enable this log line 248 // log.Warn("invalid chunk delivered", "peer", sp.ID(), "chunk", req.Addr, ) 249 req.peer.Drop(err) 250 } 251 } 252 log.Trace("handle.chunk.delivery", "done put", req.Addr, "err", err) 253 }() 254 return nil 255 } 256 257 // RequestFromPeers sends a chunk retrieve request to a peer 258 // The most eligible peer that hasn't already been sent to is chosen 259 // TODO: define "eligible" 260 func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*enode.ID, chan struct{}, error) { 261 requestFromPeersCount.Inc(1) 262 var sp *Peer 263 spID := req.Source 264 265 if spID != nil { 266 sp = d.getPeer(*spID) 267 if sp == nil { 268 return nil, nil, fmt.Errorf("source peer %v not found", spID.String()) 269 } 270 } else { 271 d.kad.EachConn(req.Addr[:], 255, func(p *network.Peer, po int) bool { 272 id := p.ID() 273 if p.LightNode { 274 // skip light nodes 275 return true 276 } 277 if req.SkipPeer(id.String()) { 278 log.Trace("Delivery.RequestFromPeers: skip peer", "peer id", id) 279 return true 280 } 281 sp = d.getPeer(id) 282 // sp is nil, when we encounter a peer that is not registered for delivery, i.e. doesn't support the `stream` protocol 283 if sp == nil { 284 return true 285 } 286 spID = &id 287 return false 288 }) 289 if sp == nil { 290 return nil, nil, errors.New("no peer found") 291 } 292 } 293 294 // setting this value in the context creates a new span that can persist across the sendpriority queue and the network roundtrip 295 // this span will finish only when delivery is handled (or times out) 296 ctx = context.WithValue(ctx, tracing.StoreLabelId, "stream.send.request") 297 ctx = context.WithValue(ctx, tracing.StoreLabelMeta, fmt.Sprintf("%v.%v", sp.ID(), req.Addr)) 298 log.Trace("request.from.peers", "peer", sp.ID(), "ref", req.Addr) 299 err := sp.SendPriority(ctx, &RetrieveRequestMsg{ 300 Addr: req.Addr, 301 SkipCheck: req.SkipCheck, 302 HopCount: req.HopCount, 303 }, Top) 304 if err != nil { 305 return nil, nil, err 306 } 307 requestFromPeersEachCount.Inc(1) 308 309 return spID, sp.quit, nil 310 }