github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/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 23 "fmt" 24 25 "github.com/FusionFoundation/efsn/metrics" 26 "github.com/FusionFoundation/efsn/p2p/discover" 27 "github.com/FusionFoundation/efsn/swarm/log" 28 "github.com/FusionFoundation/efsn/swarm/network" 29 "github.com/FusionFoundation/efsn/swarm/spancontext" 30 "github.com/FusionFoundation/efsn/swarm/storage" 31 opentracing "github.com/opentracing/opentracing-go" 32 ) 33 34 const ( 35 swarmChunkServerStreamName = "RETRIEVE_REQUEST" 36 deliveryCap = 32 37 ) 38 39 var ( 40 processReceivedChunksCount = metrics.NewRegisteredCounter("network.stream.received_chunks.count", nil) 41 handleRetrieveRequestMsgCount = metrics.NewRegisteredCounter("network.stream.handle_retrieve_request_msg.count", nil) 42 43 requestFromPeersCount = metrics.NewRegisteredCounter("network.stream.request_from_peers.count", nil) 44 requestFromPeersEachCount = metrics.NewRegisteredCounter("network.stream.request_from_peers_each.count", nil) 45 ) 46 47 type Delivery struct { 48 chunkStore storage.SyncChunkStore 49 kad *network.Kademlia 50 getPeer func(discover.NodeID) *Peer 51 } 52 53 func NewDelivery(kad *network.Kademlia, chunkStore storage.SyncChunkStore) *Delivery { 54 return &Delivery{ 55 chunkStore: chunkStore, 56 kad: kad, 57 } 58 } 59 60 // SwarmChunkServer implements Server 61 type SwarmChunkServer struct { 62 deliveryC chan []byte 63 batchC chan []byte 64 chunkStore storage.ChunkStore 65 currentLen uint64 66 quit chan struct{} 67 } 68 69 // NewSwarmChunkServer is SwarmChunkServer constructor 70 func NewSwarmChunkServer(chunkStore storage.ChunkStore) *SwarmChunkServer { 71 s := &SwarmChunkServer{ 72 deliveryC: make(chan []byte, deliveryCap), 73 batchC: make(chan []byte), 74 chunkStore: chunkStore, 75 quit: make(chan struct{}), 76 } 77 go s.processDeliveries() 78 return s 79 } 80 81 // processDeliveries handles delivered chunk hashes 82 func (s *SwarmChunkServer) processDeliveries() { 83 var hashes []byte 84 var batchC chan []byte 85 for { 86 select { 87 case <-s.quit: 88 return 89 case hash := <-s.deliveryC: 90 hashes = append(hashes, hash...) 91 batchC = s.batchC 92 case batchC <- hashes: 93 hashes = nil 94 batchC = nil 95 } 96 } 97 } 98 99 // SetNextBatch 100 func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) { 101 select { 102 case hashes = <-s.batchC: 103 case <-s.quit: 104 return 105 } 106 107 from = s.currentLen 108 s.currentLen += uint64(len(hashes)) 109 to = s.currentLen 110 return 111 } 112 113 // Close needs to be called on a stream server 114 func (s *SwarmChunkServer) Close() { 115 close(s.quit) 116 } 117 118 // GetData retrives chunk data from db store 119 func (s *SwarmChunkServer) GetData(ctx context.Context, key []byte) ([]byte, error) { 120 chunk, err := s.chunkStore.Get(ctx, storage.Address(key)) 121 if err != nil { 122 return nil, err 123 } 124 return chunk.Data(), nil 125 } 126 127 // RetrieveRequestMsg is the protocol msg for chunk retrieve requests 128 type RetrieveRequestMsg struct { 129 Addr storage.Address 130 SkipCheck bool 131 } 132 133 func (d *Delivery) handleRetrieveRequestMsg(ctx context.Context, sp *Peer, req *RetrieveRequestMsg) error { 134 log.Trace("received request", "peer", sp.ID(), "hash", req.Addr) 135 handleRetrieveRequestMsgCount.Inc(1) 136 137 var osp opentracing.Span 138 ctx, osp = spancontext.StartSpan( 139 ctx, 140 "retrieve.request") 141 defer osp.Finish() 142 143 s, err := sp.getServer(NewStream(swarmChunkServerStreamName, "", false)) 144 if err != nil { 145 return err 146 } 147 streamer := s.Server.(*SwarmChunkServer) 148 149 var cancel func() 150 // TODO: do something with this hardcoded timeout, maybe use TTL in the future 151 ctx, cancel = context.WithTimeout(context.WithValue(ctx, "peer", sp.ID().String()), network.RequestTimeout) 152 153 go func() { 154 select { 155 case <-ctx.Done(): 156 case <-streamer.quit: 157 } 158 cancel() 159 }() 160 161 go func() { 162 chunk, err := d.chunkStore.Get(ctx, req.Addr) 163 if err != nil { 164 log.Warn("ChunkStore.Get can not retrieve chunk", "err", err) 165 return 166 } 167 if req.SkipCheck { 168 err = sp.Deliver(ctx, chunk, s.priority) 169 if err != nil { 170 log.Warn("ERROR in handleRetrieveRequestMsg", "err", err) 171 } 172 return 173 } 174 select { 175 case streamer.deliveryC <- chunk.Address()[:]: 176 case <-streamer.quit: 177 } 178 179 }() 180 181 return nil 182 } 183 184 type ChunkDeliveryMsg struct { 185 Addr storage.Address 186 SData []byte // the stored chunk Data (incl size) 187 peer *Peer // set in handleChunkDeliveryMsg 188 } 189 190 // TODO: Fix context SNAFU 191 func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *ChunkDeliveryMsg) error { 192 var osp opentracing.Span 193 ctx, osp = spancontext.StartSpan( 194 ctx, 195 "chunk.delivery") 196 defer osp.Finish() 197 198 processReceivedChunksCount.Inc(1) 199 200 go func() { 201 req.peer = sp 202 err := d.chunkStore.Put(ctx, storage.NewChunk(req.Addr, req.SData)) 203 if err != nil { 204 if err == storage.ErrChunkInvalid { 205 // we removed this log because it spams the logs 206 // TODO: Enable this log line 207 // log.Warn("invalid chunk delivered", "peer", sp.ID(), "chunk", req.Addr, ) 208 req.peer.Drop(err) 209 } 210 } 211 }() 212 return nil 213 } 214 215 // RequestFromPeers sends a chunk retrieve request to 216 func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*discover.NodeID, chan struct{}, error) { 217 requestFromPeersCount.Inc(1) 218 var sp *Peer 219 spID := req.Source 220 221 if spID != nil { 222 sp = d.getPeer(*spID) 223 if sp == nil { 224 return nil, nil, fmt.Errorf("source peer %v not found", spID.String()) 225 } 226 } else { 227 d.kad.EachConn(req.Addr[:], 255, func(p *network.Peer, po int, nn bool) bool { 228 id := p.ID() 229 // TODO: skip light nodes that do not accept retrieve requests 230 if req.SkipPeer(id.String()) { 231 log.Trace("Delivery.RequestFromPeers: skip peer", "peer id", id) 232 return true 233 } 234 sp = d.getPeer(id) 235 if sp == nil { 236 log.Warn("Delivery.RequestFromPeers: peer not found", "id", id) 237 return true 238 } 239 spID = &id 240 return false 241 }) 242 if sp == nil { 243 return nil, nil, errors.New("no peer found") 244 } 245 } 246 247 err := sp.SendPriority(ctx, &RetrieveRequestMsg{ 248 Addr: req.Addr, 249 SkipCheck: req.SkipCheck, 250 }, Top) 251 if err != nil { 252 return nil, nil, err 253 } 254 requestFromPeersEachCount.Inc(1) 255 256 return spID, sp.quit, nil 257 }