github.com/divan/go-ethereum@v1.8.14-0.20180820134928-1de9ada4016d/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 "time" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/metrics" 26 "github.com/ethereum/go-ethereum/p2p/discover" 27 cp "github.com/ethereum/go-ethereum/swarm/chunk" 28 "github.com/ethereum/go-ethereum/swarm/log" 29 "github.com/ethereum/go-ethereum/swarm/network" 30 "github.com/ethereum/go-ethereum/swarm/spancontext" 31 "github.com/ethereum/go-ethereum/swarm/storage" 32 opentracing "github.com/opentracing/opentracing-go" 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 44 requestFromPeersCount = metrics.NewRegisteredCounter("network.stream.request_from_peers.count", nil) 45 requestFromPeersEachCount = metrics.NewRegisteredCounter("network.stream.request_from_peers_each.count", nil) 46 ) 47 48 type Delivery struct { 49 db *storage.DBAPI 50 overlay network.Overlay 51 receiveC chan *ChunkDeliveryMsg 52 getPeer func(discover.NodeID) *Peer 53 } 54 55 func NewDelivery(overlay network.Overlay, db *storage.DBAPI) *Delivery { 56 d := &Delivery{ 57 db: db, 58 overlay: overlay, 59 receiveC: make(chan *ChunkDeliveryMsg, deliveryCap), 60 } 61 62 go d.processReceivedChunks() 63 return d 64 } 65 66 // SwarmChunkServer implements Server 67 type SwarmChunkServer struct { 68 deliveryC chan []byte 69 batchC chan []byte 70 db *storage.DBAPI 71 currentLen uint64 72 quit chan struct{} 73 } 74 75 // NewSwarmChunkServer is SwarmChunkServer constructor 76 func NewSwarmChunkServer(db *storage.DBAPI) *SwarmChunkServer { 77 s := &SwarmChunkServer{ 78 deliveryC: make(chan []byte, deliveryCap), 79 batchC: make(chan []byte), 80 db: db, 81 quit: make(chan struct{}), 82 } 83 go s.processDeliveries() 84 return s 85 } 86 87 // processDeliveries handles delivered chunk hashes 88 func (s *SwarmChunkServer) processDeliveries() { 89 var hashes []byte 90 var batchC chan []byte 91 for { 92 select { 93 case <-s.quit: 94 return 95 case hash := <-s.deliveryC: 96 hashes = append(hashes, hash...) 97 batchC = s.batchC 98 case batchC <- hashes: 99 hashes = nil 100 batchC = nil 101 } 102 } 103 } 104 105 // SetNextBatch 106 func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) { 107 select { 108 case hashes = <-s.batchC: 109 case <-s.quit: 110 return 111 } 112 113 from = s.currentLen 114 s.currentLen += uint64(len(hashes)) 115 to = s.currentLen 116 return 117 } 118 119 // Close needs to be called on a stream server 120 func (s *SwarmChunkServer) Close() { 121 close(s.quit) 122 } 123 124 // GetData retrives chunk data from db store 125 func (s *SwarmChunkServer) GetData(ctx context.Context, key []byte) ([]byte, error) { 126 chunk, err := s.db.Get(ctx, storage.Address(key)) 127 if err == storage.ErrFetching { 128 <-chunk.ReqC 129 } else if err != nil { 130 return nil, err 131 } 132 return chunk.SData, nil 133 } 134 135 // RetrieveRequestMsg is the protocol msg for chunk retrieve requests 136 type RetrieveRequestMsg struct { 137 Addr storage.Address 138 SkipCheck bool 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 "retrieve.request") 149 defer osp.Finish() 150 151 s, err := sp.getServer(NewStream(swarmChunkServerStreamName, "", false)) 152 if err != nil { 153 return err 154 } 155 streamer := s.Server.(*SwarmChunkServer) 156 chunk, created := d.db.GetOrCreateRequest(ctx, req.Addr) 157 if chunk.ReqC != nil { 158 if created { 159 if err := d.RequestFromPeers(ctx, chunk.Addr[:], true, sp.ID()); err != nil { 160 log.Warn("unable to forward chunk request", "peer", sp.ID(), "key", chunk.Addr, "err", err) 161 chunk.SetErrored(storage.ErrChunkForward) 162 return nil 163 } 164 } 165 go func() { 166 var osp opentracing.Span 167 ctx, osp = spancontext.StartSpan( 168 ctx, 169 "waiting.delivery") 170 defer osp.Finish() 171 172 t := time.NewTimer(10 * time.Minute) 173 defer t.Stop() 174 175 log.Debug("waiting delivery", "peer", sp.ID(), "hash", req.Addr, "node", common.Bytes2Hex(d.overlay.BaseAddr()), "created", created) 176 start := time.Now() 177 select { 178 case <-chunk.ReqC: 179 log.Debug("retrieve request ReqC closed", "peer", sp.ID(), "hash", req.Addr, "time", time.Since(start)) 180 case <-t.C: 181 log.Debug("retrieve request timeout", "peer", sp.ID(), "hash", req.Addr) 182 chunk.SetErrored(storage.ErrChunkTimeout) 183 return 184 } 185 chunk.SetErrored(nil) 186 187 if req.SkipCheck { 188 err := sp.Deliver(ctx, chunk, s.priority) 189 if err != nil { 190 log.Warn("ERROR in handleRetrieveRequestMsg, DROPPING peer!", "err", err) 191 sp.Drop(err) 192 } 193 } 194 streamer.deliveryC <- chunk.Addr[:] 195 }() 196 return nil 197 } 198 // TODO: call the retrieve function of the outgoing syncer 199 if req.SkipCheck { 200 log.Trace("deliver", "peer", sp.ID(), "hash", chunk.Addr) 201 if length := len(chunk.SData); length < 9 { 202 log.Error("Chunk.SData to deliver is too short", "len(chunk.SData)", length, "address", chunk.Addr) 203 } 204 return sp.Deliver(ctx, chunk, s.priority) 205 } 206 streamer.deliveryC <- chunk.Addr[:] 207 return nil 208 } 209 210 type ChunkDeliveryMsg struct { 211 Addr storage.Address 212 SData []byte // the stored chunk Data (incl size) 213 peer *Peer // set in handleChunkDeliveryMsg 214 } 215 216 func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *ChunkDeliveryMsg) error { 217 var osp opentracing.Span 218 ctx, osp = spancontext.StartSpan( 219 ctx, 220 "chunk.delivery") 221 defer osp.Finish() 222 223 req.peer = sp 224 d.receiveC <- req 225 return nil 226 } 227 228 func (d *Delivery) processReceivedChunks() { 229 R: 230 for req := range d.receiveC { 231 processReceivedChunksCount.Inc(1) 232 233 if len(req.SData) > cp.DefaultSize+8 { 234 log.Warn("received chunk is bigger than expected", "len", len(req.SData)) 235 continue R 236 } 237 238 // this should be has locally 239 chunk, err := d.db.Get(context.TODO(), req.Addr) 240 if err == nil { 241 continue R 242 } 243 if err != storage.ErrFetching { 244 log.Error("processReceivedChunks db error", "addr", req.Addr, "err", err, "chunk", chunk) 245 continue R 246 } 247 select { 248 case <-chunk.ReqC: 249 log.Error("someone else delivered?", "hash", chunk.Addr.Hex()) 250 continue R 251 default: 252 } 253 254 chunk.SData = req.SData 255 d.db.Put(context.TODO(), chunk) 256 257 go func(req *ChunkDeliveryMsg) { 258 err := chunk.WaitToStore() 259 if err == storage.ErrChunkInvalid { 260 req.peer.Drop(err) 261 } 262 }(req) 263 } 264 } 265 266 // RequestFromPeers sends a chunk retrieve request to 267 func (d *Delivery) RequestFromPeers(ctx context.Context, hash []byte, skipCheck bool, peersToSkip ...discover.NodeID) error { 268 var success bool 269 var err error 270 requestFromPeersCount.Inc(1) 271 272 d.overlay.EachConn(hash, 255, func(p network.OverlayConn, po int, nn bool) bool { 273 spId := p.(network.Peer).ID() 274 for _, p := range peersToSkip { 275 if p == spId { 276 log.Trace("Delivery.RequestFromPeers: skip peer", "peer", spId) 277 return true 278 } 279 } 280 sp := d.getPeer(spId) 281 if sp == nil { 282 log.Warn("Delivery.RequestFromPeers: peer not found", "id", spId) 283 return true 284 } 285 err = sp.SendPriority(ctx, &RetrieveRequestMsg{ 286 Addr: hash, 287 SkipCheck: skipCheck, 288 }, Top) 289 if err != nil { 290 return true 291 } 292 requestFromPeersEachCount.Inc(1) 293 success = true 294 return false 295 }) 296 if success { 297 return nil 298 } 299 return errors.New("no peer found") 300 }