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 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." + } + } } } } diff --git a/watchtower/wtclient/client.go b/watchtower/wtclient/client.go index 685170eab..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, 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, 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: "+ @@ -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,9 @@ 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, c.genSessionFilter(false), opts..., + ) if err != nil { return nil, err } @@ -1361,7 +1363,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, c.genSessionFilter(false), 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 {