-
Notifications
You must be signed in to change notification settings - Fork 43
/
state_client.go
199 lines (156 loc) · 5.13 KB
/
state_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package lndclient
import (
"context"
"fmt"
"sync"
"github.com/lightningnetwork/lnd/lnrpc"
"google.golang.org/grpc"
)
// StateClient exposes base lightning functionality.
type StateClient interface {
// SubscribeState subscribes to the current state of the wallet.
SubscribeState(ctx context.Context) (chan WalletState, chan error,
error)
// GetState returns the current wallet state without subscribing to more
// state updates.
GetState(context.Context) (WalletState, error)
}
// WalletState is a type that represents all states the lnd wallet can be in.
type WalletState uint8
const (
// WalletStateNonExisting denotes that no wallet has been created in lnd
// so far.
WalletStateNonExisting WalletState = 0
// WalletStateLocked denotes that a wallet exists in lnd but it has not
// yet been unlocked.
WalletStateLocked WalletState = 1
// WalletStateUnlocked denotes that a wallet exists in lnd and it has
// been unlocked but the RPC server isn't yet fully started up.
WalletStateUnlocked WalletState = 2
// WalletStateRPCActive denotes that lnd is now fully ready to receive
// RPC requests other than wallet unlocking operations.
WalletStateRPCActive WalletState = 3
// WalletStateServerActive denotes that lnd's main server is now fully
// ready to receive calls.
WalletStateServerActive WalletState = 4
// WalletStateWaitingToStart indicates that lnd is at the beginning of
// the startup process. In a cluster environment this may mean that
// we're waiting to become the leader in which case RPC calls will be
// disabled until this instance has been elected as leader.
WalletStateWaitingToStart WalletState = 255
)
// String returns a string representation of the WalletState.
func (s WalletState) String() string {
switch s {
case WalletStateNonExisting:
return "No wallet exists"
case WalletStateLocked:
return "Wallet is locked"
case WalletStateUnlocked:
return "Wallet is unlocked"
case WalletStateRPCActive:
return "Lnd RPC server is ready for requests"
case WalletStateServerActive:
return "Lnd main server is ready for requests"
case WalletStateWaitingToStart:
return "Lnd is waiting to start"
default:
return fmt.Sprintf("unknown wallet state <%d>", s)
}
}
// ReadyForGetInfo returns true if the wallet state is ready for the GetInfo to
// be called. This needs to also return true for the RPC active state to be
// backward compatible with lnd 0.13.x nodes which didn't yet have the server
// active state. But the GetInfo RPC isn't guarded by that server active flag
// anyway, so we can call that whenever the RPC server is ready.
func (s WalletState) ReadyForGetInfo() bool {
return s == WalletStateRPCActive || s == WalletStateServerActive
}
// stateClient is a client for lnd's lnrpc.State service.
type stateClient struct {
client lnrpc.StateClient
readonlyMac serializedMacaroon
wg sync.WaitGroup
}
// newStateClient returns a new stateClient.
func newStateClient(conn grpc.ClientConnInterface,
readonlyMac serializedMacaroon) *stateClient {
return &stateClient{
client: lnrpc.NewStateClient(conn),
readonlyMac: readonlyMac,
}
}
// WaitForFinished waits until all state subscriptions have finished.
func (s *stateClient) WaitForFinished() {
s.wg.Wait()
}
// SubscribeState subscribes to the current state of the wallet.
func (s *stateClient) SubscribeState(ctx context.Context) (chan WalletState,
chan error, error) {
resp, err := s.client.SubscribeState(
ctx, &lnrpc.SubscribeStateRequest{},
)
if err != nil {
return nil, nil, err
}
stateChan := make(chan WalletState, 1)
errChan := make(chan error, 1)
s.wg.Add(1)
go func() {
defer s.wg.Done()
for {
stateEvent, err := resp.Recv()
if err != nil {
errChan <- err
return
}
state, err := unmarshalWalletState(stateEvent.State)
if err != nil {
errChan <- err
return
}
select {
case stateChan <- state:
case <-ctx.Done():
return
}
// If this is the final state, no more states will be
// sent to us, and we can close the subscription.
if state == WalletStateServerActive {
close(stateChan)
close(errChan)
return
}
}
}()
return stateChan, errChan, nil
}
// GetState returns the current wallet state without subscribing to more
// state updates.
func (s *stateClient) GetState(ctx context.Context) (WalletState, error) {
state, err := s.client.GetState(ctx, &lnrpc.GetStateRequest{})
if err != nil {
return 0, err
}
return unmarshalWalletState(state.State)
}
// unmarshalWalletState turns the RPC wallet state into the internal wallet
// state type.
func unmarshalWalletState(rpcState lnrpc.WalletState) (WalletState, error) {
switch rpcState {
case lnrpc.WalletState_WAITING_TO_START:
return WalletStateWaitingToStart, nil
case lnrpc.WalletState_NON_EXISTING:
return WalletStateNonExisting, nil
case lnrpc.WalletState_LOCKED:
return WalletStateLocked, nil
case lnrpc.WalletState_UNLOCKED:
return WalletStateUnlocked, nil
case lnrpc.WalletState_RPC_ACTIVE:
return WalletStateRPCActive, nil
case lnrpc.WalletState_SERVER_ACTIVE:
return WalletStateServerActive, nil
default:
return 0, fmt.Errorf("unknown wallet state: %d", rpcState)
}
}