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