github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/network/forwarding.go (about) 1 // Copyright 2016 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package network 18 19 import ( 20 "fmt" 21 "math/rand" 22 "time" 23 24 "github.com/SmartMeshFoundation/Spectrum/log" 25 "github.com/SmartMeshFoundation/Spectrum/swarm/storage" 26 ) 27 28 const requesterCount = 3 29 30 /* 31 forwarder implements the CloudStore interface (use by storage.NetStore) 32 and serves as the cloud store backend orchestrating storage/retrieval/delivery 33 via the native bzz protocol 34 which uses an MSB logarithmic distance-based semi-permanent Kademlia table for 35 * recursive forwarding style routing for retrieval 36 * smart syncronisation 37 */ 38 39 type forwarder struct { 40 hive *Hive 41 } 42 43 func NewForwarder(hive *Hive) *forwarder { 44 return &forwarder{hive: hive} 45 } 46 47 // generate a unique id uint64 48 func generateId() uint64 { 49 r := rand.New(rand.NewSource(time.Now().UnixNano())) 50 return uint64(r.Int63()) 51 } 52 53 var searchTimeout = 3 * time.Second 54 55 // forwarding logic 56 // logic propagating retrieve requests to peers given by the kademlia hive 57 func (self *forwarder) Retrieve(chunk *storage.Chunk) { 58 peers := self.hive.getPeers(chunk.Key, 0) 59 log.Trace(fmt.Sprintf("forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers))) 60 OUT: 61 for _, p := range peers { 62 log.Trace(fmt.Sprintf("forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p)) 63 for _, recipients := range chunk.Req.Requesters { 64 for _, recipient := range recipients { 65 req := recipient.(*retrieveRequestMsgData) 66 if req.from.Addr() == p.Addr() { 67 continue OUT 68 } 69 } 70 } 71 req := &retrieveRequestMsgData{ 72 Key: chunk.Key, 73 Id: generateId(), 74 } 75 var err error 76 if p.swap != nil { 77 err = p.swap.Add(-1) 78 } 79 if err == nil { 80 p.retrieve(req) 81 break OUT 82 } 83 log.Warn(fmt.Sprintf("forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err)) 84 } 85 } 86 87 // requests to specific peers given by the kademlia hive 88 // except for peers that the store request came from (if any) 89 // delivery queueing taken care of by syncer 90 func (self *forwarder) Store(chunk *storage.Chunk) { 91 var n int 92 msg := &storeRequestMsgData{ 93 Key: chunk.Key, 94 SData: chunk.SData, 95 } 96 var source *peer 97 if chunk.Source != nil { 98 source = chunk.Source.(*peer) 99 } 100 for _, p := range self.hive.getPeers(chunk.Key, 0) { 101 log.Trace(fmt.Sprintf("forwarder.Store: %v %v", p, chunk)) 102 103 if p.syncer != nil && (source == nil || p.Addr() != source.Addr()) { 104 n++ 105 Deliver(p, msg, PropagateReq) 106 } 107 } 108 log.Trace(fmt.Sprintf("forwarder.Store: sent to %v peers (chunk = %v)", n, chunk)) 109 } 110 111 // once a chunk is found deliver it to its requesters unless timed out 112 func (self *forwarder) Deliver(chunk *storage.Chunk) { 113 // iterate over request entries 114 for id, requesters := range chunk.Req.Requesters { 115 counter := requesterCount 116 msg := &storeRequestMsgData{ 117 Key: chunk.Key, 118 SData: chunk.SData, 119 } 120 var n int 121 var req *retrieveRequestMsgData 122 // iterate over requesters with the same id 123 for id, r := range requesters { 124 req = r.(*retrieveRequestMsgData) 125 if req.timeout == nil || req.timeout.After(time.Now()) { 126 log.Trace(fmt.Sprintf("forwarder.Deliver: %v -> %v", req.Id, req.from)) 127 msg.Id = uint64(id) 128 Deliver(req.from, msg, DeliverReq) 129 n++ 130 counter-- 131 if counter <= 0 { 132 break 133 } 134 } 135 } 136 log.Trace(fmt.Sprintf("forwarder.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n)) 137 } 138 } 139 140 // initiate delivery of a chunk to a particular peer via syncer#addRequest 141 // depending on syncer mode and priority settings and sync request type 142 // this either goes via confirmation roundtrip or queued or pushed directly 143 func Deliver(p *peer, req interface{}, ty int) { 144 p.syncer.addRequest(req, ty) 145 } 146 147 // push chunk over to peer 148 func Push(p *peer, key storage.Key, priority uint) { 149 p.syncer.doDelivery(key, priority, p.syncer.quit) 150 }