server: add watchtower client

This commit is contained in:
Conner Fromknecht 2019-06-13 17:29:47 -07:00
parent fb8214b808
commit d2f3d5ef6c
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7
3 changed files with 74 additions and 3 deletions

17
lnd.go

@ -51,6 +51,7 @@ import (
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/signal" "github.com/lightningnetwork/lnd/signal"
"github.com/lightningnetwork/lnd/walletunlocker" "github.com/lightningnetwork/lnd/walletunlocker"
"github.com/lightningnetwork/lnd/watchtower/wtdb"
) )
const ( const (
@ -313,11 +314,23 @@ func Main() error {
"is proxying over Tor as well", cfg.Tor.StreamIsolation) "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 // Set up the core server which will listen for incoming peer
// connections. // connections.
server, err := newServer( server, err := newServer(
cfg.Listeners, chanDB, activeChainControl, idPrivKey, cfg.Listeners, chanDB, towerClientDB, activeChainControl,
walletInitParams.ChansToRestore, idPrivKey, walletInitParams.ChansToRestore,
) )
if err != nil { if err != nil {
srvrLog.Errorf("unable to create server: %v\n", err) srvrLog.Errorf("unable to create server: %v\n", err)

@ -50,6 +50,9 @@ import (
"github.com/lightningnetwork/lnd/ticker" "github.com/lightningnetwork/lnd/ticker"
"github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/tor"
"github.com/lightningnetwork/lnd/walletunlocker" "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" "github.com/lightningnetwork/lnd/zpay32"
) )
@ -204,6 +207,8 @@ type server struct {
sphinx *htlcswitch.OnionProcessor sphinx *htlcswitch.OnionProcessor
towerClient wtclient.Client
connMgr *connmgr.ConnManager connMgr *connmgr.ConnManager
sigPool *lnwallet.SigPool 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 // newServer creates a new instance of the server which is to listen using the
// passed listener address. // 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, privKey *btcec.PrivateKey,
chansToRestore walletunlocker.ChannelsToRecover) (*server, error) { chansToRestore walletunlocker.ChannelsToRecover) (*server, error) {
@ -1056,6 +1062,39 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
return nil, err 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 // Create the connection manager which will be responsible for
// maintaining persistent outbound connections and also accepting new // maintaining persistent outbound connections and also accepting new
// incoming connections // incoming connections
@ -1128,6 +1167,12 @@ func (s *server) Start() error {
startErr = err startErr = err
return return
} }
if s.towerClient != nil {
if err := s.towerClient.Start(); err != nil {
startErr = err
return
}
}
if err := s.htlcSwitch.Start(); err != nil { if err := s.htlcSwitch.Start(); err != nil {
startErr = err startErr = err
return return
@ -1290,6 +1335,14 @@ func (s *server) Stop() error {
s.DisconnectPeer(peer.addr.IdentityKey) 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. // Wait for all lingering goroutines to quit.
s.wg.Wait() s.wg.Wait()

@ -29,6 +29,11 @@ const (
// DefaultStatInterval specifies the default interval between logging // DefaultStatInterval specifies the default interval between logging
// metrics about the client's operation. // metrics about the client's operation.
DefaultStatInterval = 30 * time.Second 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 // Client is the primary interface used by the daemon to control a client's