Skip to content

Commit 2ba712f

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 79e0627 commit 2ba712f

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed

wtclient_client.go

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
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+
// NOTE: For the macaroon code to work correctly, this needs to be named
14+
// WatchtowerClientClient since we use reflection to set macaroon permissions.
15+
type WatchtowerClientClient interface {
16+
ServiceClient[wtclientrpc.WatchtowerClientClient]
17+
18+
// AddTower adds a new watchtower reachable at the given address and
19+
// considers it for new sessions. If the watchtower already exists, then
20+
// any new addresses included will be considered when dialing it for
21+
// session negotiations and backups.
22+
AddTower(ctx context.Context, pubkey []byte, address string) error
23+
24+
// DeactivateTower sets the given tower's status to inactive so that it
25+
// is not considered for session negotiation. Its sessions will also not
26+
// be used while the tower is inactive.
27+
DeactivateTower(ctx context.Context, pubkey []byte) (string, error)
28+
29+
// GetTowerInfo gets information about a watchtower that corresponds to
30+
// the given pubkey. The `includeSessions` flag controls whether session
31+
// information is included. The `excludeExhaustedSessions` controls
32+
// whether exhausted sessions are included in the response.
33+
GetTowerInfo(ctx context.Context, pubkey []byte, includeSessions,
34+
excludeExhaustedSessions bool) (*wtclientrpc.Tower, error)
35+
36+
// ListTowers gets information about all registered watchtowers. The
37+
// `includeSessions` and `excludeExhaustedSessions` flags serve the same
38+
// function as in the `GetTowerInfo` method.
39+
ListTowers(ctx context.Context, includeSessions,
40+
excludeExhaustedSessions bool) ([]*wtclientrpc.Tower, error)
41+
42+
// Policy returns the active watchtower client policy configuration.
43+
Policy(ctx context.Context, policyType wtclientrpc.PolicyType) (
44+
*wtclientrpc.PolicyResponse, error)
45+
46+
// RemoveTower removes a watchtower from being considered for future
47+
// session negotiations and from being used for any subsequent backups
48+
// until it's added again. If an address is provided, then this RPC only
49+
// serves as a way of removing the address from the watchtower instead.
50+
RemoveTower(ctx context.Context, pubkey []byte, address string) error
51+
52+
// Stats returns the in-memory statistics of the client since startup.
53+
Stats(ctx context.Context) (*wtclientrpc.StatsResponse, error)
54+
55+
// TerminateSession terminates the given session and marks it to not be
56+
// used for backups anymore. Returns a human-readable status string.
57+
TerminateSession(ctx context.Context, sessionId []byte) (string, error)
58+
}
59+
60+
type wtClient struct {
61+
client wtclientrpc.WatchtowerClientClient
62+
wtClientMac serializedMacaroon
63+
timeout time.Duration
64+
}
65+
66+
// A compile time check to ensure that wtClientClient implements the
67+
// WtclientClient interface.
68+
var _ WatchtowerClientClient = (*wtClient)(nil)
69+
70+
// newClientClient creates a new watchtower client interface.
71+
func newWtClient(conn grpc.ClientConnInterface, wtClientMac serializedMacaroon,
72+
timeout time.Duration) *wtClient {
73+
74+
return &wtClient{
75+
client: wtclientrpc.NewWatchtowerClientClient(conn),
76+
wtClientMac: wtClientMac,
77+
timeout: timeout,
78+
}
79+
}
80+
81+
// RawClientWithMacAuth returns a context with the proper macaroon
82+
// authentication, the default RPC timeout, and the raw client.
83+
func (m *wtClient) RawClientWithMacAuth(
84+
parentCtx context.Context) (context.Context, time.Duration,
85+
wtclientrpc.WatchtowerClientClient) {
86+
87+
return m.wtClientMac.WithMacaroonAuth(parentCtx), m.timeout, m.client
88+
}
89+
90+
// AddTower adds a new watchtower reachable at the given address and
91+
// considers it for new sessions. If the watchtower already exists, then
92+
// any new addresses included will be considered when dialing it for
93+
// session negotiations and backups.
94+
func (m *wtClient) AddTower(ctx context.Context, pubkey []byte,
95+
address string) error {
96+
97+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
98+
defer cancel()
99+
100+
rpcReq := &wtclientrpc.AddTowerRequest{
101+
Pubkey: pubkey,
102+
Address: address,
103+
}
104+
105+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
106+
_, err := m.client.AddTower(rpcCtx, rpcReq)
107+
108+
return err
109+
}
110+
111+
// DeactivateTower sets the given tower's status to inactive so that it
112+
// is not considered for session negotiation. Its sessions will also not
113+
// be used while the tower is inactive.
114+
func (m *wtClient) DeactivateTower(ctx context.Context, pubkey []byte) (
115+
string, error) {
116+
117+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
118+
defer cancel()
119+
120+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
121+
resp, err := m.client.DeactivateTower(rpcCtx,
122+
&wtclientrpc.DeactivateTowerRequest{
123+
Pubkey: pubkey,
124+
},
125+
)
126+
if err != nil {
127+
return "", err
128+
}
129+
130+
return resp.Status, nil
131+
}
132+
133+
// GetTowerInfo gets information about a watchtower that corresponds to
134+
// the given pubkey. The `includeSessions` flag controls whether session
135+
// information is included. The `excludeExhaustedSessions` controls
136+
// whether exhausted sessions are included in the response.
137+
func (m *wtClient) GetTowerInfo(ctx context.Context, pubkey []byte,
138+
includeSessions, excludeExhaustedSessions bool) (*wtclientrpc.Tower,
139+
error) {
140+
141+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
142+
defer cancel()
143+
144+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
145+
return m.client.GetTowerInfo(rpcCtx,
146+
&wtclientrpc.GetTowerInfoRequest{
147+
Pubkey: pubkey,
148+
IncludeSessions: includeSessions,
149+
ExcludeExhaustedSessions: excludeExhaustedSessions,
150+
},
151+
)
152+
}
153+
154+
// ListTowers gets information about all registered watchtowers. The
155+
// `includeSessions` and `excludeExhaustedSessions` flags serve the same
156+
// function as in the `GetTowerInfo` method.
157+
func (m *wtClient) ListTowers(ctx context.Context, includeSessions,
158+
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.ListTowers(rpcCtx, &wtclientrpc.ListTowersRequest{
165+
IncludeSessions: includeSessions,
166+
ExcludeExhaustedSessions: excludeExhaustedSessions,
167+
})
168+
if err != nil {
169+
return nil, err
170+
}
171+
172+
return resp.Towers, nil
173+
}
174+
175+
// Policy returns the active watchtower client policy configuration.
176+
func (m *wtClient) Policy(ctx context.Context,
177+
policyType wtclientrpc.PolicyType) (*wtclientrpc.PolicyResponse,
178+
error) {
179+
180+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
181+
defer cancel()
182+
183+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
184+
return m.client.Policy(rpcCtx, &wtclientrpc.PolicyRequest{
185+
PolicyType: policyType,
186+
})
187+
}
188+
189+
// RemoveTower removes a watchtower from being considered for future
190+
// session negotiations and from being used for any subsequent backups
191+
// until it's added again. If an address is provided, then this RPC only
192+
// serves as a way of removing the address from the watchtower instead.
193+
func (m *wtClient) RemoveTower(ctx context.Context, pubkey []byte,
194+
address string) error {
195+
196+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
197+
defer cancel()
198+
199+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
200+
_, err := m.client.RemoveTower(rpcCtx, &wtclientrpc.RemoveTowerRequest{
201+
Pubkey: pubkey,
202+
Address: address,
203+
})
204+
205+
return err
206+
}
207+
208+
// Stats returns the in-memory statistics of the client since startup.
209+
func (m *wtClient) Stats(ctx context.Context) (*wtclientrpc.StatsResponse,
210+
error) {
211+
212+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
213+
defer cancel()
214+
215+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
216+
return m.client.Stats(rpcCtx, &wtclientrpc.StatsRequest{})
217+
}
218+
219+
// TerminateSession terminates the given session and marks it to not be used
220+
// for backups anymore. Returns a human-readable status string.
221+
func (m *wtClient) TerminateSession(ctx context.Context,
222+
sessionId []byte) (string, error) {
223+
224+
rpcCtx, cancel := context.WithTimeout(ctx, m.timeout)
225+
defer cancel()
226+
227+
rpcCtx = m.wtClientMac.WithMacaroonAuth(rpcCtx)
228+
resp, err := m.client.TerminateSession(rpcCtx,
229+
&wtclientrpc.TerminateSessionRequest{
230+
SessionId: sessionId,
231+
},
232+
)
233+
if err != nil {
234+
return "", err
235+
}
236+
237+
return resp.Status, nil
238+
}

0 commit comments

Comments
 (0)