lnd.xprv/server.go

401 lines
9.1 KiB
Go
Raw Normal View History

2015-12-26 09:09:17 +03:00
package main
import (
2016-01-17 06:07:44 +03:00
"encoding/hex"
"fmt"
"net"
"sync"
"sync/atomic"
"github.com/lightningnetwork/lnd/channeldb"
2016-01-17 06:07:44 +03:00
"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 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
// identityPriv is the private key used to authenticate any incoming
// connections.
identityPriv *btcec.PrivateKey
2016-01-17 06:07:44 +03:00
listeners []net.Listener
peers map[int32]*peer
chanIndexMtx sync.RWMutex
chanIndex map[wire.OutPoint]*peer
rpcServer *rpcServer
// 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
2016-01-17 06:07:44 +03:00
queries chan interface{}
wg sync.WaitGroup
quit chan struct{}
}
// 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) {
privKey, err := getIdentityPrivKey(chanDB, wallet)
2016-01-17 06:07:44 +03:00
if err != nil {
return nil, err
}
listeners := make([]net.Listener, len(listenAddrs))
for i, addr := range listenAddrs {
listeners[i], err = lndc.NewListener(privKey, addr)
if err != nil {
return nil, err
}
}
s := &server{
chanDB: chanDB,
fundingMgr: newFundingManager(wallet),
lnwallet: wallet,
identityPriv: privKey,
2016-01-17 06:07:44 +03:00
listeners: listeners,
peers: make(map[int32]*peer),
chanIndex: make(map[wire.OutPoint]*peer),
2016-01-17 06:07:44 +03:00
newPeers: make(chan *peer, 100),
donePeers: make(chan *peer, 100),
queries: make(chan interface{}),
quit: make(chan struct{}),
}
s.rpcServer = newRpcServer(s)
return s, 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
}
// Start all the listeners.
for _, l := range s.listeners {
s.wg.Add(1)
go s.listener(l)
}
s.fundingMgr.Start()
s.wg.Add(2)
go s.peerManager()
go s.queryHandler()
}
// 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
}
// 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 {
select {
// New peers.
case p := <-s.newPeers:
s.addPeer(p)
// Finished peers.
case p := <-s.donePeers:
s.removePeer(p)
case <-s.quit:
break out
}
}
s.wg.Done()
}
// 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
}
// 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:
s.handleConnectPeer(msg)
case *listPeersMsg:
s.handleListPeers(msg)
}
case <-s.quit:
break out
}
}
s.wg.Done()
}
// handleListPeers...
func (s *server) handleListPeers(msg *listPeersMsg) {
peers := make([]*peer, 0, len(s.peers))
for _, peer := range s.peers {
peers = append(peers, peer)
}
msg.resp <- peers
}
// 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
}()
}
}
}
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 {
conn, err := l.Accept()
if err != nil {
// Only log the error message if we aren't currently
// shutting down.
if atomic.LoadInt32(&s.shutdown) == 0 {
srvrLog.Errorf("Can't accept connection: %v", err)
}
continue
}
srvrLog.Tracef("New inbound connection from %v", conn.RemoteAddr())
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()
}
2016-01-17 06:05:13 +03:00
// getIdentityPrivKey gets the identity private key out of the wallet DB.
func getIdentityPrivKey(c *channeldb.DB,
w *lnwallet.LightningWallet) (*btcec.PrivateKey, error) {
// First retrieve the current identity address for this peer.
adr, err := c.GetIdAdr()
2016-01-17 06:05:13 +03:00
if err != nil {
return nil, err
}
// Using the ID address, request the private key coresponding to the
// address from the wallet's address manager.
adr2, err := w.Manager.Address(adr)
2016-01-17 06:05:13 +03:00
if err != nil {
return nil, err
}
serializedKey := adr2.(waddrmgr.ManagedPubKeyAddress).PubKey().SerializeCompressed()
keyEncoded := hex.EncodeToString(serializedKey)
ltndLog.Infof("identity address: %v", adr)
ltndLog.Infof("identity pubkey retrieved: %v", keyEncoded)
2016-01-17 06:05:13 +03:00
priv, err := adr2.(waddrmgr.ManagedPubKeyAddress).PrivKey()
if err != nil {
return nil, err
}
return priv, nil
}