• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 18852986778

27 Oct 2025 07:10PM UTC coverage: 54.859% (-11.8%) from 66.648%
18852986778

Pull #10265

github

web-flow
Merge 45787b3d5 into 9a7b526c0
Pull Request #10265: multi: update close logic to handle re-orgs of depth n-1, where n is num confs - add min conf floor

529 of 828 new or added lines in 17 files covered. (63.89%)

24026 existing lines in 286 files now uncovered.

110927 of 202205 relevant lines covered (54.86%)

21658.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/discovery/chan_series.go
1
package discovery
2

3
import (
4
        "context"
5
        "iter"
6
        "time"
7

8
        "github.com/btcsuite/btcd/chaincfg/chainhash"
9
        graphdb "github.com/lightningnetwork/lnd/graph/db"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
        "github.com/lightningnetwork/lnd/netann"
12
        "github.com/lightningnetwork/lnd/routing/route"
13
)
14

15
// ChannelGraphTimeSeries is an interface that provides time and block based
16
// querying into our view of the channel graph. New channels will have
17
// monotonically increasing block heights, and new channel updates will have
18
// increasing timestamps. Once we connect to a peer, we'll use the methods in
19
// this interface to determine if we're already in sync, or need to request
20
// some new information from them.
21
type ChannelGraphTimeSeries interface {
22
        // HighestChanID should return the channel ID of the channel we know of
23
        // that's furthest in the target chain. This channel will have a block
24
        // height that's close to the current tip of the main chain as we
25
        // know it.  We'll use this to start our QueryChannelRange dance with
26
        // the remote node.
27
        HighestChanID(ctx context.Context,
28
                chain chainhash.Hash) (*lnwire.ShortChannelID, error)
29

30
        // UpdatesInHorizon returns all known channel and node updates with an
31
        // update timestamp between the start time and end time. We'll use this
32
        // to catch up a remote node to the set of channel updates that they
33
        // may have missed out on within the target chain.
34
        UpdatesInHorizon(chain chainhash.Hash, startTime time.Time,
35
                endTime time.Time) iter.Seq2[lnwire.Message, error]
36

37
        // FilterKnownChanIDs takes a target chain, and a set of channel ID's,
38
        // and returns a filtered set of chan ID's. This filtered set of chan
39
        // ID's represents the ID's that we don't know of which were in the
40
        // passed superSet.
41
        FilterKnownChanIDs(chain chainhash.Hash,
42
                superSet []graphdb.ChannelUpdateInfo,
43
                isZombieChan func(time.Time, time.Time) bool) (
44
                []lnwire.ShortChannelID, error)
45

46
        // FilterChannelRange returns the set of channels that we created
47
        // between the start height and the end height. The channel IDs are
48
        // grouped by their common block height. We'll use this to to a remote
49
        // peer's QueryChannelRange message.
50
        FilterChannelRange(chain chainhash.Hash, startHeight, endHeight uint32,
51
                withTimestamps bool) ([]graphdb.BlockChannelRange, error)
52

53
        // FetchChanAnns returns a full set of channel announcements as well as
54
        // their updates that match the set of specified short channel ID's.
55
        // We'll use this to reply to a QueryShortChanIDs message sent by a
56
        // remote peer. The response will contain a unique set of
57
        // ChannelAnnouncements, the latest ChannelUpdate for each of the
58
        // announcements, and a unique set of NodeAnnouncements.
59
        FetchChanAnns(chain chainhash.Hash,
60
                shortChanIDs []lnwire.ShortChannelID) ([]lnwire.Message, error)
61

62
        // FetchChanUpdates returns the latest channel update messages for the
63
        // specified short channel ID. If no channel updates are known for the
64
        // channel, then an empty slice will be returned.
65
        FetchChanUpdates(chain chainhash.Hash,
66
                shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1,
67
                error)
68
}
69

70
// ChanSeries is an implementation of the ChannelGraphTimeSeries
71
// interface backed by the channeldb ChannelGraph database. We'll provide this
72
// implementation to the AuthenticatedGossiper so it can properly use the
73
// in-protocol channel range queries to quickly and efficiently synchronize our
74
// channel state with all peers.
75
type ChanSeries struct {
76
        graph *graphdb.ChannelGraph
77
}
78

79
// NewChanSeries constructs a new ChanSeries backed by a channeldb.ChannelGraph.
80
// The returned ChanSeries implements the ChannelGraphTimeSeries interface.
UNCOV
81
func NewChanSeries(graph *graphdb.ChannelGraph) *ChanSeries {
×
UNCOV
82
        return &ChanSeries{
×
UNCOV
83
                graph: graph,
×
UNCOV
84
        }
×
UNCOV
85
}
×
86

