@@ -50,6 +50,9 @@ type LightningClient interface {
50
50
// ListChannels retrieves all channels of the backing lnd node.
51
51
ListChannels (ctx context.Context ) ([]ChannelInfo , error )
52
52
53
+ // ClosedChannels returns all closed channels of the backing lnd node.
54
+ ClosedChannels (ctx context.Context ) ([]ClosedChannel , error )
55
+
53
56
// ChannelBackup retrieves the backup for a particular channel. The
54
57
// backup is returned as an encrypted chanbackup.Single payload.
55
58
ChannelBackup (context.Context , wire.OutPoint ) ([]byte , error )
@@ -109,6 +112,138 @@ type ChannelInfo struct {
109
112
Uptime time.Duration
110
113
}
111
114
115
+ // ClosedChannel represents a channel that has been closed.
116
+ type ClosedChannel struct {
117
+ // ChannelPoint is the funding outpoint of the channel.
118
+ ChannelPoint string
119
+
120
+ // ChannelID holds the unique channel ID for the channel. The first 3
121
+ // bytes are the block height, the next 3 the index within the block,
122
+ // and the last 2 bytes are the output index for the channel.
123
+ ChannelID uint64
124
+
125
+ // ClosingTxHash is the tx hash of the close transaction for the channel.
126
+ ClosingTxHash string
127
+
128
+ // CloseType is the type of channel closure.
129
+ CloseType CloseType
130
+
131
+ // OpenInitiator is true if we opened the channel. This value is not
132
+ // always available (older channels do not have it).
133
+ OpenInitiator Initiator
134
+
135
+ // Initiator indicates which party initiated the channel close. Since
136
+ // this value is not always set in the rpc response, we also make a best
137
+ // effort attempt to set it based on CloseType.
138
+ CloseInitiator Initiator
139
+
140
+ // PubKeyBytes is the raw bytes of the public key of the remote node.
141
+ PubKeyBytes route.Vertex
142
+
143
+ // Capacity is the total amount of funds held in this channel.
144
+ Capacity btcutil.Amount
145
+
146
+ // SettledBalance is the amount we were paid out directly in this
147
+ // channel close. Note that this does not include cases where we need to
148
+ // sweep our commitment or htlcs.
149
+ SettledBalance btcutil.Amount
150
+ }
151
+
152
+ // CloseType is an enum which represents the types of closes our channels may
153
+ // have. This type maps to the rpc value.
154
+ type CloseType uint8
155
+
156
+ const (
157
+ // CloseTypeCooperative represents cooperative closes.
158
+ CloseTypeCooperative CloseType = iota
159
+
160
+ // CloseTypeLocalForce represents force closes that we initiated.
161
+ CloseTypeLocalForce
162
+
163
+ // CloseTypeRemoteForce represents force closes that our peer initiated.
164
+ CloseTypeRemoteForce
165
+
166
+ // CloseTypeBreach represents breach closes from our peer.
167
+ CloseTypeBreach
168
+
169
+ // CloseTypeFundingCancelled represents channels which were never
170
+ // created because their funding transaction was cancelled.
171
+ CloseTypeFundingCancelled
172
+
173
+ // CloseTypeAbandoned represents a channel that was abandoned.
174
+ CloseTypeAbandoned
175
+ )
176
+
177
+ // String returns the string representation of a close type.
178
+ func (c CloseType ) String () string {
179
+ switch c {
180
+ case CloseTypeCooperative :
181
+ return "Cooperative"
182
+
183
+ case CloseTypeLocalForce :
184
+ return "Local Force"
185
+
186
+ case CloseTypeRemoteForce :
187
+ return "Remote Force"
188
+
189
+ case CloseTypeBreach :
190
+ return "Breach"
191
+
192
+ case CloseTypeFundingCancelled :
193
+ return "Funding Cancelled"
194
+
195
+ case CloseTypeAbandoned :
196
+ return "Abandoned"
197
+
198
+ default :
199
+ return "Unknown"
200
+ }
201
+ }
202
+
203
+ // Initiator indicates the party that opened or closed a channel. This enum is
204
+ // used for cases where we may not have a full set of initiator information
205
+ // available over rpc (this is the case for older channels).
206
+ type Initiator uint8
207
+
208
+ const (
209
+ // InitiatorUnrecorded is set when we do not know the open/close
210
+ // initiator for a channel, this is the case when the channel was
211
+ // closed before lnd started tracking initiators.
212
+ InitiatorUnrecorded Initiator = iota
213
+
214
+ // InitiatorLocal is set when we initiated a channel open or close.
215
+ InitiatorLocal
216
+
217
+ // InitiatorRemote is set when the remote party initiated a chanel open
218
+ // or close.
219
+ InitiatorRemote
220
+
221
+ // InitiatorBoth is set in the case where both parties initiated a
222
+ // cooperative close (this is possible with multiple rounds of
223
+ // negotiation).
224
+ InitiatorBoth
225
+ )
226
+
227
+ // String provides the string represenetation of a close initiator.
228
+ func (c Initiator ) String () string {
229
+ switch c {
230
+ case InitiatorUnrecorded :
231
+ return "Unrecorded"
232
+
233
+ case InitiatorLocal :
234
+ return "Local"
235
+
236
+ case InitiatorRemote :
237
+ return "Remote"
238
+
239
+ case InitiatorBoth :
240
+ return "Both"
241
+
242
+ default :
243
+ return fmt .Sprintf ("unknown initiator: %d" , c )
244
+ }
245
+ }
246
+
112
247
var (
113
248
// ErrMalformedServerResponse is returned when the swap and/or prepay
114
249
// invoice is malformed.
@@ -583,6 +718,130 @@ func (s *lightningClient) ListChannels(ctx context.Context) (
583
718
return result , nil
584
719
}
585
720
721
+ // ClosedChannels returns a list of our closed channels.
722
+ func (s * lightningClient ) ClosedChannels (ctx context.Context ) ([]ClosedChannel ,
723
+ error ) {
724
+
725
+ rpcCtx , cancel := context .WithTimeout (ctx , rpcTimeout )
726
+ defer cancel ()
727
+
728
+ response , err := s .client .ClosedChannels (
729
+ s .adminMac .WithMacaroonAuth (rpcCtx ),
730
+ & lnrpc.ClosedChannelsRequest {},
731
+ )
732
+ if err != nil {
733
+ return nil , err
734
+ }
735
+
736
+ channels := make ([]ClosedChannel , len (response .Channels ))
737
+ for i , channel := range response .Channels {
738
+ remote , err := route .NewVertexFromStr (channel .RemotePubkey )
739
+ if err != nil {
740
+ return nil , err
741
+ }
742
+
743
+ closeType , err := rpcCloseType (channel .CloseType )
744
+ if err != nil {
745
+ return nil , err
746
+ }
747
+
748
+ openInitiator , err := getInitiator (channel .OpenInitiator )
749
+ if err != nil {
750
+ return nil , err
751
+ }
752
+
753
+ closeInitiator , err := rpcCloseInitiator (
754
+ channel .CloseInitiator , closeType ,
755
+ )
756
+ if err != nil {
757
+ return nil , err
758
+ }
759
+
760
+ channels [i ] = ClosedChannel {
761
+ ChannelPoint : channel .ChannelPoint ,
762
+ ChannelID : channel .ChanId ,
763
+ ClosingTxHash : channel .ClosingTxHash ,
764
+ CloseType : closeType ,
765
+ OpenInitiator : openInitiator ,
766
+ CloseInitiator : closeInitiator ,
767
+ PubKeyBytes : remote ,
768
+ Capacity : btcutil .Amount (channel .Capacity ),
769
+ SettledBalance : btcutil .Amount (channel .SettledBalance ),
770
+ }
771
+ }
772
+
773
+ return channels , nil
774
+ }
775
+
776
+ // rpcCloseType maps a rpc close type to our local enum.
777
+ func rpcCloseType (t lnrpc.ChannelCloseSummary_ClosureType ) (CloseType , error ) {
778
+ switch t {
779
+ case lnrpc .ChannelCloseSummary_COOPERATIVE_CLOSE :
780
+ return CloseTypeCooperative , nil
781
+
782
+ case lnrpc .ChannelCloseSummary_LOCAL_FORCE_CLOSE :
783
+ return CloseTypeLocalForce , nil
784
+
785
+ case lnrpc .ChannelCloseSummary_REMOTE_FORCE_CLOSE :
786
+ return CloseTypeRemoteForce , nil
787
+
788
+ case lnrpc .ChannelCloseSummary_BREACH_CLOSE :
789
+ return CloseTypeBreach , nil
790
+
791
+ case lnrpc .ChannelCloseSummary_FUNDING_CANCELED :
792
+ return CloseTypeFundingCancelled , nil
793
+
794
+ case lnrpc .ChannelCloseSummary_ABANDONED :
795
+ return CloseTypeAbandoned , nil
796
+
797
+ default :
798
+ return 0 , fmt .Errorf ("unknown close type: %v" , t )
799
+ }
800
+ }
801
+
802
+ // rpcCloseInitiator maps a close initiator to our local type. Since this field
803
+ // is not always set in lnd for older channels, also use our close type to infer
804
+ // who initiated the close when we have force closes.
805
+ func rpcCloseInitiator (initiator lnrpc.Initiator ,
806
+ closeType CloseType ) (Initiator , error ) {
807
+
808
+ // Since our close type is always set on the rpc, we first check whether
809
+ // we can figure out the close initiator from this value. This is only
810
+ // possible for force closes/breaches.
811
+ switch closeType {
812
+ case CloseTypeLocalForce :
813
+ return InitiatorLocal , nil
814
+
815
+ case CloseTypeRemoteForce , CloseTypeBreach :
816
+ return InitiatorRemote , nil
817
+ }
818
+
819
+ // Otherwise, we check whether our initiator field is set, and fail only
820
+ // if we have an unknown type.
821
+ return getInitiator (initiator )
822
+ }
823
+
824
+ // getInitiator maps a rpc initiator value to our initiator enum.
825
+ func getInitiator (initiator lnrpc.Initiator ) (Initiator , error ) {
826
+ switch initiator {
827
+ case lnrpc .Initiator_INITIATOR_LOCAL :
828
+ return InitiatorLocal , nil
829
+
830
+ case lnrpc .Initiator_INITIATOR_REMOTE :
831
+ return InitiatorRemote , nil
832
+
833
+ case lnrpc .Initiator_INITIATOR_BOTH :
834
+ return InitiatorBoth , nil
835
+
836
+ case lnrpc .Initiator_INITIATOR_UNKNOWN :
837
+ return InitiatorUnrecorded , nil
838
+
839
+ default :
840
+ return InitiatorUnrecorded , fmt .Errorf ("unknown " +
841
+ "initiator: %v" , initiator )
842
+ }
843
+ }
844
+
586
845
// ChannelBackup retrieves the backup for a particular channel. The backup is
587
846
// returned as an encrypted chanbackup.Single payload.
588
847
func (s * lightningClient ) ChannelBackup (ctx context.Context ,
0 commit comments