github.com/aaa256/atlantis@v0.0.0-20210707112435-42ee889287a2/swarm/network/stream/delivery.go (about) 1 // Copyright 2018 The go-athereum Authors 2 // This file is part of the go-athereum library. 3 // 4 // The go-athereum 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-athereum 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-athereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package stream 18 19 import ( 20 "errors" 21 "time" 22 23 "github.com/athereum/go-athereum/common" 24 "github.com/athereum/go-athereum/metrics" 25 "github.com/athereum/go-athereum/p2p/discover" 26 "github.com/athereum/go-athereum/swarm/log" 27 "github.com/athereum/go-athereum/swarm/network" 28 "github.com/athereum/go-athereum/swarm/storage" 29 ) 30 31 const ( 32 swarmChunkServerStreamName = "RETRIEVE_REQUEST" 33 deliveryCap = 32 34 ) 35 36 var ( 37 processReceivedChunksCount = metrics.NewRegisteredCounter("network.stream.received_chunks.count", nil) 38 handleRetrieveRequestMsgCount = metrics.NewRegisteredCounter("network.stream.handle_retrieve_request_msg.count", nil) 39 40 requestFromPeersCount = metrics.NewRegisteredCounter("network.stream.request_from_peers.count", nil) 41 requestFromPeersEachCount = metrics.NewRegisteredCounter("network.stream.request_from_peers_each.count", nil) 42 ) 43 44 type Delivery struct { 45 db *storage.DBAPI 46 overlay network.Overlay 47 receiveC chan *ChunkDeliveryMsg 48 getPeer func(discover.NodeID) *Peer 49 } 50 51 func NewDelivery(overlay network.Overlay, db *storage.DBAPI) *Delivery { 52 d := &Delivery{ 53 db: db, 54 overlay: overlay, 55 receiveC: make(chan *ChunkDeliveryMsg, deliveryCap), 56 } 57 58 go d.processReceivedChunks() 59 return d 60 } 61 62 // SwarmChunkServer implements Server 63 type SwarmChunkServer struct { 64 deliveryC chan []byte 65 batchC chan []byte 66 db *storage.DBAPI 67 currentLen uint64 68 quit chan struct{} 69 } 70 71 // NewSwarmChunkServer is SwarmChunkServer constructor 72 func NewSwarmChunkServer(db *storage.DBAPI) *SwarmChunkServer { 73 s := &SwarmChunkServer{ 74 deliveryC: make(chan []byte, deliveryCap), 75 batchC: make(chan []byte), 76 db: db, 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 // SetNextBatch 102 func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) { 103 select { 104 case hashes = <-s.batchC: 105 case <-s.quit: 106 return 107 } 108 109 from = s.currentLen 110 s.currentLen += uint64(len(hashes)) 111 to = s.currentLen 112 return 113 } 114 115 // Close needs to be called on a stream server 116 func (s *SwarmChunkServer) Close() { 117 close(s.quit) 118 } 119 120 // GetData retrives chunk data from db store 121 func (s *SwarmChunkServer) GetData(key []byte) ([]byte, error) { 122 chunk, err := s.db.Get(storage.Address(key)) 123 if err == storage.ErrFetching { 124 <-chunk.ReqC 125 } else if err != nil { 126 return nil, err 127 } 128 return chunk.SData, nil 129 } 130 131 // RetrieveRequestMsg is the protocol msg for chunk retrieve requests 132 type RetrieveRequestMsg struct { 133 Addr storage.Address 134 SkipCheck bool 135 } 136 137 func (d *Delivery) handleRetrieveRequestMsg(sp *Peer, req *RetrieveRequestMsg) error { 138 log.Trace("received request", "peer", sp.ID(), "hash", req.Addr) 139 handleRetrieveRequestMsgCount.Inc(1) 140 141 s, err := sp.getServer(NewStream(swarmChunkServerStreamName, "", false)) 142 if err != nil { 143 return err 144 } 145 streamer := s.Server.(*SwarmChunkServer) 146 chunk, created := d.db.GetOrCreateRequest(req.Addr) 147 if chunk.ReqC != nil { 148 if created { 149 if err := d.RequestFromPeers(chunk.Addr[:], true, sp.ID()); err != nil { 150 log.Warn("unable to forward chunk request", "peer", sp.ID(), "key", chunk.Addr, "err", err) 151 chunk.SetErrored(storage.ErrChunkForward) 152 return nil 153 } 154 } 155 go func() { 156 t := time.NewTimer(10 * time.Minute) 157 defer t.Stop() 158 159 log.Debug("waiting delivery", "peer", sp.ID(), "hash", req.Addr, "node", common.Bytes2Hex(d.overlay.BaseAddr()), "created", created) 160 start := time.Now() 161 select { 162 case <-chunk.ReqC: 163 log.Debug("retrieve request ReqC closed", "peer", sp.ID(), "hash", req.Addr, "time", time.Since(start)) 164 case <-t.C: 165 log.Debug("retrieve request timeout", "peer", sp.ID(), "hash", req.Addr) 166 chunk.SetErrored(storage.ErrChunkTimeout) 167 return 168 } 169 chunk.SetErrored(nil) 170 171 if req.SkipCheck { 172 err := sp.Deliver(chunk, s.priority) 173 if err != nil { 174 log.Warn("ERROR in handleRetrieveRequestMsg, DROPPING peer!", "err", err) 175 sp.Drop(err) 176 } 177 } 178 streamer.deliveryC <- chunk.Addr[:] 179 }() 180 return nil 181 } 182 // TODO: call the retrieve function of the outgoing syncer 183 if req.SkipCheck { 184 log.Trace("deliver", "peer", sp.ID(), "hash", chunk.Addr) 185 if length := len(chunk.SData); length < 9 { 186 log.Error("Chunk.SData to deliver is too short", "len(chunk.SData)", length, "address", chunk.Addr) 187 } 188 return sp.Deliver(chunk, s.priority) 189 } 190 streamer.deliveryC <- chunk.Addr[:] 191 return nil 192 } 193 194 type ChunkDeliveryMsg struct { 195 Addr storage.Address 196 SData []byte // the stored chunk Data (incl size) 197 peer *Peer // set in handleChunkDeliveryMsg 198 } 199 200 func (d *Delivery) handleChunkDeliveryMsg(sp *Peer, req *ChunkDeliveryMsg) error { 201 req.peer = sp 202 d.receiveC <- req 203 return nil 204 } 205 206 func (d *Delivery) processReceivedChunks() { 207 R: 208 for req := range d.receiveC { 209 processReceivedChunksCount.Inc(1) 210 211 // this should be has locally 212 chunk, err := d.db.Get(req.Addr) 213 if err == nil { 214 continue R 215 } 216 if err != storage.ErrFetching { 217 log.Error("processReceivedChunks db error", "addr", req.Addr, "err", err, "chunk", chunk) 218 continue R 219 } 220 select { 221 case <-chunk.ReqC: 222 log.Error("someone else delivered?", "hash", chunk.Addr.Hex()) 223 continue R 224 default: 225 } 226 chunk.SData = req.SData 227 d.db.Put(chunk) 228 229 go func(req *ChunkDeliveryMsg) { 230 err := chunk.WaitToStore() 231 if err == storage.ErrChunkInvalid { 232 req.peer.Drop(err) 233 } 234 }(req) 235 } 236 } 237 238 // RequestFromPeers sends a chunk retrieve request to 239 func (d *Delivery) RequestFromPeers(hash []byte, skipCheck bool, peersToSkip ...discover.NodeID) error { 240 var success bool 241 var err error 242 requestFromPeersCount.Inc(1) 243 d.overlay.EachConn(hash, 255, func(p network.OverlayConn, po int, nn bool) bool { 244 spId := p.(network.Peer).ID() 245 for _, p := range peersToSkip { 246 if p == spId { 247 log.Trace("Delivery.RequestFromPeers: skip peer", "peer", spId) 248 return true 249 } 250 } 251 sp := d.getPeer(spId) 252 if sp == nil { 253 log.Warn("Delivery.RequestFromPeers: peer not found", "id", spId) 254 return true 255 } 256 // TODO: skip light nodes that do not accept retrieve requests 257 err = sp.SendPriority(&RetrieveRequestMsg{ 258 Addr: hash, 259 SkipCheck: skipCheck, 260 }, Top) 261 if err != nil { 262 return true 263 } 264 requestFromPeersEachCount.Inc(1) 265 success = true 266 return false 267 }) 268 if success { 269 return nil 270 } 271 return errors.New("no peer found") 272 }