87
// HighestChanID should return is the channel ID of the channel we know of
88
// that's furthest in the target chain. This channel will have a block height
89
// that's close to the current tip of the main chain as we know it.  We'll use
90
// this to start our QueryChannelRange dance with the remote node.
91
//
92
// NOTE: This is part of the ChannelGraphTimeSeries interface.
93
func (c *ChanSeries) HighestChanID(ctx context.Context,
UNCOV
94
        _ chainhash.Hash) (*lnwire.ShortChannelID, error) {
×
UNCOV
95

×
UNCOV
96
        chanID, err := c.graph.HighestChanID(ctx)
×
UNCOV
97
        if err != nil {
×
98
                return nil, err
×
99
        }
×
100

UNCOV
101
        shortChanID := lnwire.NewShortChanIDFromInt(chanID)
×
UNCOV
102
        return &shortChanID, nil
×
103
}
104

105
// UpdatesInHorizon returns all known channel and node updates with an update
106
// timestamp between the start time and end time. We'll use this to catch up a
107
// remote node to the set of channel updates that they may have missed out on
108
// within the target chain.
109
//
110
// NOTE: This is part of the ChannelGraphTimeSeries interface.
111
func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
UNCOV
112
        startTime, endTime time.Time) iter.Seq2[lnwire.Message, error] {
×
UNCOV
113

×
UNCOV
114
        return func(yield func(lnwire.Message, error) bool) {
×
UNCOV
115
                // First, we'll query for all the set of channels that have an
×
UNCOV
116
                // update that falls within the specified horizon.
×
UNCOV
117
                chansInHorizon := c.graph.ChanUpdatesInHorizon(
×
UNCOV
118
                        startTime, endTime,
×
UNCOV
119
                )
×
UNCOV
120

×
UNCOV
121
                for channel, err := range chansInHorizon {
×
UNCOV
122
                        if err != nil {
×
123
                                yield(nil, err)
×
124
                                return
×
125
                        }
×
126
                        // If the channel hasn't been fully advertised yet, or
127
                        // is a private channel, then we'll skip it as we can't
128
                        // construct a full authentication proof if one is
129
                        // requested.
UNCOV
130
                        if channel.Info.AuthProof == nil {
×
UNCOV
131
                                continue
×
132
                        }
133

134
                        //nolint:ll
UNCOV
135
                        chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
×
UNCOV
136
                                channel.Info.AuthProof, channel.Info,
×
UNCOV
137
                                channel.Policy1, channel.Policy2,
×
UNCOV
138
                        )
×
UNCOV
139
                        if err != nil {
×
140
                                if !yield(nil, err) {
×
141
                                        return
×
142
                                }
×
143

144
                                continue
×
145
                        }
146

UNCOV
147
                        if !yield(chanAnn, nil) {
×
148
                                return
×
149
                        }
×
150

151
                        // We don't want to send channel updates that don't
152
                        // conform to the spec (anymore), so check to make sure
153
                        // that these channel updates are valid before yielding
154
                        // them.
UNCOV
155
                        if edge1 != nil {
×
UNCOV
156
                                err := netann.ValidateChannelUpdateFields(
×
UNCOV
157
                                        0, edge1,
×
UNCOV
158
                                )
×
UNCOV
159
                                if err != nil {
×
160
                                        log.Errorf("not sending invalid "+
×
161
                                                "channel update %v: %v",
×
162
                                                edge1, err)
×
UNCOV
163
                                } else if !yield(edge1, nil) {
×
164
                                        return
×
165
                                }
×
166
                        }
UNCOV
167
                        if edge2 != nil {
×
UNCOV
168
                                err := netann.ValidateChannelUpdateFields(
×
UNCOV
169
                                        0, edge2,
×
UNCOV
170
                                )
×
UNCOV
171
                                if err != nil {
×
172
                                        log.Errorf("not sending invalid "+
×
173
                                                "channel update %v: %v", edge2,
×
174
                                                err)
×
UNCOV
175
                                } else if !yield(edge2, nil) {
×
176
                                        return
×
177
                                }
×
178
                        }
179
                }
180

181
                // Next, we'll send out all the node announcements that have an
182
                // update within the horizon as well. We send these second to
183
                // ensure that they follow any active channels they have.
UNCOV
184
                nodeAnnsInHorizon := c.graph.NodeUpdatesInHorizon(
×
UNCOV
185
                        startTime, endTime, graphdb.WithIterPublicNodesOnly(),
×
UNCOV
186
                )
×
UNCOV
187
                for nodeAnn, err := range nodeAnnsInHorizon {
×
UNCOV
188
                        if err != nil {
×
189
                                yield(nil, err)
×
190
                                return
×
191
                        }
×
UNCOV
192
                        nodeUpdate, err := nodeAnn.NodeAnnouncement(true)
×
UNCOV
193
                        if err != nil {
×
194
                                if !yield(nil, err) {
×
195
                                        return
×
196
                                }
×
197

198
                                continue
×
199
                        }
200

UNCOV
201
                        if !yield(nodeUpdate, nil) {
×
202
                                return
×
203
                        }
×
204
                }
205
        }
