github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/lnp2p/peermgr.go (about) 1 package lnp2p 2 3 //"crypto/ecdsa" // TODO Use ecdsa not koblitz 4 import ( 5 "crypto/ecdsa" 6 "fmt" 7 "net" 8 "sync" 9 "time" 10 11 "github.com/mit-dci/lit/btcutil/hdkeychain" 12 "github.com/mit-dci/lit/crypto/koblitz" 13 "github.com/mit-dci/lit/eventbus" 14 "github.com/mit-dci/lit/lncore" 15 "github.com/mit-dci/lit/lndc" 16 "github.com/mit-dci/lit/lnutil" 17 "github.com/mit-dci/lit/logging" 18 "github.com/mit-dci/lit/nat" 19 "github.com/mit-dci/lit/portxo" 20 ) 21 22 type privkey *koblitz.PrivateKey 23 type pubkey *koblitz.PublicKey 24 25 // MaxNodeCount is the size of the peerIdx->LnAddr array. 26 // TEMP This shouldn't be necessary. 27 const MaxNodeCount = 1024 28 29 // PeerManager . 30 type PeerManager struct { 31 32 // Biographical. 33 idkey privkey 34 peerdb lncore.LitPeerStorage 35 ebus *eventbus.EventBus 36 mproc MessageProcessor 37 netsettings *NetSettings 38 39 // Peer tracking. 40 peers []lncore.LnAddr // compatibility 41 peerMap map[lncore.LnAddr]*Peer 42 43 // Accepting connections. 44 listeningPorts map[int]*listeningthread 45 46 // Outgoing messages. 47 sending bool 48 outqueue chan outgoingmsg 49 50 // Tracker 51 trackerURL string 52 53 // Sync. 54 mtx *sync.Mutex 55 } 56 57 const outgoingbuf = 16 58 59 // NetSettings is a container struct for misc network settings like NAT 60 // holepunching and proxies. 61 type NetSettings struct { 62 NatMode *string `json:"natmode"` 63 64 ProxyAddr *string `json:"proxyserv"` 65 ProxyAuth *string `json:"proxyauth"` 66 } 67 68 // NewPeerManager creates a peer manager from a root key 69 func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, trackerURL string, bus *eventbus.EventBus, ns *NetSettings) (*PeerManager, error) { 70 k, err := computeIdentKeyFromRoot(rootkey) 71 if err != nil { 72 return nil, err 73 } 74 75 pm := &PeerManager{ 76 idkey: k, 77 peerdb: pdb, 78 ebus: bus, 79 mproc: NewMessageProcessor(), 80 netsettings: ns, 81 peers: make([]lncore.LnAddr, MaxNodeCount), 82 peerMap: map[lncore.LnAddr]*Peer{}, 83 listeningPorts: map[int]*listeningthread{}, 84 sending: false, 85 trackerURL: trackerURL, 86 outqueue: make(chan outgoingmsg, outgoingbuf), 87 mtx: &sync.Mutex{}, 88 } 89 90 return pm, nil 91 } 92 93 // GetMessageProcessor gets the message processor for this peer manager that's passed incoming messasges from peers. 94 func (pm *PeerManager) GetMessageProcessor() *MessageProcessor { 95 return &pm.mproc 96 } 97 98 // GetExternalAddress returns the human-readable LN address 99 func (pm *PeerManager) GetExternalAddress() string { 100 idk := pm.idkey // lol 101 c := koblitz.PublicKey(ecdsa.PublicKey(idk.PublicKey)) 102 addr := convertPubkeyToLitAddr(pubkey(&c)) 103 return string(addr) 104 } 105 106 func computeIdentKeyFromRoot(rootkey *hdkeychain.ExtendedKey) (privkey, error) { 107 var kg portxo.KeyGen 108 kg.Depth = 5 109 kg.Step[0] = 44 | 1<<31 // from bip44, but not actually sensical in this context 110 kg.Step[1] = 513 | 1<<31 // magic 111 kg.Step[2] = 9 | 1<<31 // magic 112 kg.Step[3] = 0 | 1<<31 113 kg.Step[4] = 0 | 1<<31 114 k, err := kg.DerivePrivateKey(rootkey) 115 if err != nil { 116 return nil, err 117 } 118 return privkey(k), nil 119 } 120 121 // GetPeerIdx is a convenience function for working with older code. 122 func (pm *PeerManager) GetPeerIdx(peer *Peer) uint32 { 123 if peer.idx == nil { 124 return 0 125 } 126 return *peer.idx 127 } 128 129 // GetPeer returns the peer with the given lnaddr. 130 func (pm *PeerManager) GetPeer(lnaddr lncore.LnAddr) *Peer { 131 p, ok := pm.peerMap[lnaddr] 132 if !ok { 133 return nil 134 } 135 return p 136 } 137 138 // GetPeerByIdx is a compatibility function for getting a peer by its "peer id". 139 func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { 140 if id < 0 || id >= int32(len(pm.peers)) { 141 return nil 142 } 143 return pm.peerMap[pm.peers[id]] 144 } 145 146 // TryConnectAddress attempts to connect to the specified LN address. 147 func (pm *PeerManager) TryConnectAddress(addr string) (*Peer, error) { 148 149 // Figure out who we're trying to connect to. 150 who, where := splitAdrString(addr) 151 if where == "" { 152 ipv4, _, err := lnutil.Lookup(addr, pm.trackerURL, "") 153 if err != nil { 154 return nil, err 155 } 156 where = fmt.Sprintf("%s:2448", ipv4) 157 } 158 159 lnwho, err := lncore.ParseLnAddr(who) 160 if err != nil { 161 return nil, err 162 } 163 164 x, y := pm.tryConnectPeer(where, &lnwho) 165 return x, y 166 167 } 168 169 func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr) (*Peer, error) { 170 171 // lnaddr check, to make sure that we do the right thing. 172 if lnaddr == nil { 173 return nil, fmt.Errorf("connection to a peer with unknown lnaddr not supported yet") 174 } 175 176 dialer := net.Dial 177 178 // Use a proxy server if applicable. 179 ns := pm.netsettings 180 if ns != nil && ns.ProxyAddr != nil { 181 d, err := connectToProxyTCP(*ns.ProxyAddr, ns.ProxyAuth) 182 if err != nil { 183 return nil, err 184 } 185 dialer = d 186 } 187 188 // Create the connection. 189 lndcconn, err := lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer) 190 if err != nil { 191 return nil, err 192 } 193 194 // Try to set up the new connection. 195 p, err := pm.handleNewConnection(lndcconn, *lnaddr) 196 if err != nil { 197 return nil, err 198 } 199 200 // Now start listening for inbound traffic. 201 // (it *also* took me a while to realize I forgot *this*) 202 go processConnectionInboundTraffic(p, pm) 203 204 // Return 205 return p, nil 206 207 } 208 209 func (pm *PeerManager) handleNewConnection(conn *lndc.Conn, expectedAddr lncore.LnAddr) (*Peer, error) { 210 211 // Now that we've got the connection, actually create the peer object. 212 pk := pubkey(conn.RemotePub()) 213 rlitaddr := convertPubkeyToLitAddr(pk) 214 215 if rlitaddr != expectedAddr { 216 conn.Close() 217 return nil, fmt.Errorf("peermgr: Connection init error, expected addr %s got addr %s", expectedAddr, rlitaddr) 218 } 219 220 p := &Peer{ 221 lnaddr: rlitaddr, 222 nickname: nil, 223 conn: conn, 224 idpubkey: pk, 225 226 // TEMP 227 idx: nil, 228 } 229 230 pi, err := pm.peerdb.GetPeerInfo(expectedAddr) 231 if err != nil { 232 logging.Errorf("peermgr: Problem loading peer info from DB: %s\n", err.Error()) 233 // don't kill the connection? 234 } 235 236 if pi == nil { 237 pidx, err := pm.peerdb.GetUniquePeerIdx() 238 if err != nil { 239 logging.Errorf("Problem getting unique peeridx from DB: %s\n", err.Error()) 240 } else { 241 p.idx = &pidx 242 } 243 raddr := conn.RemoteAddr().String() 244 pi = &lncore.PeerInfo{ 245 LnAddr: &rlitaddr, 246 Nickname: nil, 247 NetAddr: &raddr, 248 PeerIdx: pidx, 249 } 250 err = pm.peerdb.AddPeer(p.GetLnAddr(), *pi) 251 if err != nil { 252 logging.Errorf("Error saving new peer to DB: %s\n", err.Error()) 253 } 254 } else { 255 p.nickname = pi.Nickname 256 // TEMP 257 p.idx = &pi.PeerIdx 258 } 259 260 // Register the peer we just connected to! 261 // (it took me a while to realize I forgot this) 262 pm.registerPeer(p) 263 264 // Now actually return the peer. 265 return p, nil 266 267 } 268 269 func (pm *PeerManager) registerPeer(peer *Peer) { 270 271 lnaddr := peer.lnaddr 272 273 // We're making changes to the manager so keep stuff away while we set up. 274 pm.mtx.Lock() 275 defer pm.mtx.Unlock() 276 277 logging.Infof("peermgr: New peer %s\n", peer.GetLnAddr()) 278 279 // Append peer to peer list and add to peermap 280 pm.peers[int(*peer.idx)] = lnaddr // TEMP This idx logic is a litte weird. 281 pm.peerMap[lnaddr] = peer 282 peer.pmgr = pm 283 284 // Announce the peer has been added. 285 e := NewPeerEvent{ 286 Addr: lnaddr, 287 Peer: peer, 288 RemoteInitiated: false, 289 290 // TODO Remove these. 291 RemotePub: peer.idpubkey, 292 Conn: peer.conn, 293 } 294 pm.ebus.Publish(e) 295 296 } 297 298 func (pm *PeerManager) unregisterPeer(peer *Peer) { 299 300 // Again, sensitive changes we should get a lock to do first. 301 pm.mtx.Lock() 302 defer pm.mtx.Unlock() 303 304 logging.Infof("peermgr: Unregistering peer: %s\n", peer.GetLnAddr()) 305 306 // Remove the peer idx entry. 307 idx := pm.GetPeerIdx(peer) 308 pm.peers[idx] = "" 309 310 // Remove the actual peer entry. 311 pm.peerMap[peer.GetLnAddr()] = nil 312 313 // More cleanup. 314 peer.conn = nil 315 peer.idx = nil 316 peer.pmgr = nil 317 318 } 319 320 // DisconnectPeer disconnects a peer from ourselves and does relevant cleanup. 321 func (pm *PeerManager) DisconnectPeer(peer *Peer) error { 322 323 err := peer.conn.Close() 324 if err != nil { 325 return err 326 } 327 328 // This will cause the peer disconnect event to be raised when the reader 329 // goroutine started to exit and run the unregistration 330 331 return nil 332 333 } 334 335 // ListenOnPort attempts to start a goroutine lisening on the port. 336 func (pm *PeerManager) ListenOnPort(port int) error { 337 338 // Do NAT setup stuff. 339 ns := pm.netsettings 340 if ns != nil && ns.NatMode != nil { 341 342 // Do some type juggling. 343 lisPort := uint16(port) 344 345 // Actually figure out what we're going to do. 346 if *ns.NatMode == "upnp" { 347 // Universal Plug-n-Play 348 logging.Infof("Attempting port forwarding via UPnP...") 349 err := nat.SetupUpnp(lisPort) 350 if err != nil { 351 return err 352 } 353 } else if *ns.NatMode == "pmp" { 354 // NAT Port Mapping Protocol 355 timeout := time.Duration(10 * time.Second) 356 logging.Infof("Attempting port forwarding via PMP...") 357 _, err := nat.SetupPmp(timeout, lisPort) 358 if err != nil { 359 return err 360 } 361 } else { 362 return fmt.Errorf("invalid NAT type: %s", *ns.NatMode) 363 } 364 } 365 366 threadobj := &listeningthread{ 367 listener: nil, 368 } 369 370 // Publish the new thread 371 res, err := pm.ebus.Publish(NewListeningPortEvent{port}) 372 if err != nil { 373 return err 374 } 375 376 if !res { 377 return fmt.Errorf("listen cancelled by event handler") 378 } 379 380 // Try to start listening. 381 // TODO Listen on proxy if possible? 382 logging.Info("PORT: ", port) 383 listener, err := lndc.NewListener(pm.idkey, port) 384 if err != nil { 385 logging.Errorf("listening failed: %s\n", err.Error()) 386 logging.Info(err) 387 pm.ebus.Publish(StopListeningPortEvent{ 388 Port: port, 389 Reason: "initfail", 390 }) 391 return err 392 } 393 394 threadobj.listener = listener 395 396 // Install the thread object. 397 pm.mtx.Lock() 398 pm.listeningPorts[port] = threadobj 399 pm.mtx.Unlock() 400 401 // Activate the MessageProcessor if we haven't yet. 402 if !pm.mproc.IsActive() { 403 pm.mproc.Activate() 404 } 405 406 // Actually start it 407 go acceptConnections(listener, port, pm) 408 409 return nil 410 411 } 412 413 // GetListeningAddrs returns the listening addresses. 414 func (pm *PeerManager) GetListeningAddrs() []string { 415 pm.mtx.Lock() 416 defer pm.mtx.Unlock() 417 ports := make([]string, 0) 418 for _, t := range pm.listeningPorts { 419 ports = append(ports, t.listener.Addr().String()) 420 } 421 return ports 422 } 423 424 // StopListening closes the socket listened on the given address, stopping the goroutine. 425 func (pm *PeerManager) StopListening(port int) error { 426 427 pm.mtx.Lock() 428 defer pm.mtx.Unlock() 429 430 // This will interrupt the .Accept() call in the other goroutine, and handle cleanup for us. 431 lt, ok := pm.listeningPorts[port] 432 if !ok { 433 return fmt.Errorf("not listening") 434 } 435 436 lt.listener.Close() 437 return nil 438 439 } 440 441 // StartSending starts a goroutine to start sending queued messages out to peers. 442 func (pm *PeerManager) StartSending() error { 443 if pm.sending { 444 return fmt.Errorf("already sending") 445 } 446 pm.sending = true 447 go sendMessages(pm.outqueue) 448 return nil 449 } 450 451 // StopSending has us stop sending new messages to peers. 452 func (pm *PeerManager) StopSending() error { 453 if !pm.sending { 454 return fmt.Errorf("not sending") 455 } 456 fc := make(chan error) 457 pm.outqueue <- outgoingmsg{nil, nil, &fc} // sends a message to stop the goroutine 458 459 <-fc // wait for the queue to flush 460 pm.sending = false 461 462 return nil 463 } 464 465 func (pm *PeerManager) queueMessageToPeer(peer *Peer, msg Message, ec *chan error) error { 466 if !pm.sending { 467 return fmt.Errorf("sending is disabled on this peer manager, need to start it?") 468 } 469 pm.outqueue <- outgoingmsg{peer, &msg, ec} 470 return nil 471 } 472 473 // TmpHintPeerIdx sets the peer idx hint for a particular peer. 474 // TEMP This should be removed at some point in the future. 475 func (pm *PeerManager) TmpHintPeerIdx(peer *Peer, idx uint32) error { 476 477 pi, err := pm.peerdb.GetPeerInfo(peer.GetLnAddr()) 478 if err != nil { 479 return err 480 } 481 482 pi.PeerIdx = idx 483 484 return pm.peerdb.UpdatePeer(peer.GetLnAddr(), pi) 485 486 }