From d2f3d5ef6c59bdacc5914bd1da81411d7d3b497f Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Thu, 13 Jun 2019 17:29:47 -0700 Subject: [PATCH] server: add watchtower client --- lnd.go | 17 +++++++++-- server.go | 55 ++++++++++++++++++++++++++++++++++- watchtower/wtclient/client.go | 5 ++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/lnd.go b/lnd.go index f9ccc413..f7532823 100644 --- a/lnd.go +++ b/lnd.go @@ -51,6 +51,7 @@ import ( "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/walletunlocker" + "github.com/lightningnetwork/lnd/watchtower/wtdb" ) const ( @@ -313,11 +314,23 @@ func Main() error { "is proxying over Tor as well", cfg.Tor.StreamIsolation) } + // If the watchtower client should be active, open the client database. + // This is done here so that Close always executes when lndMain returns. + var towerClientDB *wtdb.ClientDB + if cfg.WtClient.IsActive() { + var err error + towerClientDB, err = wtdb.OpenClientDB(graphDir) + if err != nil { + ltndLog.Errorf("Unable to open watchtower client db: %v", err) + } + defer towerClientDB.Close() + } + // Set up the core server which will listen for incoming peer // connections. server, err := newServer( - cfg.Listeners, chanDB, activeChainControl, idPrivKey, - walletInitParams.ChansToRestore, + cfg.Listeners, chanDB, towerClientDB, activeChainControl, + idPrivKey, walletInitParams.ChansToRestore, ) if err != nil { srvrLog.Errorf("unable to create server: %v\n", err) diff --git a/server.go b/server.go index dd91d5bd..ee6998ae 100644 --- a/server.go +++ b/server.go @@ -50,6 +50,9 @@ import ( "github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/walletunlocker" + "github.com/lightningnetwork/lnd/watchtower/wtclient" + "github.com/lightningnetwork/lnd/watchtower/wtdb" + "github.com/lightningnetwork/lnd/watchtower/wtpolicy" "github.com/lightningnetwork/lnd/zpay32" ) @@ -204,6 +207,8 @@ type server struct { sphinx *htlcswitch.OnionProcessor + towerClient wtclient.Client + connMgr *connmgr.ConnManager sigPool *lnwallet.SigPool @@ -282,7 +287,8 @@ func noiseDial(idPriv *btcec.PrivateKey) func(net.Addr) (net.Conn, error) { // newServer creates a new instance of the server which is to listen using the // passed listener address. -func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl, +func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, + towerClientDB *wtdb.ClientDB, cc *chainControl, privKey *btcec.PrivateKey, chansToRestore walletunlocker.ChannelsToRecover) (*server, error) { @@ -1056,6 +1062,39 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl, return nil, err } + if cfg.WtClient.IsActive() { + policy := wtpolicy.DefaultPolicy() + + if cfg.WtClient.SweepFeeRate != 0 { + // We expose the sweep fee rate in sat/byte, but the + // tower protocol operations on sat/kw. + sweepRateSatPerByte := lnwallet.SatPerKVByte( + 1000 * cfg.WtClient.SweepFeeRate, + ) + policy.SweepFeeRate = sweepRateSatPerByte.FeePerKWeight() + } + + s.towerClient, err = wtclient.New(&wtclient.Config{ + Signer: cc.wallet.Cfg.Signer, + NewAddress: func() ([]byte, error) { + return newSweepPkScript(cc.wallet) + }, + SecretKeyRing: s.cc.keyRing, + Dial: cfg.net.Dial, + AuthDial: wtclient.AuthDial, + DB: towerClientDB, + Policy: wtpolicy.DefaultPolicy(), + PrivateTower: cfg.WtClient.PrivateTowers[0], + ChainHash: *activeNetParams.GenesisHash, + MinBackoff: 10 * time.Second, + MaxBackoff: 5 * time.Minute, + ForceQuitDelay: wtclient.DefaultForceQuitDelay, + }) + if err != nil { + return nil, err + } + } + // Create the connection manager which will be responsible for // maintaining persistent outbound connections and also accepting new // incoming connections @@ -1128,6 +1167,12 @@ func (s *server) Start() error { startErr = err return } + if s.towerClient != nil { + if err := s.towerClient.Start(); err != nil { + startErr = err + return + } + } if err := s.htlcSwitch.Start(); err != nil { startErr = err return @@ -1290,6 +1335,14 @@ func (s *server) Stop() error { s.DisconnectPeer(peer.addr.IdentityKey) } + // Now that all connections have been torn down, stop the tower + // client which will reliably flush all queued states to the + // tower. If this is halted for any reason, the force quit timer + // will kick in and abort to allow this method to return. + if s.towerClient != nil { + s.towerClient.Stop() + } + // Wait for all lingering goroutines to quit. s.wg.Wait() diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index dc250980..7845d81e 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -29,6 +29,11 @@ const ( // DefaultStatInterval specifies the default interval between logging // metrics about the client's operation. DefaultStatInterval = 30 * time.Second + + // DefaultForceQuitDelay specifies the default duration after which the + // client should abandon any pending updates or session negotiations + // before terminating. + DefaultForceQuitDelay = 10 * time.Second ) // Client is the primary interface used by the daemon to control a client's