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 }