watchtower: prevent removal of last tower addr
This addresses a potential panic when a tower has one of its candidate sessions chosen, but its only reachable address was removed by a user-initiated RPC before the fact.
This commit is contained in:
parent
3ae46d81f4
commit
d3157bcaf8
@ -19,7 +19,7 @@ type TowerCandidateIterator interface {
|
|||||||
// iterator. An optional address can be provided to indicate a stale
|
// iterator. An optional address can be provided to indicate a stale
|
||||||
// tower address to remove it. If it isn't provided, then the tower is
|
// tower address to remove it. If it isn't provided, then the tower is
|
||||||
// completely removed from the iterator.
|
// completely removed from the iterator.
|
||||||
RemoveCandidate(wtdb.TowerID, net.Addr)
|
RemoveCandidate(wtdb.TowerID, net.Addr) error
|
||||||
|
|
||||||
// IsActive determines whether a given tower is exists within the
|
// IsActive determines whether a given tower is exists within the
|
||||||
// iterator.
|
// iterator.
|
||||||
@ -131,19 +131,26 @@ func (t *towerListIterator) AddCandidate(candidate *wtdb.Tower) {
|
|||||||
// optional address can be provided to indicate a stale tower address to remove
|
// optional address can be provided to indicate a stale tower address to remove
|
||||||
// it. If it isn't provided, then the tower is completely removed from the
|
// it. If it isn't provided, then the tower is completely removed from the
|
||||||
// iterator.
|
// iterator.
|
||||||
func (t *towerListIterator) RemoveCandidate(candidate wtdb.TowerID, addr net.Addr) {
|
func (t *towerListIterator) RemoveCandidate(candidate wtdb.TowerID,
|
||||||
|
addr net.Addr) error {
|
||||||
|
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
|
|
||||||
tower, ok := t.candidates[candidate]
|
tower, ok := t.candidates[candidate]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
if addr != nil {
|
if addr != nil {
|
||||||
tower.RemoveAddress(addr)
|
tower.RemoveAddress(addr)
|
||||||
|
if len(tower.Addresses) == 0 {
|
||||||
|
return wtdb.ErrLastTowerAddr
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
delete(t.candidates, candidate)
|
delete(t.candidates, candidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsActive determines whether a given tower is exists within the iterator.
|
// IsActive determines whether a given tower is exists within the iterator.
|
||||||
|
@ -1124,7 +1124,10 @@ func (c *TowerClient) handleStaleTower(msg *staleTowerMsg) error {
|
|||||||
if err := c.cfg.DB.RemoveTower(msg.pubKey, msg.addr); err != nil {
|
if err := c.cfg.DB.RemoveTower(msg.pubKey, msg.addr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.candidateTowers.RemoveCandidate(tower.ID, msg.addr)
|
err = c.candidateTowers.RemoveCandidate(tower.ID, msg.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// If an address was provided, then we're only meant to remove the
|
// If an address was provided, then we're only meant to remove the
|
||||||
// address from the tower, so there's nothing left for us to do.
|
// address from the tower, so there's nothing left for us to do.
|
||||||
|
@ -108,6 +108,10 @@ var (
|
|||||||
// created because session key index differs from the reserved key
|
// created because session key index differs from the reserved key
|
||||||
// index.
|
// index.
|
||||||
ErrIncorrectKeyIndex = errors.New("incorrect key index")
|
ErrIncorrectKeyIndex = errors.New("incorrect key index")
|
||||||
|
|
||||||
|
// ErrLastTowerAddr is an error returned when the last address of a
|
||||||
|
// watchtower is attempted to be removed.
|
||||||
|
ErrLastTowerAddr = errors.New("cannot remove last tower address")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientDB is single database providing a persistent storage engine for the
|
// ClientDB is single database providing a persistent storage engine for the
|
||||||
@ -333,7 +337,13 @@ func (c *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Towers should always have at least one address saved.
|
||||||
tower.RemoveAddress(addr)
|
tower.RemoveAddress(addr)
|
||||||
|
if len(tower.Addresses) == 0 {
|
||||||
|
return ErrLastTowerAddr
|
||||||
|
}
|
||||||
|
|
||||||
return putTower(towers, tower)
|
return putTower(towers, tower)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ func testRemoveTower(h *clientDBHarness) {
|
|||||||
|
|
||||||
// We'll then remove the first address. We should now see that the tower
|
// We'll then remove the first address. We should now see that the tower
|
||||||
// has no addresses left.
|
// has no addresses left.
|
||||||
h.removeTower(pk, addr1, false, nil)
|
h.removeTower(pk, addr1, false, wtdb.ErrLastTowerAddr)
|
||||||
|
|
||||||
// Removing the tower as a whole from the database should succeed since
|
// Removing the tower as a whole from the database should succeed since
|
||||||
// there aren't any active sessions for it.
|
// there aren't any active sessions for it.
|
||||||
|
@ -101,6 +101,9 @@ func (m *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error {
|
|||||||
|
|
||||||
if addr != nil {
|
if addr != nil {
|
||||||
tower.RemoveAddress(addr)
|
tower.RemoveAddress(addr)
|
||||||
|
if len(tower.Addresses) == 0 {
|
||||||
|
return wtdb.ErrLastTowerAddr
|
||||||
|
}
|
||||||
m.towers[tower.ID] = tower
|
m.towers[tower.ID] = tower
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user