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  }