lnd: implement connectpeer and listpeers RPC calls
This commit is contained in:
3 files changed
+377
-144
No files matched your search
@@ -11,23 +11,37 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lndc"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
|
||||
"github.com/roasbeef/btcwallet/waddrmgr"
|
||||
)
|
||||
|
||||
// server...
|
||||
// server is the main server of the Lightning Network Daemon. The server
|
||||
// houses global state pertianing to the wallet, database, and the rpcserver.
|
||||
// Additionally, the server is also used as a central messaging bus to interact
|
||||
// with any of its companion objects.
|
||||
type server struct {
|
||||
started int32 // atomic
|
||||
shutdown int32 // atomic
|
||||
|
||||
longTermPriv *btcec.PrivateKey
|
||||
// identityPriv is the private key used to authenticate any incoming
|
||||
// connections.
|
||||
identityPriv *btcec.PrivateKey
|
||||
|
||||
listeners []net.Listener
|
||||
peers map[int32]*peer
|
||||
|
||||
chanIndexMtx sync.RWMutex
|
||||
chanIndex map[wire.OutPoint]*peer
|
||||
|
||||
rpcServer *rpcServer
|
||||
lnwallet *lnwallet.LightningWallet
|
||||
chanDB *channeldb.DB
|
||||
// TODO(roasbeef): add chan notifier also
|
||||
lnwallet *lnwallet.LightningWallet
|
||||
|
||||
// TODO(roasbeef): add to constructor
|
||||
fundingMgr *fundingManager
|
||||
chanDB *channeldb.DB
|
||||
|
||||
newPeers chan *peer
|
||||
donePeers chan *peer
|
||||
@@ -37,7 +51,8 @@ type server struct {
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
// newServer...
|
||||
// newServer creates a new instance of the server which is to listen using the
|
||||
// passed listener address.
|
||||
func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet,
|
||||
chanDB *channeldb.DB) (*server, error) {
|
||||
|
||||
@@ -56,12 +71,14 @@ func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet,
|
||||
|
||||
s := &server{
|
||||
chanDB: chanDB,
|
||||
longTermPriv: privKey,
|
||||
fundingMgr: newFundingManager(wallet),
|
||||
lnwallet: wallet,
|
||||
identityPriv: privKey,
|
||||
listeners: listeners,
|
||||
peers: make(map[int32]*peer),
|
||||
chanIndex: make(map[wire.OutPoint]*peer),
|
||||
newPeers: make(chan *peer, 100),
|
||||
donePeers: make(chan *peer, 100),
|
||||
lnwallet: wallet,
|
||||
queries: make(chan interface{}),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
@@ -71,26 +88,65 @@ func newServer(listenAddrs []string, wallet *lnwallet.LightningWallet,
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// addPeer...
|
||||
func (s *server) addPeer(p *peer) {
|
||||
if p == nil {
|
||||
// Start starts the main daemon server, all requested listeners, and any helper
|
||||
// goroutines.
|
||||
func (s *server) Start() {
|
||||
// Already running?
|
||||
if atomic.AddInt32(&s.started, 1) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore new peers if we're shutting down.
|
||||
if atomic.LoadInt32(&s.shutdown) != 0 {
|
||||
p.Stop()
|
||||
return
|
||||
// Start all the listeners.
|
||||
for _, l := range s.listeners {
|
||||
s.wg.Add(1)
|
||||
go s.listener(l)
|
||||
}
|
||||
|
||||
s.peers[p.peerId] = p
|
||||
s.fundingMgr.Start()
|
||||
|
||||
s.wg.Add(2)
|
||||
go s.peerManager()
|
||||
go s.queryHandler()
|
||||
}
|
||||
|
||||
// removePeer...
|
||||
func (s *server) removePeer(p *peer) {
|
||||
// Stop gracefully shutsdown the main daemon server. This function will signal
|
||||
// any active goroutines, or helper objects to exit, then blocks until they've
|
||||
// all successfully exited. Additionally, any/all listeners are closed.
|
||||
func (s *server) Stop() error {
|
||||
// Bail if we're already shutting down.
|
||||
if atomic.AddInt32(&s.shutdown, 1) != 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop all the listeners.
|
||||
for _, listener := range s.listeners {
|
||||
if err := listener.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown the wallet, funding manager, and the rpc server.
|
||||
s.rpcServer.Stop()
|
||||
s.lnwallet.Shutdown()
|
||||
s.fundingMgr.Stop()
|
||||
|
||||
// Signal all the lingering goroutines to quit.
|
||||
close(s.quit)
|
||||
s.wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// peerManager...
|
||||
// WaitForShutdown blocks all goroutines have been stopped.
|
||||
func (s *server) WaitForShutdown() {
|
||||
s.wg.Wait()
|
||||
}
|
||||
|
||||
// peerManager handles any requests to modify the server's internal state of
|
||||
// all active peers. Additionally, any queries directed at peers will be
|
||||
// handled by this goroutine.
|
||||
//
|
||||
// NOTE: This MUST be run as a goroutine.
|
||||
func (s *server) peerManager() {
|
||||
out:
|
||||
for {
|
||||
@@ -108,67 +164,68 @@ out:
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
// connectPeerMsg...
|
||||
type connectPeerMsg struct {
|
||||
addr *lndc.LNAdr
|
||||
reply chan error
|
||||
// addPeer adds the passed peer to the server's global state of all active
|
||||
// peers.
|
||||
func (s *server) addPeer(p *peer) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore new peers if we're shutting down.
|
||||
if atomic.LoadInt32(&s.shutdown) != 0 {
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
s.peers[p.id] = p
|
||||
}
|
||||
|
||||
// queryHandler...
|
||||
// removePeer removes the passed peer from the server's state of all active
|
||||
// peers.
|
||||
func (s *server) removePeer(p *peer) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore deleting peers if we're shutting down.
|
||||
if atomic.LoadInt32(&s.shutdown) != 0 {
|
||||
p.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
delete(s.peers, p.id)
|
||||
}
|
||||
|
||||
// connectPeerMsg is a message requesting the server to open a connection to a
|
||||
// particular peer. This message also houses an error channel which will be
|
||||
// used to report success/failure.
|
||||
type connectPeerMsg struct {
|
||||
addr *lndc.LNAdr
|
||||
resp chan int32
|
||||
err chan error
|
||||
}
|
||||
|
||||
// listPeersMsg is a message sent to the server in order to obtain a listing
|
||||
// of all currently active channels.
|
||||
type listPeersMsg struct {
|
||||
resp chan []*peer
|
||||
}
|
||||
// queryHandler is a a goroutine dedicated to handling an queries or requests
|
||||
// to mutate the server's global state.
|
||||
//
|
||||
// NOTE: This MUST be run as a goroutine.
|
||||
func (s *server) queryHandler() {
|
||||
// TODO(roabeef): consolidate with peerManager
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case query := <-s.queries:
|
||||
// TODO(roasbeef): make all goroutines?
|
||||
switch msg := query.(type) {
|
||||
case *connectPeerMsg:
|
||||
addr := msg.addr
|
||||
|
||||
// Ensure we're not already connected to this
|
||||
// peer.
|
||||
for _, peer := range s.peers {
|
||||
if peer.lightningAddr.String() ==
|
||||
addr.String() {
|
||||
msg.reply <- fmt.Errorf(
|
||||
"already connected to peer: %v",
|
||||
peer.lightningAddr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Launch a goroutine to connect to the requested
|
||||
// peer so we can continue to handle queries.
|
||||
go func() {
|
||||
// For the lndc crypto handshake, we
|
||||
// either need a compressed pubkey, or a
|
||||
// 20-byte pkh.
|
||||
var remoteId []byte
|
||||
if addr.PubKey == nil {
|
||||
remoteId = addr.Base58Adr.ScriptAddress()
|
||||
} else {
|
||||
remoteId = addr.PubKey.SerializeCompressed()
|
||||
}
|
||||
|
||||
// Attempt to connect to the remote
|
||||
// node. If the we can't make the
|
||||
// connection, or the crypto negotation
|
||||
// breaks down, then return an error to the
|
||||
// caller.
|
||||
ipAddr := addr.NetAddr.String()
|
||||
conn := lndc.NewConn(nil)
|
||||
if err := conn.Dial(
|
||||
s.longTermPriv, ipAddr, remoteId); err != nil {
|
||||
msg.reply <- err
|
||||
}
|
||||
|
||||
// Now that we've established a connection,
|
||||
// create a peer, and it to the set of
|
||||
// currently active peers.
|
||||
peer := newPeer(conn, s)
|
||||
s.newPeers <- peer
|
||||
|
||||
msg.reply <- nil
|
||||
}()
|
||||
s.handleConnectPeer(msg)
|
||||
case *listPeersMsg:
|
||||
s.handleListPeers(msg)
|
||||
}
|
||||
case <-s.quit:
|
||||
break out
|
||||
@@ -178,21 +235,113 @@ out:
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
// ConnectToPeer...
|
||||
func (s *server) ConnectToPeer(addr *lndc.LNAdr) error {
|
||||
reply := make(chan error, 1)
|
||||
// handleListPeers...
|
||||
func (s *server) handleListPeers(msg *listPeersMsg) {
|
||||
peers := make([]*peer, 0, len(s.peers))
|
||||
for _, peer := range s.peers {
|
||||
peers = append(peers, peer)
|
||||
}
|
||||
|
||||
s.queries <- &connectPeerMsg{addr, reply}
|
||||
|
||||
return <-reply
|
||||
msg.resp <- peers
|
||||
}
|
||||
|
||||
// AddPeer...
|
||||
func (s *server) AddPeer(p *peer) {
|
||||
s.newPeers <- p
|
||||
// handleConnectPeer attempts to establish a connection to the address enclosed
|
||||
// within the passed connectPeerMsg. This function is *async*, a goroutine will
|
||||
// be spawned in order to finish the request, and respond to the caller.
|
||||
func (s *server) handleConnectPeer(msg *connectPeerMsg) {
|
||||
addr := msg.addr
|
||||
|
||||
// Ensure we're not already connected to this
|
||||
// peer.
|
||||
for _, peer := range s.peers {
|
||||
if peer.lightningAddr.String() ==
|
||||
addr.String() {
|
||||
msg.err <- fmt.Errorf(
|
||||
"already connected to peer: %v",
|
||||
peer.lightningAddr,
|
||||
)
|
||||
msg.resp <- -1
|
||||
}
|
||||
}
|
||||
|
||||
// Launch a goroutine to connect to the requested
|
||||
// peer so we can continue to handle queries.
|
||||
// TODO(roasbeef): semaphore to limit the number of goroutines for
|
||||
// async requests.
|
||||
go func() {
|
||||
// For the lndc crypto handshake, we
|
||||
// either need a compressed pubkey, or a
|
||||
// 20-byte pkh.
|
||||
var remoteId []byte
|
||||
if addr.PubKey == nil {
|
||||
remoteId = addr.Base58Adr.ScriptAddress()
|
||||
} else {
|
||||
remoteId = addr.PubKey.SerializeCompressed()
|
||||
}
|
||||
|
||||
srvrLog.Debugf("connecting to %v", hex.EncodeToString(remoteId))
|
||||
// Attempt to connect to the remote
|
||||
// node. If the we can't make the
|
||||
// connection, or the crypto negotation
|
||||
// breaks down, then return an error to the
|
||||
// caller.
|
||||
ipAddr := addr.NetAddr.String()
|
||||
conn := lndc.NewConn(nil)
|
||||
if err := conn.Dial(
|
||||
s.identityPriv, ipAddr, remoteId); err != nil {
|
||||
msg.err <- err
|
||||
msg.resp <- -1
|
||||
return
|
||||
}
|
||||
|
||||
// Now that we've established a connection,
|
||||
// create a peer, and it to the set of
|
||||
// currently active peers.
|
||||
peer, err := newPeer(conn, s, activeNetParams.Net, false)
|
||||
if err != nil {
|
||||
srvrLog.Errorf("unable to create peer %v", err)
|
||||
msg.resp <- -1
|
||||
msg.err <- err
|
||||
return
|
||||
}
|
||||
|
||||
peer.Start()
|
||||
s.newPeers <- peer
|
||||
|
||||
msg.resp <- peer.id
|
||||
msg.err <- nil
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// listener...
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, listener := range s.listeners {
|
||||
}
|
||||
|
||||
|
||||
// Peers returns a slice of all active peers.
|
||||
func (s *server) Peers() []*peer {
|
||||
resp := make(chan []*peer)
|
||||
|
||||
s.queries <- &listPeersMsg{resp}
|
||||
|
||||
return <-resp
|
||||
}
|
||||
|
||||
// listener is a goroutine dedicated to accepting in coming peer connections
|
||||
// from the passed listener.
|
||||
//
|
||||
// NOTE: This MUST be run as a goroutine.
|
||||
func (s *server) listener(l net.Listener) {
|
||||
srvrLog.Infof("Server listening on %s", l.Addr())
|
||||
for atomic.LoadInt32(&s.shutdown) == 0 {
|
||||
@@ -207,67 +356,41 @@ func (s *server) listener(l net.Listener) {
|
||||
}
|
||||
|
||||
srvrLog.Tracef("New inbound connection from %v", conn.RemoteAddr())
|
||||
peer := newPeer(conn, s)
|
||||
peer.Start()
|
||||
peer, err := newPeer(conn, s, activeNetParams.Net, true)
|
||||
if err != nil {
|
||||
srvrLog.Errorf("unable to create peer: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
peer.Start()
|
||||
s.newPeers <- peer
|
||||
}
|
||||
|
||||
s.wg.Done()
|
||||
}
|
||||
|
||||
// Start...
|
||||
func (s *server) Start() {
|
||||
// Already running?
|
||||
if atomic.AddInt32(&s.started, 1) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Start all the listeners.
|
||||
for _, l := range s.listeners {
|
||||
s.wg.Add(1)
|
||||
go s.listener(l)
|
||||
}
|
||||
|
||||
s.wg.Add(2)
|
||||
go s.peerManager()
|
||||
go s.queryHandler()
|
||||
}
|
||||
|
||||
// Stop...
|
||||
func (s *server) Stop() error {
|
||||
// Bail if we're already shutting down.
|
||||
if atomic.AddInt32(&s.shutdown, 1) != 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop all the listeners.
|
||||
for _, listener := range s.listeners {
|
||||
if err := listener.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.rpcServer.Stop()
|
||||
s.lnwallet.Shutdown()
|
||||
|
||||
// Signal all the lingering goroutines to quit.
|
||||
close(s.quit)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getIdentityPrivKey gets the identity private key out of the wallet DB.
|
||||
func getIdentityPrivKey(c *channeldb.DB, w *lnwallet.LightningWallet) (*btcec.PrivateKey, error) {
|
||||
func getIdentityPrivKey(c *channeldb.DB,
|
||||
w *lnwallet.LightningWallet) (*btcec.PrivateKey, error) {
|
||||
|
||||
// First retrieve the current identity address for this peer.
|
||||
adr, err := c.GetIdAdr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ltndLog.Infof("got ID address: %s", adr.String())
|
||||
|
||||
// Using the ID address, request the private key coresponding to the
|
||||
// address from the wallet's address manager.
|
||||
adr2, err := w.Manager.Address(adr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ltndLog.Infof("pubkey: %v", hex.EncodeToString(adr2.(waddrmgr.ManagedPubKeyAddress).PubKey().SerializeCompressed()))
|
||||
|
||||
serializedKey := adr2.(waddrmgr.ManagedPubKeyAddress).PubKey().SerializeCompressed()
|
||||
keyEncoded := hex.EncodeToString(serializedKey)
|
||||
ltndLog.Infof("identity address: %v", adr)
|
||||
ltndLog.Infof("identity pubkey retrieved: %v", keyEncoded)
|
||||
|
||||
priv, err := adr2.(waddrmgr.ManagedPubKeyAddress).PrivKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -275,8 +398,3 @@ func getIdentityPrivKey(c *channeldb.DB, w *lnwallet.LightningWallet) (*btcec.Pr
|
||||
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
// WaitForShutdown blocks all goroutines have been stopped.
|
||||
func (s *server) WaitForShutdown() {
|
||||
s.wg.Wait()
|
||||
}
|
||||
Reference in new issue
Block a user