diff --git a/config.go b/config.go index 6dea524d..33e68cbf 100644 --- a/config.go +++ b/config.go @@ -33,7 +33,6 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/tor" - "github.com/lightningnetwork/lnd/watchtower" ) const ( @@ -1091,17 +1090,6 @@ func loadConfig() (*config, error) { return nil, err } - // If the user provided private watchtower addresses, parse them to - // obtain the LN addresses. - if cfg.WtClient.IsActive() { - err := cfg.WtClient.ParsePrivateTowers( - watchtower.DefaultPeerPort, cfg.net.ResolveTCPAddr, - ) - if err != nil { - return nil, err - } - } - // Finally, ensure that the user's color is correctly formatted, // otherwise the server will not be able to start after the unlocking // the wallet. diff --git a/lncfg/wtclient.go b/lncfg/wtclient.go index a4c424fd..4d8119b9 100644 --- a/lncfg/wtclient.go +++ b/lncfg/wtclient.go @@ -1,65 +1,38 @@ package lncfg -import ( - "fmt" - "strconv" - - "github.com/lightningnetwork/lnd/lnwire" -) +import "errors" // WtClient holds the configuration options for the daemon's watchtower client. type WtClient struct { + // Active determines whether a watchtower client should be created to + // back up channel states with registered watchtowers. + Active bool `long:"active" description:"Whether the daemon should use private watchtowers to back up revoked channel states."` + // PrivateTowerURIs specifies the lightning URIs of the towers the // watchtower client should send new backups to. - PrivateTowerURIs []string `long:"private-tower-uris" description:"Specifies the URIs of private watchtowers to use in backing up revoked states. URIs must be of the form @. Only 1 URI is supported at this time, if none are provided the tower will not be enabled."` - - // PrivateTowers is the list of towers parsed from the URIs provided in - // PrivateTowerURIs. - PrivateTowers []*lnwire.NetAddress + PrivateTowerURIs []string `long:"private-tower-uris" description:"(Deprecated) Specifies the URIs of private watchtowers to use in backing up revoked states. URIs must be of the form @. Only 1 URI is supported at this time, if none are provided the tower will not be enabled."` // SweepFeeRate specifies the fee rate in sat/byte to be used when // constructing justice transactions sent to the tower. SweepFeeRate uint64 `long:"sweep-fee-rate" description:"Specifies the fee rate in sat/byte to be used when constructing justice transactions sent to the watchtower."` } -// Validate asserts that at most 1 private watchtower is requested. +// Validate ensures the user has provided a valid configuration. // // NOTE: Part of the Validator interface. func (c *WtClient) Validate() error { - if len(c.PrivateTowerURIs) > 1 { - return fmt.Errorf("at most 1 private watchtower is supported, "+ - "found %d", len(c.PrivateTowerURIs)) + if len(c.PrivateTowerURIs) > 0 { + return errors.New("`wtclient.private-tower-uris` is " + + "deprecated and will be removed in the v0.8.0 " + + "release, to specify watchtowers remove " + + "`wtclient.private-tower-uris`, set " + + "`wtclient.active`, and check out `lncli wtclient -h` " + + "for more information on how to manage towers") } return nil } -// IsActive returns true if the watchtower client should be active. -func (c *WtClient) IsActive() bool { - return len(c.PrivateTowerURIs) > 0 -} - -// ParsePrivateTowers parses any private tower URIs held PrivateTowerURIs. The -// value of port should be the default port to use when a URI does not have one. -func (c *WtClient) ParsePrivateTowers(port int, resolver TCPResolver) error { - towers := make([]*lnwire.NetAddress, 0, len(c.PrivateTowerURIs)) - for _, uri := range c.PrivateTowerURIs { - addr, err := ParseLNAddressString( - uri, strconv.Itoa(port), resolver, - ) - if err != nil { - return fmt.Errorf("unable to parse private "+ - "watchtower address: %v", err) - } - - towers = append(towers, addr) - } - - c.PrivateTowers = towers - - return nil -} - // Compile-time constraint to ensure WtClient implements the Validator // interface. var _ Validator = (*WtClient)(nil) diff --git a/lnd.go b/lnd.go index 85a45dae..7a9dda12 100644 --- a/lnd.go +++ b/lnd.go @@ -340,7 +340,7 @@ func Main() error { // 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() { + if cfg.WtClient.Active { var err error towerClientDB, err = wtdb.OpenClientDB(graphDir) if err != nil { diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go index e14a2a18..53674d5a 100644 --- a/lntest/itest/lnd_test.go +++ b/lntest/itest/lnd_test.go @@ -35,6 +35,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc" + "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" @@ -7708,22 +7709,27 @@ func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness, externalIP, willyInfo.Uris[0]) } - // Construct a URI from listening port and public key, since aren't - // actually connecting remotely. - willyTowerURI := fmt.Sprintf("%x@%s", willyInfo.Pubkey, listener) - // Dave will be the breached party. We set --nolisten to ensure Carol // won't be able to connect to him and trigger the channel data // protection logic automatically. dave, err := net.NewNode("Dave", []string{ "--nolisten", - "--wtclient.private-tower-uris=" + willyTowerURI, + "--wtclient.active", }) if err != nil { t.Fatalf("unable to create new node: %v", err) } defer shutdownAndAssert(net, t, dave) + ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) + addTowerReq := &wtclientrpc.AddTowerRequest{ + Pubkey: willyInfo.Pubkey, + Address: listener, + } + if _, err := dave.WatchtowerClient.AddTower(ctxt, addTowerReq); err != nil { + t.Fatalf("unable to add willy's watchtower: %v", err) + } + // We must let Dave have an open channel before she can send a node // announcement, so we open a channel with Carol, if err := net.ConnectNodes(ctxb, dave, carol); err != nil { diff --git a/server.go b/server.go index 11f32cb8..ba9bf50d 100644 --- a/server.go +++ b/server.go @@ -1081,7 +1081,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, return nil, err } - if cfg.WtClient.IsActive() { + if cfg.WtClient.Active { policy := wtpolicy.DefaultPolicy() if cfg.WtClient.SweepFeeRate != 0 { @@ -1104,7 +1104,6 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, Dial: cfg.net.Dial, AuthDial: wtclient.AuthDial, DB: towerClientDB, - PrivateTower: cfg.WtClient.PrivateTowers[0], Policy: policy, ChainHash: *activeNetParams.GenesisHash, MinBackoff: 10 * time.Second, diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index 58666e0d..795f1798 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -141,10 +141,6 @@ type Config struct { // new sessions will be requested immediately. Policy wtpolicy.Policy - // PrivateTower is the net address of a private tower. The client will - // try to create all sessions with this tower. - PrivateTower *lnwire.NetAddress - // ChainHash identifies the chain that the client is on and for which // the tower must be watching to monitor for breaches. ChainHash chainhash.Hash @@ -266,13 +262,6 @@ func New(config *Config) (*TowerClient, error) { cfg.WriteTimeout = DefaultWriteTimeout } - // Record the tower in our database, also loading any addresses - // previously associated with its public key. - tower, err := cfg.DB.CreateTower(cfg.PrivateTower) - if err != nil { - return nil, err - } - // Next, load all candidate sessions and towers from the database into // the client. We will use any of these session if their policies match // the current policy of the client, otherwise they will be ignored and diff --git a/watchtower/wtclient/client_test.go b/watchtower/wtclient/client_test.go index 85478ae2..e9019ad9 100644 --- a/watchtower/wtclient/client_test.go +++ b/watchtower/wtclient/client_test.go @@ -26,7 +26,11 @@ import ( "github.com/lightningnetwork/lnd/watchtower/wtserver" ) -const csvDelay uint32 = 144 +const ( + csvDelay uint32 = 144 + + towerAddrStr = "18.28.243.2:9911" +) var ( revPrivBytes = []byte{ @@ -387,7 +391,6 @@ type harnessCfg struct { } func newHarness(t *testing.T, cfg harnessCfg) *testHarness { - towerAddrStr := "18.28.243.2:9911" towerTCPAddr, err := net.ResolveTCPAddr("tcp", towerAddrStr) if err != nil { t.Fatalf("Unable to resolve tower TCP addr: %v", err) @@ -412,6 +415,7 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { DB: serverDB, ReadTimeout: timeout, WriteTimeout: timeout, + NodePrivKey: privKey, NewAddress: func() (btcutil.Address, error) { return addr, nil }, @@ -435,7 +439,6 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { DB: clientDB, AuthDial: mockNet.AuthDial, SecretKeyRing: wtmock.NewSecretKeyRing(), - PrivateTower: towerAddr, Policy: cfg.policy, NewAddress: func() ([]byte, error) { return addrScript, nil @@ -458,6 +461,10 @@ func newHarness(t *testing.T, cfg harnessCfg) *testHarness { server.Stop() t.Fatalf("Unable to start wtclient: %v", err) } + if err := client.AddTower(towerAddr); err != nil { + server.Stop() + t.Fatalf("Unable to add tower to wtclient: %v", err) + } h := &testHarness{ t: t, @@ -505,7 +512,15 @@ func (h *testHarness) startServer() { func (h *testHarness) startClient() { h.t.Helper() - var err error + towerTCPAddr, err := net.ResolveTCPAddr("tcp", towerAddrStr) + if err != nil { + h.t.Fatalf("Unable to resolve tower TCP addr: %v", err) + } + towerAddr := &lnwire.NetAddress{ + IdentityKey: h.serverCfg.NodePrivKey.PubKey(), + Address: towerTCPAddr, + } + h.client, err = wtclient.New(h.clientCfg) if err != nil { h.t.Fatalf("unable to create wtclient: %v", err) @@ -513,6 +528,9 @@ func (h *testHarness) startClient() { if err := h.client.Start(); err != nil { h.t.Fatalf("unable to start wtclient: %v", err) } + if err := h.client.AddTower(towerAddr); err != nil { + h.t.Fatalf("unable to add tower to wtclient: %v", err) + } } // chanIDFromInt creates a unique channel id given a unique integral id.