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