From 40ac82e439f2676dd4d7035c9fe38c1141908d6d Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 18 Oct 2022 12:08:46 +0200 Subject: [PATCH 1/4] watchtower: add ClientSessionFilterFn to session requests In this commit, a new ClientSessionFilterFn parameter is added to the DB's ListClientSession method which can be used to allow the caller to specify a filter function for filtering sessions read from the DB. Currently all filtering of sessions are done after the sessions have been read from the DB, so adding this option should provide some efficiency. --- watchtower/wtclient/client.go | 12 ++++---- watchtower/wtclient/interface.go | 3 +- watchtower/wtdb/client_db.go | 49 +++++++++++++++++++++++-------- watchtower/wtdb/client_db_test.go | 13 ++++---- watchtower/wtmock/client_db.go | 13 ++++++-- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index 685170eab..8cd2b7e7d 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -418,7 +418,7 @@ func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing, return nil, err } - sessions, err := db.ListClientSessions(&tower.ID, opts...) + sessions, err := db.ListClientSessions(&tower.ID, nil, opts...) if err != nil { return nil, err } @@ -470,7 +470,7 @@ func getClientSessions(db DB, keyRing ECDHKeyRing, forTower *wtdb.TowerID, opts ...wtdb.ClientSessionListOption) ( map[wtdb.SessionID]*ClientSession, error) { - dbSessions, err := db.ListClientSessions(forTower, opts...) + dbSessions, err := db.ListClientSessions(forTower, nil, opts...) if err != nil { return nil, err } @@ -1289,7 +1289,7 @@ func (c *TowerClient) handleStaleTower(msg *staleTowerMsg) error { // Otherwise, the tower should no longer be used for future session // negotiations and backups. pubKey := msg.pubKey.SerializeCompressed() - sessions, err := c.cfg.DB.ListClientSessions(&dbTower.ID) + sessions, err := c.cfg.DB.ListClientSessions(&dbTower.ID, nil) if err != nil { return fmt.Errorf("unable to retrieve sessions for tower %x: "+ "%v", pubKey, err) @@ -1320,7 +1320,7 @@ func (c *TowerClient) RegisteredTowers(opts ...wtdb.ClientSessionListOption) ( if err != nil { return nil, err } - clientSessions, err := c.cfg.DB.ListClientSessions(nil, opts...) + clientSessions, err := c.cfg.DB.ListClientSessions(nil, nil, opts...) if err != nil { return nil, err } @@ -1361,7 +1361,9 @@ func (c *TowerClient) LookupTower(pubKey *btcec.PublicKey, return nil, err } - towerSessions, err := c.cfg.DB.ListClientSessions(&tower.ID, opts...) + towerSessions, err := c.cfg.DB.ListClientSessions( + &tower.ID, nil, opts..., + ) if err != nil { return nil, err } diff --git a/watchtower/wtclient/interface.go b/watchtower/wtclient/interface.go index 63d718b60..9751988ba 100644 --- a/watchtower/wtclient/interface.go +++ b/watchtower/wtclient/interface.go @@ -60,7 +60,8 @@ type DB interface { // ListClientSessions returns the set of all client sessions known to // the db. An optional tower ID can be used to filter out any client // sessions in the response that do not correspond to this tower. - ListClientSessions(*wtdb.TowerID, ...wtdb.ClientSessionListOption) ( + ListClientSessions(*wtdb.TowerID, wtdb.ClientSessionFilterFn, + ...wtdb.ClientSessionListOption) ( map[wtdb.SessionID]*wtdb.ClientSession, error) // FetchSessionCommittedUpdates retrieves the current set of un-acked diff --git a/watchtower/wtdb/client_db.go b/watchtower/wtdb/client_db.go index f8eda37bf..3cbe0c6f1 100644 --- a/watchtower/wtdb/client_db.go +++ b/watchtower/wtdb/client_db.go @@ -138,6 +138,10 @@ var ( // range-index found for the given session ID to channel ID pair. ErrNoRangeIndexFound = errors.New("no range index found for the " + "given session-channel pair") + + // ErrSessionFailedFilterFn indicates that a particular session did + // not pass the filter func provided by the caller. + ErrSessionFailedFilterFn = errors.New("session failed filter func") ) // NewBoltBackendCreator returns a function that creates a new bbolt backend for @@ -469,7 +473,7 @@ func (c *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error { towerSessions, err := c.listTowerSessions( towerID, sessions, chanIDIndexBkt, - towersToSessionsIndex, + towersToSessionsIndex, nil, WithPerCommittedUpdate(perCommittedUpdate), ) if err != nil { @@ -960,7 +964,8 @@ func getSessionKeyIndex(keyIndexes kvdb.RwBucket, towerID TowerID, // optional tower ID can be used to filter out any client sessions in the // response that do not correspond to this tower. func (c *ClientDB) ListClientSessions(id *TowerID, - opts ...ClientSessionListOption) (map[SessionID]*ClientSession, error) { + filterFn ClientSessionFilterFn, opts ...ClientSessionListOption) ( + map[SessionID]*ClientSession, error) { var clientSessions map[SessionID]*ClientSession err := kvdb.View(c.db, func(tx kvdb.RTx) error { @@ -985,7 +990,7 @@ func (c *ClientDB) ListClientSessions(id *TowerID, // known to the db. if id == nil { clientSessions, err = c.listClientAllSessions( - sessions, chanIDIndexBkt, opts..., + sessions, chanIDIndexBkt, filterFn, opts..., ) return err } @@ -998,7 +1003,7 @@ func (c *ClientDB) ListClientSessions(id *TowerID, clientSessions, err = c.listTowerSessions( *id, sessions, chanIDIndexBkt, towerToSessionIndex, - opts..., + filterFn, opts..., ) return err }, func() { @@ -1013,7 +1018,8 @@ func (c *ClientDB) ListClientSessions(id *TowerID, // listClientAllSessions returns the set of all client sessions known to the db. func (c *ClientDB) listClientAllSessions(sessions, chanIDIndexBkt kvdb.RBucket, - opts ...ClientSessionListOption) (map[SessionID]*ClientSession, error) { + filterFn ClientSessionFilterFn, opts ...ClientSessionListOption) ( + map[SessionID]*ClientSession, error) { clientSessions := make(map[SessionID]*ClientSession) err := sessions.ForEach(func(k, _ []byte) error { @@ -1022,9 +1028,11 @@ func (c *ClientDB) listClientAllSessions(sessions, chanIDIndexBkt kvdb.RBucket, // committed updates and compute the highest known commit height // for each channel. session, err := c.getClientSession( - sessions, chanIDIndexBkt, k, opts..., + sessions, chanIDIndexBkt, k, filterFn, opts..., ) - if err != nil { + if errors.Is(err, ErrSessionFailedFilterFn) { + return nil + } else if err != nil { return err } @@ -1042,8 +1050,8 @@ func (c *ClientDB) listClientAllSessions(sessions, chanIDIndexBkt kvdb.RBucket, // listTowerSessions returns the set of all client sessions known to the db // that are associated with the given tower id. func (c *ClientDB) listTowerSessions(id TowerID, sessionsBkt, chanIDIndexBkt, - towerToSessionIndex kvdb.RBucket, opts ...ClientSessionListOption) ( - map[SessionID]*ClientSession, error) { + towerToSessionIndex kvdb.RBucket, filterFn ClientSessionFilterFn, + opts ...ClientSessionListOption) (map[SessionID]*ClientSession, error) { towerIndexBkt := towerToSessionIndex.NestedReadBucket(id.Bytes()) if towerIndexBkt == nil { @@ -1057,9 +1065,11 @@ func (c *ClientDB) listTowerSessions(id TowerID, sessionsBkt, chanIDIndexBkt, // committed updates and compute the highest known commit height // for each channel. session, err := c.getClientSession( - sessionsBkt, chanIDIndexBkt, k, opts..., + sessionsBkt, chanIDIndexBkt, k, filterFn, opts..., ) - if err != nil { + if errors.Is(err, ErrSessionFailedFilterFn) { + return nil + } else if err != nil { return err } @@ -1523,6 +1533,11 @@ func getClientSessionBody(sessions kvdb.RBucket, return &session, nil } +// ClientSessionFilterFn describes the signature of a callback function that can +// be used to filter the sessions that are returned in any of the DB methods +// that read sessions from the DB. +type ClientSessionFilterFn func(*ClientSession) bool + // PerMaxHeightCB describes the signature of a callback function that can be // called for each channel that a session has updates for to communicate the // maximum commitment height that the session has backed up for the channel. @@ -1533,6 +1548,10 @@ type PerMaxHeightCB func(*ClientSession, lnwire.ChannelID, uint64) // number of updates that the session has for the channel. type PerNumAckedUpdatesCB func(*ClientSession, lnwire.ChannelID, uint16) +// PerAckedUpdateCB describes the signature of a callback function that can be +// called for each of a session's acked updates. +type PerAckedUpdateCB func(*ClientSession, uint16, BackupID) + // PerCommittedUpdateCB describes the signature of a callback function that can // be called for each of a session's committed updates (updates that the client // has not yet received an ACK for). @@ -1597,8 +1616,8 @@ func WithPerCommittedUpdate(cb PerCommittedUpdateCB) ClientSessionListOption { // session id. This method populates the CommittedUpdates, AckUpdates and Tower // in addition to the ClientSession's body. func (c *ClientDB) getClientSession(sessionsBkt, chanIDIndexBkt kvdb.RBucket, - idBytes []byte, opts ...ClientSessionListOption) (*ClientSession, - error) { + idBytes []byte, filterFn ClientSessionFilterFn, + opts ...ClientSessionListOption) (*ClientSession, error) { cfg := NewClientSessionCfg() for _, o := range opts { @@ -1610,6 +1629,10 @@ func (c *ClientDB) getClientSession(sessionsBkt, chanIDIndexBkt kvdb.RBucket, return nil, err } + if filterFn != nil && !filterFn(session) { + return nil, ErrSessionFailedFilterFn + } + // Can't fail because client session body has already been read. sessionBkt := sessionsBkt.NestedReadBucket(idBytes) diff --git a/watchtower/wtdb/client_db_test.go b/watchtower/wtdb/client_db_test.go index 2cafc160b..cd77ec77e 100644 --- a/watchtower/wtdb/client_db_test.go +++ b/watchtower/wtdb/client_db_test.go @@ -49,11 +49,12 @@ func (h *clientDBHarness) insertSession(session *wtdb.ClientSession, } func (h *clientDBHarness) listSessions(id *wtdb.TowerID, + filterFn wtdb.ClientSessionFilterFn, opts ...wtdb.ClientSessionListOption) map[wtdb.SessionID]*wtdb.ClientSession { h.t.Helper() - sessions, err := h.db.ListClientSessions(id, opts...) + sessions, err := h.db.ListClientSessions(id, filterFn, opts...) require.NoError(h.t, err, "unable to list client sessions") return sessions @@ -80,7 +81,7 @@ func (h *clientDBHarness) createTower(lnAddr *lnwire.NetAddress, require.ErrorIs(h.t, err, expErr) require.NotZero(h.t, tower.ID, "tower id should never be 0") - for _, session := range h.listSessions(&tower.ID) { + for _, session := range h.listSessions(&tower.ID, nil) { require.Equal(h.t, wtdb.CSessionActive, session.Status) } @@ -123,7 +124,7 @@ func (h *clientDBHarness) removeTower(pubKey *btcec.PublicKey, addr net.Addr, return } - for _, session := range h.listSessions(&tower.ID) { + for _, session := range h.listSessions(&tower.ID, nil) { require.Equal(h.t, wtdb.CSessionInactive, session.Status, "expected status for session "+ "%v to be %v, got %v", session.ID, @@ -268,7 +269,7 @@ func testCreateClientSession(h *clientDBHarness) { // First, assert that this session is not already present in the // database. - _, ok := h.listSessions(nil)[session.ID] + _, ok := h.listSessions(nil, nil)[session.ID] require.Falsef(h.t, ok, "session for id %x should not exist yet", session.ID) @@ -296,7 +297,7 @@ func testCreateClientSession(h *clientDBHarness) { h.insertSession(session, nil) // Verify that the session now exists in the database. - _, ok = h.listSessions(nil)[session.ID] + _, ok = h.listSessions(nil, nil)[session.ID] require.Truef(h.t, ok, "session for id %x should exist now", session.ID) // Attempt to insert the session again, which should fail due to the @@ -344,7 +345,7 @@ func testFilterClientSessions(h *clientDBHarness) { // We should see the expected sessions for each tower when filtering // them. for towerID, expectedSessions := range towerSessions { - sessions := h.listSessions(&towerID) + sessions := h.listSessions(&towerID, nil) require.Len(h.t, sessions, len(expectedSessions)) for _, expectedSession := range expectedSessions { diff --git a/watchtower/wtmock/client_db.go b/watchtower/wtmock/client_db.go index 095e8cbac..071ee7782 100644 --- a/watchtower/wtmock/client_db.go +++ b/watchtower/wtmock/client_db.go @@ -83,7 +83,7 @@ func (m *ClientDB) CreateTower(lnAddr *lnwire.NetAddress) (*wtdb.Tower, error) { tower = m.towers[towerID] tower.AddAddress(lnAddr.Address) - towerSessions, err := m.listClientSessions(&towerID) + towerSessions, err := m.listClientSessions(&towerID, nil) if err != nil { return nil, err } @@ -135,7 +135,7 @@ func (m *ClientDB) RemoveTower(pubKey *btcec.PublicKey, addr net.Addr) error { return nil } - towerSessions, err := m.listClientSessions(&tower.ID) + towerSessions, err := m.listClientSessions(&tower.ID, nil) if err != nil { return err } @@ -220,18 +220,20 @@ func (m *ClientDB) MarkBackupIneligible(_ lnwire.ChannelID, _ uint64) error { // optional tower ID can be used to filter out any client sessions in the // response that do not correspond to this tower. func (m *ClientDB) ListClientSessions(tower *wtdb.TowerID, + filterFn wtdb.ClientSessionFilterFn, opts ...wtdb.ClientSessionListOption) ( map[wtdb.SessionID]*wtdb.ClientSession, error) { m.mu.Lock() defer m.mu.Unlock() - return m.listClientSessions(tower, opts...) + return m.listClientSessions(tower, filterFn, opts...) } // listClientSessions returns the set of all client sessions known to the db. An // optional tower ID can be used to filter out any client sessions in the // response that do not correspond to this tower. func (m *ClientDB) listClientSessions(tower *wtdb.TowerID, + filterFn wtdb.ClientSessionFilterFn, opts ...wtdb.ClientSessionListOption) ( map[wtdb.SessionID]*wtdb.ClientSession, error) { @@ -246,6 +248,11 @@ func (m *ClientDB) listClientSessions(tower *wtdb.TowerID, if tower != nil && *tower != session.TowerID { continue } + + if filterFn != nil && !filterFn(&session) { + continue + } + sessions[session.ID] = &session if cfg.PerMaxHeight != nil { From c1be4206968fdcb2d9272e468dea9a723f84238f Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 18 Oct 2022 12:21:42 +0200 Subject: [PATCH 2/4] watchtower: make use of the new ClientSessionFilterFn option In this commit, the wtclient starts making use of the new ClientSessionFilterFn to do any session filtering instead of doing it in memory. --- watchtower/wtclient/client.go | 82 ++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index 8cd2b7e7d..32622b7da 100644 --- a/watchtower/wtclient/client.go +++ b/watchtower/wtclient/client.go @@ -42,12 +42,24 @@ const ( DefaultForceQuitDelay = 10 * time.Second ) -// genActiveSessionFilter generates a filter that selects active sessions that -// also match the desired channel type, either legacy or anchor. -func genActiveSessionFilter(anchor bool) func(*ClientSession) bool { - return func(s *ClientSession) bool { - return s.Status == wtdb.CSessionActive && - anchor == s.Policy.IsAnchorChannel() +// genSessionFilter constructs a filter that can be used to select sessions only +// if they match the policy of the client (namely anchor vs legacy). If +// activeOnly is set, then only active sessions will be returned. +func (c *TowerClient) genSessionFilter( + activeOnly bool) wtdb.ClientSessionFilterFn { + + return func(session *wtdb.ClientSession) bool { + if c.cfg.Policy.IsAnchorChannel() != + session.Policy.IsAnchorChannel() { + + return false + } + + if !activeOnly { + return true + } + + return session.Status == wtdb.CSessionActive } } @@ -344,13 +356,6 @@ func New(config *Config) (*TowerClient, error) { perUpdate(s.Policy, u.BackupID.ChanID, u.BackupID.CommitHeight) } - // Load all candidate sessions and towers from the database into the - // client. We will use any of these sessions if their policies match the - // current policy of the client, otherwise they will be ignored and new - // sessions will be requested. - isAnchorClient := cfg.Policy.IsAnchorChannel() - activeSessionFilter := genActiveSessionFilter(isAnchorClient) - candidateTowers := newTowerListIterator() perActiveTower := func(tower *Tower) { // If the tower has already been marked as active, then there is @@ -366,9 +371,13 @@ func New(config *Config) (*TowerClient, error) { candidateTowers.AddCandidate(tower) } + // Load all candidate sessions and towers from the database into the + // client. We will use any of these sessions if their policies match the + // current policy of the client, otherwise they will be ignored and new + // sessions will be requested. candidateSessions, err := getTowerAndSessionCandidates( - cfg.DB, cfg.SecretKeyRing, activeSessionFilter, perActiveTower, - wtdb.WithPerMaxHeight(perMaxHeight), + cfg.DB, cfg.SecretKeyRing, c.genSessionFilter(true), + perActiveTower, wtdb.WithPerMaxHeight(perMaxHeight), wtdb.WithPerCommittedUpdate(perCommittedUpdate), ) if err != nil { @@ -401,7 +410,7 @@ func New(config *Config) (*TowerClient, error) { // sessionFilter check then the perActiveTower call-back will be called on that // tower. func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing, - sessionFilter func(*ClientSession) bool, + sessionFilter wtdb.ClientSessionFilterFn, perActiveTower func(tower *Tower), opts ...wtdb.ClientSessionListOption) ( map[wtdb.SessionID]*ClientSession, error) { @@ -418,7 +427,9 @@ func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing, return nil, err } - sessions, err := db.ListClientSessions(&tower.ID, nil, opts...) + sessions, err := db.ListClientSessions( + &tower.ID, sessionFilter, opts..., + ) if err != nil { return nil, err } @@ -438,19 +449,14 @@ func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing, towerKeyDesc, keyRing, ) - cs := &ClientSession{ + // Add the session to the set of candidate sessions. + candidateSessions[s.ID] = &ClientSession{ ID: s.ID, ClientSessionBody: s.ClientSessionBody, Tower: tower, SessionKeyECDH: sessionKeyECDH, } - if !sessionFilter(cs) { - continue - } - - // Add the session to the set of candidate sessions. - candidateSessions[s.ID] = cs perActiveTower(tower) } } @@ -466,11 +472,13 @@ func getTowerAndSessionCandidates(db DB, keyRing ECDHKeyRing, // ClientSession's SessionPrivKey field is desired, otherwise, the existing // ListClientSessions method should be used. func getClientSessions(db DB, keyRing ECDHKeyRing, forTower *wtdb.TowerID, - passesFilter func(*ClientSession) bool, + sessionFilter wtdb.ClientSessionFilterFn, opts ...wtdb.ClientSessionListOption) ( map[wtdb.SessionID]*ClientSession, error) { - dbSessions, err := db.ListClientSessions(forTower, nil, opts...) + dbSessions, err := db.ListClientSessions( + forTower, sessionFilter, opts..., + ) if err != nil { return nil, err } @@ -494,6 +502,7 @@ func getClientSessions(db DB, keyRing ECDHKeyRing, forTower *wtdb.TowerID, if err != nil { return nil, err } + sessionKeyECDH := keychain.NewPubKeyECDH(towerKeyDesc, keyRing) tower, err := NewTowerFromDBTower(dbTower) @@ -501,20 +510,12 @@ func getClientSessions(db DB, keyRing ECDHKeyRing, forTower *wtdb.TowerID, return nil, err } - cs := &ClientSession{ + sessions[s.ID] = &ClientSession{ ID: s.ID, ClientSessionBody: s.ClientSessionBody, Tower: tower, SessionKeyECDH: sessionKeyECDH, } - - // If an optional filter was provided, use it to filter out any - // undesired sessions. - if passesFilter != nil && !passesFilter(cs) { - continue - } - - sessions[s.ID] = cs } return sessions, nil @@ -1200,10 +1201,9 @@ func (c *TowerClient) handleNewTower(msg *newTowerMsg) error { c.candidateTowers.AddCandidate(tower) // Include all of its corresponding sessions to our set of candidates. - isAnchorClient := c.cfg.Policy.IsAnchorChannel() - activeSessionFilter := genActiveSessionFilter(isAnchorClient) sessions, err := getClientSessions( - c.cfg.DB, c.cfg.SecretKeyRing, &tower.ID, activeSessionFilter, + c.cfg.DB, c.cfg.SecretKeyRing, &tower.ID, + c.genSessionFilter(true), ) if err != nil { return fmt.Errorf("unable to determine sessions for tower %x: "+ @@ -1320,7 +1320,9 @@ func (c *TowerClient) RegisteredTowers(opts ...wtdb.ClientSessionListOption) ( if err != nil { return nil, err } - clientSessions, err := c.cfg.DB.ListClientSessions(nil, nil, opts...) + clientSessions, err := c.cfg.DB.ListClientSessions( + nil, c.genSessionFilter(false), opts..., + ) if err != nil { return nil, err } @@ -1362,7 +1364,7 @@ func (c *TowerClient) LookupTower(pubKey *btcec.PublicKey, } towerSessions, err := c.cfg.DB.ListClientSessions( - &tower.ID, nil, opts..., + &tower.ID, c.genSessionFilter(false), opts..., ) if err != nil { return nil, err From b635629e3c4bc5cdc55477a15ffa80e803f30138 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Tue, 18 Oct 2022 12:25:22 +0200 Subject: [PATCH 3/4] lnrpc/wtclientrpc: correctly merge tower client info Prior to this commit, the wtclient would request tower info from both the legacy client and the anchors client and would then merge the results returned. This is incorrect since the two clients will have different session info and different "active" statuses for the same tower. This commit thus ensures that we dont lose this info. --- lnrpc/wtclientrpc/wtclient.go | 119 +++++-- lnrpc/wtclientrpc/wtclient.pb.go | 402 ++++++++++++++++-------- lnrpc/wtclientrpc/wtclient.proto | 29 +- lnrpc/wtclientrpc/wtclient.swagger.json | 38 ++- 4 files changed, 421 insertions(+), 167 deletions(-) diff --git a/lnrpc/wtclientrpc/wtclient.go b/lnrpc/wtclientrpc/wtclient.go index 6672c0c75..415ae1702 100644 --- a/lnrpc/wtclientrpc/wtclient.go +++ b/lnrpc/wtclientrpc/wtclient.go @@ -1,10 +1,13 @@ package wtclientrpc import ( + "bytes" "context" + "encoding/binary" "errors" "fmt" "net" + "sort" "strconv" "github.com/btcsuite/btcd/btcec/v2" @@ -274,30 +277,46 @@ func (c *WatchtowerClient) ListTowers(ctx context.Context, return nil, err } + // Collect all the anchor client towers. + rpcTowers := make(map[wtdb.TowerID]*Tower) + for _, tower := range anchorTowers { + rpcTower := marshallTower( + tower, PolicyType_ANCHOR, req.IncludeSessions, + ackCounts, committedUpdateCounts, + ) + + rpcTowers[tower.ID] = rpcTower + } + legacyTowers, err := c.cfg.Client.RegisteredTowers(opts...) if err != nil { return nil, err } - // Filter duplicates. - towers := make(map[wtdb.TowerID]*wtclient.RegisteredTower) - for _, tower := range anchorTowers { - towers[tower.Tower.ID] = tower - } + // Collect all the legacy client towers. If it has any of the same + // towers that the anchors client has, then just add the session info + // for the legacy client to the existing tower. for _, tower := range legacyTowers { - towers[tower.Tower.ID] = tower - } - - rpcTowers := make([]*Tower, 0, len(towers)) - for _, tower := range towers { rpcTower := marshallTower( - tower, req.IncludeSessions, ackCounts, - committedUpdateCounts, + tower, PolicyType_LEGACY, req.IncludeSessions, + ackCounts, committedUpdateCounts, ) - rpcTowers = append(rpcTowers, rpcTower) + + t, ok := rpcTowers[tower.ID] + if !ok { + rpcTowers[tower.ID] = rpcTower + continue + } + + t.SessionInfo = append(t.SessionInfo, rpcTower.SessionInfo...) } - return &ListTowersResponse{Towers: rpcTowers}, nil + towers := make([]*Tower, 0, len(rpcTowers)) + for _, tower := range rpcTowers { + towers = append(towers, tower) + } + + return &ListTowersResponse{Towers: towers}, nil } // GetTowerInfo retrieves information for a registered watchtower. @@ -317,18 +336,37 @@ func (c *WatchtowerClient) GetTowerInfo(ctx context.Context, req.IncludeSessions, ) - var tower *wtclient.RegisteredTower - tower, err = c.cfg.Client.LookupTower(pubKey, opts...) - if err == wtdb.ErrTowerNotFound { - tower, err = c.cfg.AnchorClient.LookupTower(pubKey, opts...) + // Get the tower and its sessions from anchors client. + tower, err := c.cfg.AnchorClient.LookupTower(pubKey, opts...) + if err != nil { + return nil, err } + rpcTower := marshallTower( + tower, PolicyType_ANCHOR, req.IncludeSessions, ackCounts, + committedUpdateCounts, + ) + + // Get the tower and its sessions from legacy client. + tower, err = c.cfg.Client.LookupTower(pubKey, opts...) if err != nil { return nil, err } - return marshallTower( - tower, req.IncludeSessions, ackCounts, committedUpdateCounts, - ), nil + rpcLegacyTower := marshallTower( + tower, PolicyType_LEGACY, req.IncludeSessions, ackCounts, + committedUpdateCounts, + ) + + if !bytes.Equal(rpcTower.Pubkey, rpcLegacyTower.Pubkey) { + return nil, fmt.Errorf("legacy and anchor clients returned " + + "inconsistent results for the given tower") + } + + rpcTower.SessionInfo = append( + rpcTower.SessionInfo, rpcLegacyTower.SessionInfo..., + ) + + return rpcTower, nil } // constructFunctionalOptions is a helper function that constructs a list of @@ -437,8 +475,8 @@ func (c *WatchtowerClient) Policy(ctx context.Context, // marshallTower converts a client registered watchtower into its corresponding // RPC type. -func marshallTower(tower *wtclient.RegisteredTower, includeSessions bool, - ackCounts map[wtdb.SessionID]uint16, +func marshallTower(tower *wtclient.RegisteredTower, policyType PolicyType, + includeSessions bool, ackCounts map[wtdb.SessionID]uint16, pendingCounts map[wtdb.SessionID]uint16) *Tower { rpcAddrs := make([]string, 0, len(tower.Addresses)) @@ -448,8 +486,24 @@ func marshallTower(tower *wtclient.RegisteredTower, includeSessions bool, var rpcSessions []*TowerSession if includeSessions { - rpcSessions = make([]*TowerSession, 0, len(tower.Sessions)) + // To ensure that the output order is deterministic for a given + // set of sessions, we put the sessions into a slice and order + // them based on session ID. + sessions := make([]*wtdb.ClientSession, 0, len(tower.Sessions)) for _, session := range tower.Sessions { + sessions = append(sessions, session) + } + + sort.Slice(sessions, func(i, j int) bool { + id1 := sessions[i].ID + id2 := sessions[j].ID + + return binary.BigEndian.Uint64(id1[:]) < + binary.BigEndian.Uint64(id2[:]) + }) + + rpcSessions = make([]*TowerSession, 0, len(tower.Sessions)) + for _, session := range sessions { satPerVByte := session.Policy.SweepFeeRate.FeePerKVByte() / 1000 rpcSessions = append(rpcSessions, &TowerSession{ NumBackups: uint32(ackCounts[session.ID]), @@ -463,11 +517,22 @@ func marshallTower(tower *wtclient.RegisteredTower, includeSessions bool, } } - return &Tower{ - Pubkey: tower.IdentityKey.SerializeCompressed(), - Addresses: rpcAddrs, + rpcTower := &Tower{ + Pubkey: tower.IdentityKey.SerializeCompressed(), + Addresses: rpcAddrs, + SessionInfo: []*TowerSessionInfo{{ + PolicyType: policyType, + ActiveSessionCandidate: tower.ActiveSessionCandidate, + NumSessions: uint32(len(tower.Sessions)), + Sessions: rpcSessions, + }}, + // The below fields are populated for backwards compatibility + // but will be removed in a future commit when the proto fields + // are removed. ActiveSessionCandidate: tower.ActiveSessionCandidate, NumSessions: uint32(len(tower.Sessions)), Sessions: rpcSessions, } + + return rpcTower } diff --git a/lnrpc/wtclientrpc/wtclient.pb.go b/lnrpc/wtclientrpc/wtclient.pb.go index d614a9f39..10f4559ee 100644 --- a/lnrpc/wtclientrpc/wtclient.pb.go +++ b/lnrpc/wtclientrpc/wtclient.pb.go @@ -418,12 +418,26 @@ type Tower struct { Pubkey []byte `protobuf:"bytes,1,opt,name=pubkey,proto3" json:"pubkey,omitempty"` // The list of addresses the watchtower is reachable over. Addresses []string `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` + // Deprecated, use the active_session_candidate field under the + // correct identifier in the client_type map. // Whether the watchtower is currently a candidate for new sessions. + // + // Deprecated: Do not use. ActiveSessionCandidate bool `protobuf:"varint,3,opt,name=active_session_candidate,json=activeSessionCandidate,proto3" json:"active_session_candidate,omitempty"` + // Deprecated, use the num_sessions field under the correct identifier + // in the client_type map. // The number of sessions that have been negotiated with the watchtower. + // + // Deprecated: Do not use. NumSessions uint32 `protobuf:"varint,4,opt,name=num_sessions,json=numSessions,proto3" json:"num_sessions,omitempty"` + // Deprecated, use the sessions field under the correct identifier in the + // client_type map. // The list of sessions that have been negotiated with the watchtower. + // + // Deprecated: Do not use. Sessions []*TowerSession `protobuf:"bytes,5,rep,name=sessions,proto3" json:"sessions,omitempty"` + // A list sessions held with the tower. + SessionInfo []*TowerSessionInfo `protobuf:"bytes,6,rep,name=session_info,json=sessionInfo,proto3" json:"session_info,omitempty"` } func (x *Tower) Reset() { @@ -472,6 +486,7 @@ func (x *Tower) GetAddresses() []string { return nil } +// Deprecated: Do not use. func (x *Tower) GetActiveSessionCandidate() bool { if x != nil { return x.ActiveSessionCandidate @@ -479,6 +494,7 @@ func (x *Tower) GetActiveSessionCandidate() bool { return false } +// Deprecated: Do not use. func (x *Tower) GetNumSessions() uint32 { if x != nil { return x.NumSessions @@ -486,6 +502,7 @@ func (x *Tower) GetNumSessions() uint32 { return 0 } +// Deprecated: Do not use. func (x *Tower) GetSessions() []*TowerSession { if x != nil { return x.Sessions @@ -493,6 +510,88 @@ func (x *Tower) GetSessions() []*TowerSession { return nil } +func (x *Tower) GetSessionInfo() []*TowerSessionInfo { + if x != nil { + return x.SessionInfo + } + return nil +} + +type TowerSessionInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Whether the watchtower is currently a candidate for new sessions. + ActiveSessionCandidate bool `protobuf:"varint,1,opt,name=active_session_candidate,json=activeSessionCandidate,proto3" json:"active_session_candidate,omitempty"` + // The number of sessions that have been negotiated with the watchtower. + NumSessions uint32 `protobuf:"varint,2,opt,name=num_sessions,json=numSessions,proto3" json:"num_sessions,omitempty"` + // The list of sessions that have been negotiated with the watchtower. + Sessions []*TowerSession `protobuf:"bytes,3,rep,name=sessions,proto3" json:"sessions,omitempty"` + // The session's policy type. + PolicyType PolicyType `protobuf:"varint,4,opt,name=policy_type,json=policyType,proto3,enum=wtclientrpc.PolicyType" json:"policy_type,omitempty"` +} + +func (x *TowerSessionInfo) Reset() { + *x = TowerSessionInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_wtclientrpc_wtclient_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TowerSessionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TowerSessionInfo) ProtoMessage() {} + +func (x *TowerSessionInfo) ProtoReflect() protoreflect.Message { + mi := &file_wtclientrpc_wtclient_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TowerSessionInfo.ProtoReflect.Descriptor instead. +func (*TowerSessionInfo) Descriptor() ([]byte, []int) { + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{7} +} + +func (x *TowerSessionInfo) GetActiveSessionCandidate() bool { + if x != nil { + return x.ActiveSessionCandidate + } + return false +} + +func (x *TowerSessionInfo) GetNumSessions() uint32 { + if x != nil { + return x.NumSessions + } + return 0 +} + +func (x *TowerSessionInfo) GetSessions() []*TowerSession { + if x != nil { + return x.Sessions + } + return nil +} + +func (x *TowerSessionInfo) GetPolicyType() PolicyType { + if x != nil { + return x.PolicyType + } + return PolicyType_LEGACY +} + type ListTowersRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -505,7 +604,7 @@ type ListTowersRequest struct { func (x *ListTowersRequest) Reset() { *x = ListTowersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[7] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -518,7 +617,7 @@ func (x *ListTowersRequest) String() string { func (*ListTowersRequest) ProtoMessage() {} func (x *ListTowersRequest) ProtoReflect() protoreflect.Message { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[7] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -531,7 +630,7 @@ func (x *ListTowersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTowersRequest.ProtoReflect.Descriptor instead. func (*ListTowersRequest) Descriptor() ([]byte, []int) { - return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{7} + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{8} } func (x *ListTowersRequest) GetIncludeSessions() bool { @@ -553,7 +652,7 @@ type ListTowersResponse struct { func (x *ListTowersResponse) Reset() { *x = ListTowersResponse{} if protoimpl.UnsafeEnabled { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[8] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -566,7 +665,7 @@ func (x *ListTowersResponse) String() string { func (*ListTowersResponse) ProtoMessage() {} func (x *ListTowersResponse) ProtoReflect() protoreflect.Message { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[8] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -579,7 +678,7 @@ func (x *ListTowersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTowersResponse.ProtoReflect.Descriptor instead. func (*ListTowersResponse) Descriptor() ([]byte, []int) { - return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{8} + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{9} } func (x *ListTowersResponse) GetTowers() []*Tower { @@ -598,7 +697,7 @@ type StatsRequest struct { func (x *StatsRequest) Reset() { *x = StatsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[9] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -611,7 +710,7 @@ func (x *StatsRequest) String() string { func (*StatsRequest) ProtoMessage() {} func (x *StatsRequest) ProtoReflect() protoreflect.Message { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[9] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -624,7 +723,7 @@ func (x *StatsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StatsRequest.ProtoReflect.Descriptor instead. func (*StatsRequest) Descriptor() ([]byte, []int) { - return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{9} + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{10} } type StatsResponse struct { @@ -650,7 +749,7 @@ type StatsResponse struct { func (x *StatsResponse) Reset() { *x = StatsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[10] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -663,7 +762,7 @@ func (x *StatsResponse) String() string { func (*StatsResponse) ProtoMessage() {} func (x *StatsResponse) ProtoReflect() protoreflect.Message { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[10] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -676,7 +775,7 @@ func (x *StatsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StatsResponse.ProtoReflect.Descriptor instead. func (*StatsResponse) Descriptor() ([]byte, []int) { - return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{10} + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{11} } func (x *StatsResponse) GetNumBackups() uint32 { @@ -726,7 +825,7 @@ type PolicyRequest struct { func (x *PolicyRequest) Reset() { *x = PolicyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[11] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -739,7 +838,7 @@ func (x *PolicyRequest) String() string { func (*PolicyRequest) ProtoMessage() {} func (x *PolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[11] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -752,7 +851,7 @@ func (x *PolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyRequest.ProtoReflect.Descriptor instead. func (*PolicyRequest) Descriptor() ([]byte, []int) { - return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{11} + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{12} } func (x *PolicyRequest) GetPolicyType() PolicyType { @@ -784,7 +883,7 @@ type PolicyResponse struct { func (x *PolicyResponse) Reset() { *x = PolicyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[12] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -797,7 +896,7 @@ func (x *PolicyResponse) String() string { func (*PolicyResponse) ProtoMessage() {} func (x *PolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_wtclientrpc_wtclient_proto_msgTypes[12] + mi := &file_wtclientrpc_wtclient_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -810,7 +909,7 @@ func (x *PolicyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyResponse.ProtoReflect.Descriptor instead. func (*PolicyResponse) Descriptor() ([]byte, []int) { - return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{12} + return file_wtclientrpc_wtclient_proto_rawDescGZIP(), []int{13} } func (x *PolicyResponse) GetMaxUpdates() uint32 { @@ -871,94 +970,113 @@ var file_wtclientrpc_wtclient_proto_rawDesc = []byte{ 0x73, 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, 0x77, - 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x22, 0xd1, - 0x01, 0x0a, 0x05, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, + 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x22, 0x9f, + 0x02, 0x0a, 0x05, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x38, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, - 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, - 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, - 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x3e, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x77, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x06, 0x74, 0x6f, - 0x77, 0x65, 0x72, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xf8, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x62, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f, 0x70, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f, 0x66, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x61, - 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x6e, 0x75, 0x6d, - 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, - 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x22, - 0x49, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x0e, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2f, - 0x0a, 0x12, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0f, - 0x73, 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x12, - 0x2d, 0x0a, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, - 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, 0x77, - 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x2a, 0x24, - 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, - 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, 0x48, - 0x4f, 0x52, 0x10, 0x01, 0x32, 0xc5, 0x03, 0x0a, 0x10, 0x57, 0x61, 0x74, 0x63, 0x68, 0x74, 0x6f, - 0x77, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x41, 0x64, 0x64, - 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, - 0x72, 0x12, 0x1f, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, - 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x53, 0x74, 0x61, - 0x74, 0x73, 0x12, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, - 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1b, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, 0x2f, - 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, - 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0c, + 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, + 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x22, 0xe0, 0x01, 0x0a, 0x10, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, + 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x22, 0x3e, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x22, 0x40, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x74, 0x6f, 0x77, + 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x06, 0x74, + 0x6f, 0x77, 0x65, 0x72, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xf8, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x62, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, + 0x6d, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x5f, + 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6e, 0x75, 0x6d, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f, + 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6e, 0x75, 0x6d, 0x5f, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x6e, 0x75, + 0x6d, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75, + 0x73, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6e, 0x75, 0x6d, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, + 0x22, 0x49, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x91, 0x01, 0x0a, 0x0e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x2f, 0x0a, 0x12, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x0f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, + 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, + 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x73, + 0x77, 0x65, 0x65, 0x70, 0x53, 0x61, 0x74, 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x2a, + 0x24, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, + 0x06, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x4e, 0x43, + 0x48, 0x4f, 0x52, 0x10, 0x01, 0x32, 0xc5, 0x03, 0x0a, 0x10, 0x57, 0x61, 0x74, 0x63, 0x68, 0x74, + 0x6f, 0x77, 0x65, 0x72, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x08, 0x41, 0x64, + 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, + 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4d, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, + 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x77, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x12, 0x19, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, + 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1a, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, + 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x6c, 0x6e, 0x64, + 0x2f, 0x6c, 0x6e, 0x72, 0x70, 0x63, 0x2f, 0x77, 0x74, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x72, + 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -974,7 +1092,7 @@ func file_wtclientrpc_wtclient_proto_rawDescGZIP() []byte { } var file_wtclientrpc_wtclient_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_wtclientrpc_wtclient_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_wtclientrpc_wtclient_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_wtclientrpc_wtclient_proto_goTypes = []interface{}{ (PolicyType)(0), // 0: wtclientrpc.PolicyType (*AddTowerRequest)(nil), // 1: wtclientrpc.AddTowerRequest @@ -984,34 +1102,38 @@ var file_wtclientrpc_wtclient_proto_goTypes = []interface{}{ (*GetTowerInfoRequest)(nil), // 5: wtclientrpc.GetTowerInfoRequest (*TowerSession)(nil), // 6: wtclientrpc.TowerSession (*Tower)(nil), // 7: wtclientrpc.Tower - (*ListTowersRequest)(nil), // 8: wtclientrpc.ListTowersRequest - (*ListTowersResponse)(nil), // 9: wtclientrpc.ListTowersResponse - (*StatsRequest)(nil), // 10: wtclientrpc.StatsRequest - (*StatsResponse)(nil), // 11: wtclientrpc.StatsResponse - (*PolicyRequest)(nil), // 12: wtclientrpc.PolicyRequest - (*PolicyResponse)(nil), // 13: wtclientrpc.PolicyResponse + (*TowerSessionInfo)(nil), // 8: wtclientrpc.TowerSessionInfo + (*ListTowersRequest)(nil), // 9: wtclientrpc.ListTowersRequest + (*ListTowersResponse)(nil), // 10: wtclientrpc.ListTowersResponse + (*StatsRequest)(nil), // 11: wtclientrpc.StatsRequest + (*StatsResponse)(nil), // 12: wtclientrpc.StatsResponse + (*PolicyRequest)(nil), // 13: wtclientrpc.PolicyRequest + (*PolicyResponse)(nil), // 14: wtclientrpc.PolicyResponse } var file_wtclientrpc_wtclient_proto_depIdxs = []int32{ 6, // 0: wtclientrpc.Tower.sessions:type_name -> wtclientrpc.TowerSession - 7, // 1: wtclientrpc.ListTowersResponse.towers:type_name -> wtclientrpc.Tower - 0, // 2: wtclientrpc.PolicyRequest.policy_type:type_name -> wtclientrpc.PolicyType - 1, // 3: wtclientrpc.WatchtowerClient.AddTower:input_type -> wtclientrpc.AddTowerRequest - 3, // 4: wtclientrpc.WatchtowerClient.RemoveTower:input_type -> wtclientrpc.RemoveTowerRequest - 8, // 5: wtclientrpc.WatchtowerClient.ListTowers:input_type -> wtclientrpc.ListTowersRequest - 5, // 6: wtclientrpc.WatchtowerClient.GetTowerInfo:input_type -> wtclientrpc.GetTowerInfoRequest - 10, // 7: wtclientrpc.WatchtowerClient.Stats:input_type -> wtclientrpc.StatsRequest - 12, // 8: wtclientrpc.WatchtowerClient.Policy:input_type -> wtclientrpc.PolicyRequest - 2, // 9: wtclientrpc.WatchtowerClient.AddTower:output_type -> wtclientrpc.AddTowerResponse - 4, // 10: wtclientrpc.WatchtowerClient.RemoveTower:output_type -> wtclientrpc.RemoveTowerResponse - 9, // 11: wtclientrpc.WatchtowerClient.ListTowers:output_type -> wtclientrpc.ListTowersResponse - 7, // 12: wtclientrpc.WatchtowerClient.GetTowerInfo:output_type -> wtclientrpc.Tower - 11, // 13: wtclientrpc.WatchtowerClient.Stats:output_type -> wtclientrpc.StatsResponse - 13, // 14: wtclientrpc.WatchtowerClient.Policy:output_type -> wtclientrpc.PolicyResponse - 9, // [9:15] is the sub-list for method output_type - 3, // [3:9] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 8, // 1: wtclientrpc.Tower.session_info:type_name -> wtclientrpc.TowerSessionInfo + 6, // 2: wtclientrpc.TowerSessionInfo.sessions:type_name -> wtclientrpc.TowerSession + 0, // 3: wtclientrpc.TowerSessionInfo.policy_type:type_name -> wtclientrpc.PolicyType + 7, // 4: wtclientrpc.ListTowersResponse.towers:type_name -> wtclientrpc.Tower + 0, // 5: wtclientrpc.PolicyRequest.policy_type:type_name -> wtclientrpc.PolicyType + 1, // 6: wtclientrpc.WatchtowerClient.AddTower:input_type -> wtclientrpc.AddTowerRequest + 3, // 7: wtclientrpc.WatchtowerClient.RemoveTower:input_type -> wtclientrpc.RemoveTowerRequest + 9, // 8: wtclientrpc.WatchtowerClient.ListTowers:input_type -> wtclientrpc.ListTowersRequest + 5, // 9: wtclientrpc.WatchtowerClient.GetTowerInfo:input_type -> wtclientrpc.GetTowerInfoRequest + 11, // 10: wtclientrpc.WatchtowerClient.Stats:input_type -> wtclientrpc.StatsRequest + 13, // 11: wtclientrpc.WatchtowerClient.Policy:input_type -> wtclientrpc.PolicyRequest + 2, // 12: wtclientrpc.WatchtowerClient.AddTower:output_type -> wtclientrpc.AddTowerResponse + 4, // 13: wtclientrpc.WatchtowerClient.RemoveTower:output_type -> wtclientrpc.RemoveTowerResponse + 10, // 14: wtclientrpc.WatchtowerClient.ListTowers:output_type -> wtclientrpc.ListTowersResponse + 7, // 15: wtclientrpc.WatchtowerClient.GetTowerInfo:output_type -> wtclientrpc.Tower + 12, // 16: wtclientrpc.WatchtowerClient.Stats:output_type -> wtclientrpc.StatsResponse + 14, // 17: wtclientrpc.WatchtowerClient.Policy:output_type -> wtclientrpc.PolicyResponse + 12, // [12:18] is the sub-list for method output_type + 6, // [6:12] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_wtclientrpc_wtclient_proto_init() } @@ -1105,7 +1227,7 @@ func file_wtclientrpc_wtclient_proto_init() { } } file_wtclientrpc_wtclient_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListTowersRequest); i { + switch v := v.(*TowerSessionInfo); i { case 0: return &v.state case 1: @@ -1117,7 +1239,7 @@ func file_wtclientrpc_wtclient_proto_init() { } } file_wtclientrpc_wtclient_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListTowersResponse); i { + switch v := v.(*ListTowersRequest); i { case 0: return &v.state case 1: @@ -1129,7 +1251,7 @@ func file_wtclientrpc_wtclient_proto_init() { } } file_wtclientrpc_wtclient_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatsRequest); i { + switch v := v.(*ListTowersResponse); i { case 0: return &v.state case 1: @@ -1141,7 +1263,7 @@ func file_wtclientrpc_wtclient_proto_init() { } } file_wtclientrpc_wtclient_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StatsResponse); i { + switch v := v.(*StatsRequest); i { case 0: return &v.state case 1: @@ -1153,7 +1275,7 @@ func file_wtclientrpc_wtclient_proto_init() { } } file_wtclientrpc_wtclient_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyRequest); i { + switch v := v.(*StatsResponse); i { case 0: return &v.state case 1: @@ -1165,6 +1287,18 @@ func file_wtclientrpc_wtclient_proto_init() { } } file_wtclientrpc_wtclient_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_wtclientrpc_wtclient_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PolicyResponse); i { case 0: return &v.state @@ -1183,7 +1317,7 @@ func file_wtclientrpc_wtclient_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_wtclientrpc_wtclient_proto_rawDesc, NumEnums: 1, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/lnrpc/wtclientrpc/wtclient.proto b/lnrpc/wtclientrpc/wtclient.proto index 736a4bdde..45bdd24ef 100644 --- a/lnrpc/wtclientrpc/wtclient.proto +++ b/lnrpc/wtclientrpc/wtclient.proto @@ -107,14 +107,37 @@ message Tower { // The list of addresses the watchtower is reachable over. repeated string addresses = 2; + // Deprecated, use the active_session_candidate field under the + // correct identifier in the client_type map. // Whether the watchtower is currently a candidate for new sessions. - bool active_session_candidate = 3; + bool active_session_candidate = 3 [deprecated = true]; + + // Deprecated, use the num_sessions field under the correct identifier + // in the client_type map. + // The number of sessions that have been negotiated with the watchtower. + uint32 num_sessions = 4 [deprecated = true]; + + // Deprecated, use the sessions field under the correct identifier in the + // client_type map. + // The list of sessions that have been negotiated with the watchtower. + repeated TowerSession sessions = 5 [deprecated = true]; + + // A list sessions held with the tower. + repeated TowerSessionInfo session_info = 6; +} + +message TowerSessionInfo { + // Whether the watchtower is currently a candidate for new sessions. + bool active_session_candidate = 1; // The number of sessions that have been negotiated with the watchtower. - uint32 num_sessions = 4; + uint32 num_sessions = 2; // The list of sessions that have been negotiated with the watchtower. - repeated TowerSession sessions = 5; + repeated TowerSession sessions = 3; + + // The session's policy type. + PolicyType policy_type = 4; } message ListTowersRequest { diff --git a/lnrpc/wtclientrpc/wtclient.swagger.json b/lnrpc/wtclientrpc/wtclient.swagger.json index 9ba618316..830dd10fe 100644 --- a/lnrpc/wtclientrpc/wtclient.swagger.json +++ b/lnrpc/wtclientrpc/wtclient.swagger.json @@ -359,19 +359,26 @@ }, "active_session_candidate": { "type": "boolean", - "description": "Whether the watchtower is currently a candidate for new sessions." + "description": "Deprecated, use the active_session_candidate field under the\ncorrect identifier in the client_type map.\nWhether the watchtower is currently a candidate for new sessions." }, "num_sessions": { "type": "integer", "format": "int64", - "description": "The number of sessions that have been negotiated with the watchtower." + "description": "Deprecated, use the num_sessions field under the correct identifier\nin the client_type map.\nThe number of sessions that have been negotiated with the watchtower." }, "sessions": { "type": "array", "items": { "$ref": "#/definitions/wtclientrpcTowerSession" }, - "description": "The list of sessions that have been negotiated with the watchtower." + "description": "Deprecated, use the sessions field under the correct identifier in the\nclient_type map.\nThe list of sessions that have been negotiated with the watchtower." + }, + "session_info": { + "type": "array", + "items": { + "$ref": "#/definitions/wtclientrpcTowerSessionInfo" + }, + "description": "A list sessions held with the tower." } } }, @@ -404,6 +411,31 @@ "description": "The fee rate, in satoshis per vbyte, that will be used by the watchtower for\nthe justice transaction in the event of a channel breach." } } + }, + "wtclientrpcTowerSessionInfo": { + "type": "object", + "properties": { + "active_session_candidate": { + "type": "boolean", + "description": "Whether the watchtower is currently a candidate for new sessions." + }, + "num_sessions": { + "type": "integer", + "format": "int64", + "description": "The number of sessions that have been negotiated with the watchtower." + }, + "sessions": { + "type": "array", + "items": { + "$ref": "#/definitions/wtclientrpcTowerSession" + }, + "description": "The list of sessions that have been negotiated with the watchtower." + }, + "policy_type": { + "$ref": "#/definitions/wtclientrpcPolicyType", + "description": "The session's policy type." + } + } } } } From bc868fe270c3a2432f3fe504646a004d1f8d39be Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 16 Mar 2023 15:00:26 +0200 Subject: [PATCH 4/4] docs: update release notes with 7059 --- docs/release-notes/release-notes-0.16.1.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/release-notes-0.16.1.md b/docs/release-notes/release-notes-0.16.1.md index 0edf7c3c8..c53d672ef 100644 --- a/docs/release-notes/release-notes-0.16.1.md +++ b/docs/release-notes/release-notes-0.16.1.md @@ -2,9 +2,14 @@ ## `lncli` -- The `lncli wallet psbt fund` command now allows users to specify the +* The `lncli wallet psbt fund` command now allows users to specify the [`--min_confs` flag](https://github.com/lightningnetwork/lnd/pull/7510). +## Watchtowers + +* [Allow caller to filter sessions at the time of reading them from + disk](https://github.com/lightningnetwork/lnd/pull/7059) + ## Misc * [Return `FEE_INSUFFICIENT` before checking balance for incoming low-fee @@ -12,6 +17,7 @@ # Contributors (Alphabetical Order) +* Elle Mouton * Oliver Gugger * Tommy Volk * Yong Yu