github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/gateway/nodes.go (about) 1 package gateway 2 3 import ( 4 "errors" 5 "net" 6 "time" 7 8 "github.com/NebulousLabs/Sia/crypto" 9 "github.com/NebulousLabs/Sia/encoding" 10 "github.com/NebulousLabs/Sia/modules" 11 ) 12 13 const ( 14 maxSharedNodes = 10 15 maxAddrLength = 100 16 minPeers = 3 17 ) 18 19 // addNode adds an address to the set of nodes on the network. 20 func (g *Gateway) addNode(addr modules.NetAddress) error { 21 if _, exists := g.nodes[addr]; exists { 22 return errors.New("node already added") 23 } else if net.ParseIP(addr.Host()) == nil { 24 return errors.New("address is not routable: " + string(addr)) 25 } else if addr.IsValid() != nil { 26 return errors.New("address is not valid: " + string(addr)) 27 } 28 g.nodes[addr] = struct{}{} 29 return nil 30 } 31 32 func (g *Gateway) removeNode(addr modules.NetAddress) error { 33 if _, exists := g.nodes[addr]; !exists { 34 return errors.New("no record of that node") 35 } 36 delete(g.nodes, addr) 37 g.log.Println("INFO: removed node", addr) 38 return nil 39 } 40 41 func (g *Gateway) randomNode() (modules.NetAddress, error) { 42 if len(g.nodes) > 0 { 43 r, _ := crypto.RandIntn(len(g.nodes)) 44 for node := range g.nodes { 45 if r <= 0 { 46 return node, nil 47 } 48 r-- 49 } 50 } 51 52 return "", errNoPeers 53 } 54 55 // shareNodes is the receiving end of the ShareNodes RPC. It writes up to 10 56 // randomly selected nodes to the caller. 57 func (g *Gateway) shareNodes(conn modules.PeerConn) error { 58 id := g.mu.RLock() 59 var nodes []modules.NetAddress 60 for node := range g.nodes { 61 if len(nodes) == maxSharedNodes { 62 break 63 } 64 nodes = append(nodes, node) 65 } 66 g.mu.RUnlock(id) 67 return encoding.WriteObject(conn, nodes) 68 } 69 70 // requestNodes is the calling end of the ShareNodes RPC. 71 func (g *Gateway) requestNodes(conn modules.PeerConn) error { 72 var nodes []modules.NetAddress 73 if err := encoding.ReadObject(conn, &nodes, maxSharedNodes*maxAddrLength); err != nil { 74 return err 75 } 76 id := g.mu.Lock() 77 for _, node := range nodes { 78 err := g.addNode(node) 79 if err != nil { 80 g.log.Printf("WARN: peer '%v' send the invalid addr '%v'", conn.RemoteAddr(), node) 81 } 82 } 83 g.save() 84 g.mu.Unlock(id) 85 return nil 86 } 87 88 // relayNode is the recipient end of the RelayNode RPC. It reads a node, adds 89 // it to the Gateway's node list, and relays it to each of the Gateway's 90 // peers. If the node is already in the node list, it is not relayed. 91 func (g *Gateway) relayNode(conn modules.PeerConn) error { 92 // read address 93 var addr modules.NetAddress 94 if err := encoding.ReadObject(conn, &addr, maxAddrLength); err != nil { 95 return err 96 } 97 // add node 98 err := func() error { 99 // We wrap this logic in an anonymous function so we can defer Unlock to 100 // avoid managing locks across branching. 101 id := g.mu.Lock() 102 defer g.mu.Unlock(id) 103 if err := g.addNode(addr); err != nil { 104 return err 105 } 106 if err := g.save(); err != nil { 107 return err 108 } 109 return nil 110 }() 111 if err != nil { 112 return err 113 } 114 // relay 115 peers := g.Peers() 116 go g.Broadcast("RelayNode", addr, peers) 117 return nil 118 } 119 120 // sendAddress is the calling end of the RelayNode RPC. 121 func (g *Gateway) sendAddress(conn modules.PeerConn) error { 122 // don't send if we aren't connectible 123 if g.Address().IsValid() != nil { 124 return errors.New("can't send address without knowing external IP") 125 } 126 return encoding.WriteObject(conn, g.Address()) 127 } 128 129 // threadedNodeManager tries to keep the Gateway's node list healthy. As long 130 // as the Gateway has fewer than minNodeListSize nodes, it asks a random peer 131 // for more nodes. It also continually pings nodes in order to establish their 132 // connectivity. Unresponsive nodes are aggressively removed. 133 func (g *Gateway) threadedNodeManager() { 134 for { 135 select { 136 case <-time.After(5 * time.Second): 137 case <-g.closeChan: 138 return 139 } 140 141 id := g.mu.RLock() 142 numNodes := len(g.nodes) 143 peer, err := g.randomPeer() 144 g.mu.RUnlock(id) 145 if err != nil { 146 // can't do much until we have peers 147 continue 148 } 149 150 if numNodes < minNodeListLen { 151 g.RPC(peer, "ShareNodes", g.requestNodes) 152 } 153 154 // find an untested node to check 155 id = g.mu.RLock() 156 node, err := g.randomNode() 157 g.mu.RUnlock(id) 158 if err != nil { 159 continue 160 } 161 162 // try to connect 163 conn, err := net.DialTimeout("tcp", string(node), dialTimeout) 164 if err != nil { 165 id = g.mu.Lock() 166 g.removeNode(node) 167 g.save() 168 g.mu.Unlock(id) 169 continue 170 } 171 // if connection succeeds, supply an unacceptable version to ensure 172 // they won't try to add us as a peer 173 encoding.WriteObject(conn, "0.0.0") 174 conn.Close() 175 // sleep for an extra 10 minutes after success; we don't want to spam 176 // connectable nodes 177 select { 178 case <-time.After(10 * time.Minute): 179 case <-g.closeChan: 180 return 181 } 182 } 183 }