From 8478b56ce6609ccc4b63f59ae09490fe69d92e76 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Mon, 20 Mar 2023 10:56:23 +0200 Subject: [PATCH] watchtower: method to dial tower and send DeleteSession This commit adds a deleteSessionFromTower method which can be used to dial the tower that we created a given session with and then sends that tower the DeleteSession method. --- watchtower/wtclient/client.go | 124 ++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index b2ce38351..464e93f16 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -902,6 +902,130 @@ func (c *TowerClient) handleClosedChannel(chanID lnwire.ChannelID, return nil } +// deleteSessionFromTower dials the tower that we created the session with and +// attempts to send the tower the DeleteSession message. +func (c *TowerClient) deleteSessionFromTower(sess *wtdb.ClientSession) error { + // First, we check if we have already loaded this tower in our + // candidate towers iterator. + tower, err := c.candidateTowers.GetTower(sess.TowerID) + if errors.Is(err, ErrTowerNotInIterator) { + // If not, then we attempt to load it from the DB. + dbTower, err := c.cfg.DB.LoadTowerByID(sess.TowerID) + if err != nil { + return err + } + + tower, err = NewTowerFromDBTower(dbTower) + if err != nil { + return err + } + } else if err != nil { + return err + } + + session, err := NewClientSessionFromDBSession( + sess, tower, c.cfg.SecretKeyRing, + ) + if err != nil { + return err + } + + localInit := wtwire.NewInitMessage( + lnwire.NewRawFeatureVector(wtwire.AltruistSessionsRequired), + c.cfg.ChainHash, + ) + + var ( + conn wtserver.Peer + + // addrIterator is a copy of the tower's address iterator. + // We use this copy so that iterating through the addresses does + // not affect any other threads using this iterator. + addrIterator = tower.Addresses.Copy() + towerAddr = addrIterator.Peek() + ) + // Attempt to dial the tower with its available addresses. + for { + conn, err = c.dial( + session.SessionKeyECDH, &lnwire.NetAddress{ + IdentityKey: tower.IdentityKey, + Address: towerAddr, + }, + ) + if err != nil { + // If there are more addrs available, immediately try + // those. + nextAddr, iteratorErr := addrIterator.Next() + if iteratorErr == nil { + towerAddr = nextAddr + continue + } + + // Otherwise, if we have exhausted the address list, + // exit. + addrIterator.Reset() + + return fmt.Errorf("failed to dial tower(%x) at any "+ + "available addresses", + tower.IdentityKey.SerializeCompressed()) + } + + break + } + defer conn.Close() + + // Send Init to tower. + err = c.sendMessage(conn, localInit) + if err != nil { + return err + } + + // Receive Init from tower. + remoteMsg, err := c.readMessage(conn) + if err != nil { + return err + } + + remoteInit, ok := remoteMsg.(*wtwire.Init) + if !ok { + return fmt.Errorf("watchtower %s responded with %T to Init", + towerAddr, remoteMsg) + } + + // Validate Init. + err = localInit.CheckRemoteInit(remoteInit, wtwire.FeatureNames) + if err != nil { + return err + } + + // Send DeleteSession to tower. + err = c.sendMessage(conn, &wtwire.DeleteSession{}) + if err != nil { + return err + } + + // Receive DeleteSessionReply from tower. + remoteMsg, err = c.readMessage(conn) + if err != nil { + return err + } + + deleteSessionReply, ok := remoteMsg.(*wtwire.DeleteSessionReply) + if !ok { + return fmt.Errorf("watchtower %s responded with %T to "+ + "DeleteSession", towerAddr, remoteMsg) + } + + switch deleteSessionReply.Code { + case wtwire.CodeOK, wtwire.DeleteSessionCodeNotFound: + return nil + default: + return fmt.Errorf("received error code %v in "+ + "DeleteSessionReply when attempting to delete "+ + "session from tower", deleteSessionReply.Code) + } +} + // backupDispatcher processes events coming from the taskPipeline and is // responsible for detecting when the client needs to renegotiate a session to // fulfill continuing demand. The event loop exits after all tasks have been