github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/network/hive.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 "path/filepath" 23 "time" 24 25 "github.com/SmartMeshFoundation/Spectrum/common" 26 "github.com/SmartMeshFoundation/Spectrum/log" 27 "github.com/SmartMeshFoundation/Spectrum/p2p/discover" 28 "github.com/SmartMeshFoundation/Spectrum/p2p/netutil" 29 "github.com/SmartMeshFoundation/Spectrum/swarm/network/kademlia" 30 "github.com/SmartMeshFoundation/Spectrum/swarm/storage" 31 ) 32 33 // Hive is the logistic manager of the swarm 34 // it uses a generic kademlia nodetable to find best peer list 35 // for any target 36 // this is used by the netstore to search for content in the swarm 37 // the bzz protocol peersMsgData exchange is relayed to Kademlia 38 // for db storage and filtering 39 // connections and disconnections are reported and relayed 40 // to keep the nodetable uptodate 41 42 type Hive struct { 43 listenAddr func() string 44 callInterval uint64 45 id discover.NodeID 46 addr kademlia.Address 47 kad *kademlia.Kademlia 48 path string 49 quit chan bool 50 toggle chan bool 51 more chan bool 52 53 // for testing only 54 swapEnabled bool 55 syncEnabled bool 56 blockRead bool 57 blockWrite bool 58 } 59 60 const ( 61 callInterval = 3000000000 62 // bucketSize = 3 63 // maxProx = 8 64 // proxBinSize = 4 65 ) 66 67 type HiveParams struct { 68 CallInterval uint64 69 KadDbPath string 70 *kademlia.KadParams 71 } 72 73 //create default params 74 func NewDefaultHiveParams() *HiveParams { 75 kad := kademlia.NewDefaultKadParams() 76 // kad.BucketSize = bucketSize 77 // kad.MaxProx = maxProx 78 // kad.ProxBinSize = proxBinSize 79 80 return &HiveParams{ 81 CallInterval: callInterval, 82 KadParams: kad, 83 } 84 } 85 86 //this can only finally be set after all config options (file, cmd line, env vars) 87 //have been evaluated 88 func (self *HiveParams) Init(path string) { 89 self.KadDbPath = filepath.Join(path, "bzz-peers.json") 90 } 91 92 func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive { 93 kad := kademlia.New(kademlia.Address(addr), params.KadParams) 94 return &Hive{ 95 callInterval: params.CallInterval, 96 kad: kad, 97 addr: kad.Addr(), 98 path: params.KadDbPath, 99 swapEnabled: swapEnabled, 100 syncEnabled: syncEnabled, 101 } 102 } 103 104 func (self *Hive) SyncEnabled(on bool) { 105 self.syncEnabled = on 106 } 107 108 func (self *Hive) SwapEnabled(on bool) { 109 self.swapEnabled = on 110 } 111 112 func (self *Hive) BlockNetworkRead(on bool) { 113 self.blockRead = on 114 } 115 116 func (self *Hive) BlockNetworkWrite(on bool) { 117 self.blockWrite = on 118 } 119 120 // public accessor to the hive base address 121 func (self *Hive) Addr() kademlia.Address { 122 return self.addr 123 } 124 125 // Start receives network info only at startup 126 // listedAddr is a function to retrieve listening address to advertise to peers 127 // connectPeer is a function to connect to a peer based on its NodeID or enode URL 128 // there are called on the p2p.Server which runs on the node 129 func (self *Hive) Start(id discover.NodeID, listenAddr func() string, connectPeer func(string) error) (err error) { 130 self.toggle = make(chan bool) 131 self.more = make(chan bool) 132 self.quit = make(chan bool) 133 self.id = id 134 self.listenAddr = listenAddr 135 err = self.kad.Load(self.path, nil) 136 if err != nil { 137 log.Warn(fmt.Sprintf("Warning: error reading kaddb '%s' (skipping): %v", self.path, err)) 138 err = nil 139 } 140 // this loop is doing bootstrapping and maintains a healthy table 141 go self.keepAlive() 142 go func() { 143 // whenever toggled ask kademlia about most preferred peer 144 for alive := range self.more { 145 if !alive { 146 // receiving false closes the loop while allowing parallel routines 147 // to attempt to write to more (remove Peer when shutting down) 148 return 149 } 150 node, need, proxLimit := self.kad.Suggest() 151 152 if node != nil && len(node.Url) > 0 { 153 log.Trace(fmt.Sprintf("call known bee %v", node.Url)) 154 // enode or any lower level connection address is unnecessary in future 155 // discovery table is used to look it up. 156 connectPeer(node.Url) 157 } 158 if need { 159 // a random peer is taken from the table 160 peers := self.kad.FindClosest(kademlia.RandomAddressAt(self.addr, rand.Intn(self.kad.MaxProx)), 1) 161 if len(peers) > 0 { 162 // a random address at prox bin 0 is sent for lookup 163 randAddr := kademlia.RandomAddressAt(self.addr, proxLimit) 164 req := &retrieveRequestMsgData{ 165 Key: storage.Key(randAddr[:]), 166 } 167 log.Trace(fmt.Sprintf("call any bee near %v (PO%03d) - messenger bee: %v", randAddr, proxLimit, peers[0])) 168 peers[0].(*peer).retrieve(req) 169 } else { 170 log.Warn(fmt.Sprintf("no peer")) 171 } 172 log.Trace(fmt.Sprintf("buzz kept alive")) 173 } else { 174 log.Info(fmt.Sprintf("no need for more bees")) 175 } 176 select { 177 case self.toggle <- need: 178 case <-self.quit: 179 return 180 } 181 log.Debug(fmt.Sprintf("queen's address: %v, population: %d (%d)", self.addr, self.kad.Count(), self.kad.DBCount())) 182 } 183 }() 184 return 185 } 186 187 // keepAlive is a forever loop 188 // in its awake state it periodically triggers connection attempts 189 // by writing to self.more until Kademlia Table is saturated 190 // wake state is toggled by writing to self.toggle 191 // it restarts if the table becomes non-full again due to disconnections 192 func (self *Hive) keepAlive() { 193 alarm := time.NewTicker(time.Duration(self.callInterval)).C 194 for { 195 select { 196 case <-alarm: 197 if self.kad.DBCount() > 0 { 198 select { 199 case self.more <- true: 200 log.Debug(fmt.Sprintf("buzz wakeup")) 201 default: 202 } 203 } 204 case need := <-self.toggle: 205 if alarm == nil && need { 206 alarm = time.NewTicker(time.Duration(self.callInterval)).C 207 } 208 if alarm != nil && !need { 209 alarm = nil 210 211 } 212 case <-self.quit: 213 return 214 } 215 } 216 } 217 218 func (self *Hive) Stop() error { 219 // closing toggle channel quits the updateloop 220 close(self.quit) 221 return self.kad.Save(self.path, saveSync) 222 } 223 224 // called at the end of a successful protocol handshake 225 func (self *Hive) addPeer(p *peer) error { 226 defer func() { 227 select { 228 case self.more <- true: 229 default: 230 } 231 }() 232 log.Trace(fmt.Sprintf("hi new bee %v", p)) 233 err := self.kad.On(p, loadSync) 234 if err != nil { 235 return err 236 } 237 // self lookup (can be encoded as nil/zero key since peers addr known) + no id () 238 // the most common way of saying hi in bzz is initiation of gossip 239 // let me know about anyone new from my hood , here is the storageradius 240 // to send the 6 byte self lookup 241 // we do not record as request or forward it, just reply with peers 242 p.retrieve(&retrieveRequestMsgData{}) 243 log.Trace(fmt.Sprintf("'whatsup wheresdaparty' sent to %v", p)) 244 245 return nil 246 } 247 248 // called after peer disconnected 249 func (self *Hive) removePeer(p *peer) { 250 log.Debug(fmt.Sprintf("bee %v removed", p)) 251 self.kad.Off(p, saveSync) 252 select { 253 case self.more <- true: 254 default: 255 } 256 if self.kad.Count() == 0 { 257 log.Debug(fmt.Sprintf("empty, all bees gone")) 258 } 259 } 260 261 // Retrieve a list of live peers that are closer to target than us 262 func (self *Hive) getPeers(target storage.Key, max int) (peers []*peer) { 263 var addr kademlia.Address 264 copy(addr[:], target[:]) 265 for _, node := range self.kad.FindClosest(addr, max) { 266 peers = append(peers, node.(*peer)) 267 } 268 return 269 } 270 271 // disconnects all the peers 272 func (self *Hive) DropAll() { 273 log.Info(fmt.Sprintf("dropping all bees")) 274 for _, node := range self.kad.FindClosest(kademlia.Address{}, 0) { 275 node.Drop() 276 } 277 } 278 279 // contructor for kademlia.NodeRecord based on peer address alone 280 // TODO: should go away and only addr passed to kademlia 281 func newNodeRecord(addr *peerAddr) *kademlia.NodeRecord { 282 now := time.Now() 283 return &kademlia.NodeRecord{ 284 Addr: addr.Addr, 285 Url: addr.String(), 286 Seen: now, 287 After: now, 288 } 289 } 290 291 // called by the protocol when receiving peerset (for target address) 292 // peersMsgData is converted to a slice of NodeRecords for Kademlia 293 // this is to store all thats needed 294 func (self *Hive) HandlePeersMsg(req *peersMsgData, from *peer) { 295 var nrs []*kademlia.NodeRecord 296 for _, p := range req.Peers { 297 if err := netutil.CheckRelayIP(from.remoteAddr.IP, p.IP); err != nil { 298 log.Trace(fmt.Sprintf("invalid peer IP %v from %v: %v", from.remoteAddr.IP, p.IP, err)) 299 continue 300 } 301 nrs = append(nrs, newNodeRecord(p)) 302 } 303 self.kad.Add(nrs) 304 } 305 306 // peer wraps the protocol instance to represent a connected peer 307 // it implements kademlia.Node interface 308 type peer struct { 309 *bzz // protocol instance running on peer connection 310 } 311 312 // protocol instance implements kademlia.Node interface (embedded peer) 313 func (self *peer) Addr() kademlia.Address { 314 return self.remoteAddr.Addr 315 } 316 317 func (self *peer) Url() string { 318 return self.remoteAddr.String() 319 } 320 321 // TODO take into account traffic 322 func (self *peer) LastActive() time.Time { 323 return self.lastActive 324 } 325 326 // reads the serialised form of sync state persisted as the 'Meta' attribute 327 // and sets the decoded syncState on the online node 328 func loadSync(record *kademlia.NodeRecord, node kademlia.Node) error { 329 p, ok := node.(*peer) 330 if !ok { 331 return fmt.Errorf("invalid type") 332 } 333 if record.Meta == nil { 334 log.Debug(fmt.Sprintf("no sync state for node record %v setting default", record)) 335 p.syncState = &syncState{DbSyncState: &storage.DbSyncState{}} 336 return nil 337 } 338 state, err := decodeSync(record.Meta) 339 if err != nil { 340 return fmt.Errorf("error decoding kddb record meta info into a sync state: %v", err) 341 } 342 log.Trace(fmt.Sprintf("sync state for node record %v read from Meta: %s", record, string(*(record.Meta)))) 343 p.syncState = state 344 return err 345 } 346 347 // callback when saving a sync state 348 func saveSync(record *kademlia.NodeRecord, node kademlia.Node) { 349 if p, ok := node.(*peer); ok { 350 meta, err := encodeSync(p.syncState) 351 if err != nil { 352 log.Warn(fmt.Sprintf("error saving sync state for %v: %v", node, err)) 353 return 354 } 355 log.Trace(fmt.Sprintf("saved sync state for %v: %s", node, string(*meta))) 356 record.Meta = meta 357 } 358 } 359 360 // the immediate response to a retrieve request, 361 // sends relevant peer data given by the kademlia hive to the requester 362 // TODO: remember peers sent for duration of the session, only new peers sent 363 func (self *Hive) peers(req *retrieveRequestMsgData) { 364 if req != nil { 365 var addrs []*peerAddr 366 if req.timeout == nil || time.Now().Before(*(req.timeout)) { 367 key := req.Key 368 // self lookup from remote peer 369 if storage.IsZeroKey(key) { 370 addr := req.from.Addr() 371 key = storage.Key(addr[:]) 372 req.Key = nil 373 } 374 // get peer addresses from hive 375 for _, peer := range self.getPeers(key, int(req.MaxPeers)) { 376 addrs = append(addrs, peer.remoteAddr) 377 } 378 log.Debug(fmt.Sprintf("Hive sending %d peer addresses to %v. req.Id: %v, req.Key: %v", len(addrs), req.from, req.Id, req.Key.Log())) 379 380 peersData := &peersMsgData{ 381 Peers: addrs, 382 Key: req.Key, 383 Id: req.Id, 384 } 385 peersData.setTimeout(req.timeout) 386 req.from.peers(peersData) 387 } 388 } 389 } 390 391 func (self *Hive) String() string { 392 return self.kad.String() 393 }