206
}
207

208
// FilterKnownChanIDs takes a target chain, and a set of channel ID's, and
209
// returns a filtered set of chan ID's. This filtered set of chan ID's
210
// represents the ID's that we don't know of which were in the passed superSet.
211
//
212
// NOTE: This is part of the ChannelGraphTimeSeries interface.
213
func (c *ChanSeries) FilterKnownChanIDs(_ chainhash.Hash,
214
        superSet []graphdb.ChannelUpdateInfo,
215
        isZombieChan func(time.Time, time.Time) bool) (
UNCOV
216
        []lnwire.ShortChannelID, error) {
×
UNCOV
217

×
UNCOV
218
        newChanIDs, err := c.graph.FilterKnownChanIDs(superSet, isZombieChan)
×
UNCOV
219
        if err != nil {
×
220
                return nil, err
×
221
        }
×
222

UNCOV
223
        filteredIDs := make([]lnwire.ShortChannelID, 0, len(newChanIDs))
×
UNCOV
224
        for _, chanID := range newChanIDs {
×
UNCOV
225
                filteredIDs = append(
×
UNCOV
226
                        filteredIDs, lnwire.NewShortChanIDFromInt(chanID),
×
UNCOV
227
                )
×
UNCOV
228
        }
×
229

UNCOV
230
        return filteredIDs, nil
×
231
}
232

233
// FilterChannelRange returns the set of channels that we created between the
234
// start height and the end height. The channel IDs are grouped by their common
235
// block height. We'll use this respond to a remote peer's QueryChannelRange
236
// message.
237
//
238
// NOTE: This is part of the ChannelGraphTimeSeries interface.
239
func (c *ChanSeries) FilterChannelRange(_ chainhash.Hash, startHeight,
240
        endHeight uint32, withTimestamps bool) ([]graphdb.BlockChannelRange,
UNCOV
241
        error) {
×
UNCOV
242

×
UNCOV
243
        return c.graph.FilterChannelRange(
×
UNCOV
244
                startHeight, endHeight, withTimestamps,
×
UNCOV
245
        )
×
UNCOV
246
}
×
247

248
// FetchChanAnns returns a full set of channel announcements as well as their
249
// updates that match the set of specified short channel ID's.  We'll use this
250
// to reply to a QueryShortChanIDs message sent by a remote peer. The response
251
// will contain a unique set of ChannelAnnouncements, the latest ChannelUpdate
252
// for each of the announcements, and a unique set of NodeAnnouncements.
253
// Invalid node announcements are skipped and logged for debugging purposes.
254
//
255
// NOTE: This is part of the ChannelGraphTimeSeries interface.
256
func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
UNCOV
257
        shortChanIDs []lnwire.ShortChannelID) ([]lnwire.Message, error) {
×
UNCOV
258

×
UNCOV
259
        chanIDs := make([]uint64, 0, len(shortChanIDs))
×
UNCOV
260
        for _, chanID := range shortChanIDs {
×
UNCOV
261
                chanIDs = append(chanIDs, chanID.ToUint64())
×
UNCOV
262
        }
×
263

UNCOV
264
        channels, err := c.graph.FetchChanInfos(chanIDs)
×
UNCOV
265
        if err != nil {
×
266
                return nil, err
×
267
        }
×
268

269
        // We'll use this map to ensure we don't send the same node
270
        // announcement more than one time as one node may have many channel
271
        // anns we'll need to send.
UNCOV
272
        nodePubsSent := make(map[route.Vertex]struct{})
×
UNCOV
273

×
UNCOV
274
        chanAnns := make([]lnwire.Message, 0, len(channels)*3)
×
UNCOV
275
        for _, channel := range channels {
×
UNCOV
276
                // If the channel doesn't have an authentication proof, then we
×
UNCOV
277
                // won't send it over as it may not yet be finalized, or be a
×
UNCOV
278
                // non-advertised channel.
×
UNCOV
279
                if channel.Info.AuthProof == nil {
×
280
                        continue
×
281
                }
282

UNCOV
283
                chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
×
UNCOV
284
                        channel.Info.AuthProof, channel.Info, channel.Policy1,
×
UNCOV
285
                        channel.Policy2,
×
UNCOV
286
                )
×
UNCOV
287
                if err != nil {
×
288
                        return nil, err
×
289
                }
×
290

UNCOV
291
                chanAnns = append(chanAnns, chanAnn)
×
UNCOV
292
                if edge1 != nil {
×
UNCOV
293
                        chanAnns = append(chanAnns, edge1)
×
UNCOV
294

×
UNCOV
295
                        // If this edge has a validated node announcement, that
×
UNCOV
296
                        // we haven't yet sent, then we'll send that as well.
×
UNCOV
297
                        nodePub := channel.Node2.PubKeyBytes
×
UNCOV
298
                        hasNodeAnn := channel.Node2.HaveNodeAnnouncement
×
UNCOV
299
                        if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
×
UNCOV
300
                                nodeAnn, err := channel.Node2.NodeAnnouncement(
×
UNCOV
301
                                        true,
×
UNCOV
302
                                )
×
UNCOV
303
                                if err != nil {
×
304
                                        return nil, err
×
305
                                }
×
306

UNCOV
307
                                err = netann.ValidateNodeAnnFields(nodeAnn)
×
UNCOV
308
                                if err != nil {
×
309
                                        log.Debugf("Skipping forwarding "+
×
310
                                                "invalid node announcement "+
×
311
                                                "%x: %v", nodeAnn.NodeID, err)
×
UNCOV
312
                                } else {
×
UNCOV
313
                                        chanAnns = append(chanAnns, nodeAnn)
×
UNCOV
314
                                        nodePubsSent[nodePub] = struct{}{}
×
UNCOV
315
                                }
×
316
                        }
317
                }
