Skip to content

Commit 48b687c

Browse files
committed
lndclient: Add watchtower client interface
Add the wtclient_client.go file which contains the lnd watchtower client interface. Signed-off-by: Jonathan <[email protected]>
1 parent d78bede commit 48b687c

File tree

1 file changed

+279
-0
lines changed

1 file changed

+279
-0
lines changed

wtclient_client.go

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
package lndclient
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/lightningnetwork/lnd/lnrpc/wtclientrpc"
8+
"google.golang.org/grpc"
9+
)
10+
11+
// WatchtowerClientClient implements the watchtower client interface
12+
// https://lightning.engineering/api-docs/category/watchtowerclient-service/
13+
type WatchtowerClientClient interface {
14+
ServiceClient[wtclientrpc.WatchtowerClientClient]
15+
16+
// AddTower adds a new watchtower reachable at the given address and
17+
// considers it for new sessions. If the watchtower already exists, then
18+
// any new addresses included will be considered when dialing it for
19+
// session negotiations and backups.
20+
AddTower(ctx context.Context, pubkey []byte, address string) error
21+
22+
// DeactivateTower sets the given tower's status to inactive so that it
23+
// is not considered for session negotiation. Its sessions will also not
24+
// be used while the tower is inactive.
25+
DeactivateTower(ctx context.Context, pubkey []byte) (string, error)
26+
27+
// GetTowerInfo gets information about a watchtower that corresponds to the
28+
// given pubkey. The `includeSessions` flag controls whether session
29+
// information is included. The `excludeExhaustedSessions` controls whether
30+
// exhausted sessions are included in the response.
31+
GetTowerInfo(ctx context.Context, pubkey []byte, includeSessions,
32+
excludeExhaustedSessions bool) (*wtclientrpc.Tower, error)
33+
34+
// ListTowers gets information about all registered watchtowers. The
35+
// `includeSessions` and `excludeExhaustedSessions` flags serve the same
36+
// function as in the `GetTowerInfo` method.
37+
ListTowers(ctx context.Context, includeSessions,
38+
excludeExhaustedSessions bool) ([]*wtclientrpc.Tower, error)
39+
40+
// Policy returns the active watchtower client policy configuration.
41+
Policy(ctx context.Context, policyType wtclientrpc.PolicyType) (
42+
*PolicyResponse, error)
43+
44+
// RemoveTower removes a watchtower from being considered for future session
45+
// negotiations and from being used for any subsequent backups until it's added
46+
// again. If an address is provided, then this RPC only serves as a way of
47+
// removing the address from the watchtower instead.
48+
RemoveTower(ctx context.Context, pubkey []byte, address string) error
49+
50+
// Stats returns the in-memory statistics of the client since startup.
51+
Stats(ctx context.Context) (*StatsResponse, error)
52+
53+
// Terminates the given session and marks it to not be used for backups
54+
// anymore. Returns a human readable status string.
55+
TerminateSession(ctx context.Context, sessionId []byte) (string, error)
56+
}
57+
58+
// PolicyResponse returned by `Policy`
59+
// nolint:lll
60+
// https://lightning.engineering/api-docs/api/lnd/watchtower-client/policy/#wtclientrpcpolicyresponse
61+
type PolicyResponse struct {
62+
maxUpdates uint32
63+
sweepSatPerVbyte uint32
64+
}
65+
66+
// StatsResponse returned by `Stats`
67+
// nolint:lll
68+
// https://lightning.engineering/api-docs/api/lnd/watchtower-client/stats/#wtclientrpcstatsresponse
69+
type StatsResponse struct {
70+
numBackups uint32
71+
numPendingBackups uint32
72+
numFailedBackups uint32
73+
numSessionsAcquired uint32
74+
numSessionsExhausted uint32
75+
}
76+
77+
type wtClientClient struct {
78+
client wtclientrpc.WatchtowerClientClient
79+
wtClientMac serializedMacaroon
80+
timeout time.Duration
81+
}
82+
83+
// A compile time check to ensure that wtClientClient implements the
84+
// WtclientClient interface.
85+
var _ WatchtowerClientClient = (*wtClientClient)(nil)
86+
87+
// newClientClient creates a new watchtower client interface.
88+
func newWtClientClient(conn grpc.ClientConnInterface,
89+
wtClientMac serializedMacaroon, timeout time.Duration) *wtClientClient {
90+
91+
return &wtClientClient{
92+
client: wtclientrpc.NewWatchtowerClientClient(conn),
93+
wtClientMac: wtClientMac,
94+
timeout: timeout,
95+
}
96+
}
97+
98+
// RawClientWithMacAuth returns a context with the proper macaroon
99+
// authentication, the default RPC timeout, and the raw client.
100+
func (m *wtClientClient) RawClientWithMacAuth(
101+
parentCtx context.Context) (context.Context, time.Duration,
102+
wtclientrpc.WatchtowerClientClient) {
103+
104+
return m.wtClientMac.WithMacaroonAuth(parentCtx), m.timeout, m.client
105+
}
106+
107+
// AddTower adds a new watchtower reachable at the given address and
108+
// considers it for new sessions. If the watchtower already exists, then
109+
// any new addresses included will be considered when dialing it for
110+
// session negotiations and backups.
111+
func (m *wtClientClient) AddTower(ctx context.Context, pubkey []byte,
112+
address string) error {
113+
114+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
115+
defer cancel()
116+
117+
rpcReq := &wtclientrpc.AddTowerRequest{
118+
Pubkey: pubkey,
119+
Address: address,
120+
}
121+
122+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
123+
_, err := m.client.AddTower(rpcCtx, rpcReq)
124+
if err != nil {
125+
return err
126+
}
127+
128+
return nil
129+
}
130+
131+
// DeactivateTower sets the given tower's status to inactive so that it
132+
// is not considered for session negotiation. Its sessions will also not
133+
// be used while the tower is inactive.
134+
func (m *wtClientClient) DeactivateTower(ctx context.Context, pubkey []byte) (
135+
string, error) {
136+
137+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
138+
defer cancel()
139+
140+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
141+
resp, err := m.client.DeactivateTower(rpcCtx,
142+
&wtclientrpc.DeactivateTowerRequest{
143+
Pubkey: pubkey,
144+
},
145+
)
146+
if err != nil {
147+
return "", err
148+
}
149+
150+
return resp.Status, nil
151+
}
152+
153+
// GetTowerInfo gets information about a watchtower that corresponds to the
154+
// given pubkey. The `includeSessions` flag controls whether session information
155+
// is included. The `excludeExhaustedSessions` controls whether exhausted
156+
// sessions are included in the response.
157+
func (m *wtClientClient) GetTowerInfo(ctx context.Context, pubkey []byte,
158+
includeSessions, excludeExhaustedSessions bool) (*wtclientrpc.Tower, error) {
159+
160+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
161+
defer cancel()
162+
163+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
164+
resp, err := m.client.GetTowerInfo(rpcCtx, &wtclientrpc.GetTowerInfoRequest{
165+
Pubkey: pubkey,
166+
IncludeSessions: includeSessions,
167+
ExcludeExhaustedSessions: excludeExhaustedSessions,
168+
})
169+
if err != nil {
170+
return nil, err
171+
}
172+
173+
return resp, nil
174+
}
175+
176+
// ListTowers gets information about all registered watchtowers. The
177+
// `includeSessions` and `excludeExhaustedSessions` flags serve the same
178+
// function as in the `GetTowerInfo` method.
179+
func (m *wtClientClient) ListTowers(ctx context.Context, includeSessions,
180+
excludeExhaustedSessions bool) ([]*wtclientrpc.Tower, error) {
181+
182+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
183+
defer cancel()
184+
185+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
186+
resp, err := m.client.ListTowers(rpcCtx, &wtclientrpc.ListTowersRequest{
187+
IncludeSessions: includeSessions,
188+
ExcludeExhaustedSessions: excludeExhaustedSessions,
189+
})
190+
if err != nil {
191+
return nil, err
192+
}
193+
194+
return resp.Towers, nil
195+
}
196+
197+
// Policy returns the active watchtower client policy configuration.
198+
func (m *wtClientClient) Policy(ctx context.Context,
199+
policyType wtclientrpc.PolicyType) (*PolicyResponse, error) {
200+
201+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
202+
defer cancel()
203+
204+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
205+
resp, err := m.client.Policy(rpcCtx, &wtclientrpc.PolicyRequest{
206+
PolicyType: policyType,
207+
})
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
return &PolicyResponse{
213+
maxUpdates: resp.MaxUpdates,
214+
sweepSatPerVbyte: resp.SweepSatPerVbyte,
215+
}, nil
216+
}
217+
218+
// RemoveTower removes a watchtower from being considered for future session
219+
// negotiations and from being used for any subsequent backups until it's added
220+
// again. If an address is provided, then this RPC only serves as a way of
221+
// removing the address from the watchtower instead.
222+
func (m *wtClientClient) RemoveTower(ctx context.Context, pubkey []byte,
223+
address string) error {
224+
225+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
226+
defer cancel()
227+
228+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
229+
_, err := m.client.RemoveTower(rpcCtx, &wtclientrpc.RemoveTowerRequest{
230+
Pubkey: pubkey,
231+
Address: address,
232+
})
233+
if err != nil {
234+
return err
235+
}
236+
237+
return nil
238+
}
239+
240+
// Stats returns the in-memory statistics of the client since startup.
241+
func (m *wtClientClient) Stats(ctx context.Context) (*StatsResponse, error) {
242+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
243+
defer cancel()
244+
245+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
246+
resp, err := m.client.Stats(rpcCtx, &wtclientrpc.StatsRequest{})
247+
if err != nil {
248+
return nil, err
249+
}
250+
251+
return &StatsResponse{
252+
numBackups: resp.NumBackups,
253+
numPendingBackups: resp.NumPendingBackups,
254+
numFailedBackups: resp.NumFailedBackups,
255+
numSessionsAcquired: resp.NumSessionsAcquired,
256+
numSessionsExhausted: resp.NumSessionsExhausted,
257+
}, nil
258+
}
259+
260+
// Terminates the given session and marks it to not be used for backups
261+
// anymore. Returns a human readable status string.
262+
func (m *wtClientClient) TerminateSession(ctx context.Context,
263+
sessionId []byte) (string, error) {
264+
265+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
266+
defer cancel()
267+
268+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
269+
resp, err := m.client.TerminateSession(rpcCtx,
270+
&wtclientrpc.TerminateSessionRequest{
271+
SessionId: sessionId,
272+
},
273+
)
274+
if err != nil {
275+
return "", err
276+
}
277+
278+
return resp.Status, nil
279+
}

0 commit comments

Comments
 (0)