github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/network/discovery.go (about)

     1  // Copyleft 2016 The susy-graviton Authors
     2  // This file is part of the susy-graviton library.
     3  //
     4  // The susy-graviton 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 susy-graviton library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MSRCHANTABILITY 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 susy-graviton library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package network
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"github.com/susy-go/susy-graviton/swarm/pot"
    25  )
    26  
    27  // discovery bzz extension for requesting and relaying node address records
    28  
    29  // Peer wraps BzzPeer and embeds Kademlia overlay connectivity driver
    30  type Peer struct {
    31  	*BzzPeer
    32  	kad       *Kademlia
    33  	sentPeers bool            // whether we already sent peer closer to this address
    34  	mtx       sync.RWMutex    //
    35  	peers     map[string]bool // tracks node records sent to the peer
    36  	depth     uint8           // the proximity order advertised by remote as depth of saturation
    37  }
    38  
    39  // NewPeer constructs a discovery peer
    40  func NewPeer(p *BzzPeer, kad *Kademlia) *Peer {
    41  	d := &Peer{
    42  		kad:     kad,
    43  		BzzPeer: p,
    44  		peers:   make(map[string]bool),
    45  	}
    46  	// record remote as seen so we never send a peer its own record
    47  	d.seen(p.BzzAddr)
    48  	return d
    49  }
    50  
    51  // HandleMsg is the message handler that delegates incoming messages
    52  func (d *Peer) HandleMsg(ctx context.Context, msg interface{}) error {
    53  	switch msg := msg.(type) {
    54  
    55  	case *peersMsg:
    56  		return d.handlePeersMsg(msg)
    57  
    58  	case *subPeersMsg:
    59  		return d.handleSubPeersMsg(msg)
    60  
    61  	default:
    62  		return fmt.Errorf("unknown message type: %T", msg)
    63  	}
    64  }
    65  
    66  // NotifyDepth sends a message to all connections if depth of saturation is changed
    67  func NotifyDepth(depth uint8, kad *Kademlia) {
    68  	f := func(val *Peer, po int) bool {
    69  		val.NotifyDepth(depth)
    70  		return true
    71  	}
    72  	kad.EachConn(nil, 255, f)
    73  }
    74  
    75  // NotifyPeer informs all peers about a newly added node
    76  func NotifyPeer(p *BzzAddr, k *Kademlia) {
    77  	f := func(val *Peer, po int) bool {
    78  		val.NotifyPeer(p, uint8(po))
    79  		return true
    80  	}
    81  	k.EachConn(p.Address(), 255, f)
    82  }
    83  
    84  // NotifyPeer notifies the remote node (recipient) about a peer if
    85  // the peer's PO is within the recipients advertised depth
    86  // OR the peer is closer to the recipient than self
    87  // unless already notified during the connection session
    88  func (d *Peer) NotifyPeer(a *BzzAddr, po uint8) {
    89  	// immediately return
    90  	if (po < d.getDepth() && pot.ProxCmp(d.kad.BaseAddr(), d, a) != 1) || d.seen(a) {
    91  		return
    92  	}
    93  	resp := &peersMsg{
    94  		Peers: []*BzzAddr{a},
    95  	}
    96  	go d.Send(context.TODO(), resp)
    97  }
    98  
    99  // NotifyDepth sends a subPeers Msg to the receiver notifying them about
   100  // a change in the depth of saturation
   101  func (d *Peer) NotifyDepth(po uint8) {
   102  	go d.Send(context.TODO(), &subPeersMsg{Depth: po})
   103  }
   104  
   105  /*
   106  peersMsg is the message to pass peer information
   107  It is always a response to a peersRequestMsg
   108  
   109  The encoding of a peer address is identical the devp2p base protocol peers
   110  messages: [IP, Port, NodeID],
   111  Note that a node's FileStore address is not the NodeID but the hash of the NodeID.
   112  
   113  TODO:
   114  To mitigate against spurious peers messages, requests should be remembered
   115  and correctness of responses should be checked
   116  
   117  If the proxBin of peers in the response is incorrect the sender should be
   118  disconnected
   119  */
   120  
   121  // peersMsg encapsulates an array of peer addresses
   122  // used for communicating about known peers
   123  // relevant for bootstrapping connectivity and updating peersets
   124  type peersMsg struct {
   125  	Peers []*BzzAddr
   126  }
   127  
   128  // String pretty prints a peersMsg
   129  func (msg peersMsg) String() string {
   130  	return fmt.Sprintf("%T: %v", msg, msg.Peers)
   131  }
   132  
   133  // handlePeersMsg called by the protocol when receiving peerset (for target address)
   134  // list of nodes ([]PeerAddr in peersMsg) is added to the overlay db using the
   135  // Register interface method
   136  func (d *Peer) handlePeersMsg(msg *peersMsg) error {
   137  	// register all addresses
   138  	if len(msg.Peers) == 0 {
   139  		return nil
   140  	}
   141  
   142  	for _, a := range msg.Peers {
   143  		d.seen(a)
   144  		NotifyPeer(a, d.kad)
   145  	}
   146  	return d.kad.Register(msg.Peers...)
   147  }
   148  
   149  // subPeers msg is communicating the depth of the overlay table of a peer
   150  type subPeersMsg struct {
   151  	Depth uint8
   152  }
   153  
   154  // String returns the pretty printer
   155  func (msg subPeersMsg) String() string {
   156  	return fmt.Sprintf("%T: request peers > PO%02d. ", msg, msg.Depth)
   157  }
   158  
   159  func (d *Peer) handleSubPeersMsg(msg *subPeersMsg) error {
   160  	if !d.sentPeers {
   161  		d.setDepth(msg.Depth)
   162  		var peers []*BzzAddr
   163  		d.kad.EachConn(d.Over(), 255, func(p *Peer, po int) bool {
   164  			if pob, _ := Pof(d, d.kad.BaseAddr(), 0); pob > po {
   165  				return false
   166  			}
   167  			if !d.seen(p.BzzAddr) {
   168  				peers = append(peers, p.BzzAddr)
   169  			}
   170  			return true
   171  		})
   172  		if len(peers) > 0 {
   173  			go d.Send(context.TODO(), &peersMsg{Peers: peers})
   174  		}
   175  	}
   176  	d.sentPeers = true
   177  	return nil
   178  }
   179  
   180  // seen takes an peer address and checks if it was sent to a peer already
   181  // if not, marks the peer as sent
   182  func (d *Peer) seen(p *BzzAddr) bool {
   183  	d.mtx.Lock()
   184  	defer d.mtx.Unlock()
   185  	k := string(p.Address())
   186  	if d.peers[k] {
   187  		return true
   188  	}
   189  	d.peers[k] = true
   190  	return false
   191  }
   192  
   193  func (d *Peer) getDepth() uint8 {
   194  	d.mtx.RLock()
   195  	defer d.mtx.RUnlock()
   196  	return d.depth
   197  }
   198  
   199  func (d *Peer) setDepth(depth uint8) {
   200  	d.mtx.Lock()
   201  	defer d.mtx.Unlock()
   202  	d.depth = depth
   203  }