UNCOV
318
                if edge2 != nil {
×
UNCOV
319
                        chanAnns = append(chanAnns, edge2)
×
UNCOV
320

×
UNCOV
321
                        // If this edge has a validated node announcement, that
×
UNCOV
322
                        // we haven't yet sent, then we'll send that as well.
×
UNCOV
323
                        nodePub := channel.Node1.PubKeyBytes
×
UNCOV
324
                        hasNodeAnn := channel.Node1.HaveNodeAnnouncement
×
UNCOV
325
                        if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
×
UNCOV
326
                                nodeAnn, err := channel.Node1.NodeAnnouncement(
×
UNCOV
327
                                        true,
×
UNCOV
328
                                )
×
UNCOV
329
                                if err != nil {
×
330
                                        return nil, err
×
331
                                }
×
332

UNCOV
333
                                err = netann.ValidateNodeAnnFields(nodeAnn)
×
UNCOV
334
                                if err != nil {
×
335
                                        log.Debugf("Skipping forwarding "+
×
336
                                                "invalid node announcement "+
×
337
                                                "%x: %v", nodeAnn.NodeID, err)
×
UNCOV
338
                                } else {
×
UNCOV
339
                                        chanAnns = append(chanAnns, nodeAnn)
×
UNCOV
340
                                        nodePubsSent[nodePub] = struct{}{}
×
UNCOV
341
                                }
×
342
                        }
343
                }
344
        }
345

UNCOV
346
        return chanAnns, nil
×
347
}
348

349
// FetchChanUpdates returns the latest channel update messages for the
350
// specified short channel ID. If no channel updates are known for the channel,
351
// then an empty slice will be returned.
352
//
353
// NOTE: This is part of the ChannelGraphTimeSeries interface.
354
func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash,
UNCOV
355
        shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, error) {
×
UNCOV
356

×
UNCOV
357
        chanInfo, e1, e2, err := c.graph.FetchChannelEdgesByID(
×
UNCOV
358
                shortChanID.ToUint64(),
×
UNCOV
359
        )
×
UNCOV
360
        if err != nil {
×
361
                return nil, err
×
362
        }
×
363

UNCOV
364
        chanUpdates := make([]*lnwire.ChannelUpdate1, 0, 2)
×
UNCOV
365
        if e1 != nil {
×
UNCOV
366
                chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e1)
×
UNCOV
367
                if err != nil {
×
368
                        return nil, err
×
369
                }
×
370

UNCOV
371
                chanUpdates = append(chanUpdates, chanUpdate)
×
372
        }
UNCOV
373
        if e2 != nil {
×
UNCOV
374
                chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e2)
×
UNCOV
375
                if err != nil {
×
376
                        return nil, err
×
377
                }
×
378

UNCOV
379
                chanUpdates = append(chanUpdates, chanUpdate)
×
380
        }
381

UNCOV
382
        return chanUpdates, nil
×
383
}
384

385
// A compile-time assertion to ensure that ChanSeries meets the
386
// ChannelGraphTimeSeries interface.
387
var _ ChannelGraphTimeSeries = (*ChanSeries)(nil)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc