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

lightningnetwork / lnd / 13397587988

18 Feb 2025 06:29PM UTC coverage: 58.785% (+0.1%) from 58.636%
13397587988

Pull #9522

github

ellemouton
graph/db: make sure to test the knownZombies returnd by FilterKnownChanIDs
Pull Request #9522: discovery/graph: move business logic out of CRUD layer

11 of 42 new or added lines in 2 files covered. (26.19%)

45 existing lines in 10 files now uncovered.

136084 of 231496 relevant lines covered (58.78%)

19295.96 hits per line

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

71.56
/discovery/chan_series.go
1
package discovery
2

3
import (
4
        "time"
5

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

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

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

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

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

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

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

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

76
// NewChanSeries constructs a new ChanSeries backed by a channeldb.ChannelGraph.
77
// The returned ChanSeries implements the ChannelGraphTimeSeries interface.
78
func NewChanSeries(graph *graphdb.ChannelGraph) *ChanSeries {
3✔
79
        return &ChanSeries{
3✔
80
                graph: graph,
3✔
81
        }
3✔
82
}
3✔
83

84
// HighestChanID should return is the channel ID of the channel we know of
85
// that's furthest in the target chain. This channel will have a block height
86
// that's close to the current tip of the main chain as we know it.  We'll use
87
// this to start our QueryChannelRange dance with the remote node.
88
//
89
// NOTE: This is part of the ChannelGraphTimeSeries interface.
90
func (c *ChanSeries) HighestChanID(chain chainhash.Hash) (*lnwire.ShortChannelID, error) {
3✔
91
        chanID, err := c.graph.HighestChanID()
3✔
92
        if err != nil {
3✔
93
                return nil, err
×
94
        }
×
95

96
        shortChanID := lnwire.NewShortChanIDFromInt(chanID)
3✔
97
        return &shortChanID, nil
3✔
98
}
99

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

3✔
109
        var updates []lnwire.Message
3✔
110

3✔
111
        // First, we'll query for all the set of channels that have an update
3✔
112
        // that falls within the specified horizon.
3✔
113
        chansInHorizon, err := c.graph.ChanUpdatesInHorizon(
3✔
114
                startTime, endTime,
3✔
115
        )
3✔
116
        if err != nil {
3✔
117
                return nil, err
×
118
        }
×
119
        for _, channel := range chansInHorizon {
6✔
120
                // If the channel hasn't been fully advertised yet, or is a
3✔
121
                // private channel, then we'll skip it as we can't construct a
3✔
122
                // full authentication proof if one is requested.
3✔
123
                if channel.Info.AuthProof == nil {
6✔
124
                        continue
3✔
125
                }
126

127
                chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
3✔
128
                        channel.Info.AuthProof, channel.Info, channel.Policy1,
3✔
129
                        channel.Policy2,
3✔
130
                )
3✔
131
                if err != nil {
3✔
132
                        return nil, err
×
133
                }
×
134

135
                updates = append(updates, chanAnn)
3✔
136
                if edge1 != nil {
6✔
137
                        // We don't want to send channel updates that don't
3✔
138
                        // conform to the spec (anymore).
3✔
139
                        err := netann.ValidateChannelUpdateFields(0, edge1)
3✔
140
                        if err != nil {
3✔
141
                                log.Errorf("not sending invalid channel "+
×
142
                                        "update %v: %v", edge1, err)
×
143
                        } else {
3✔
144
                                updates = append(updates, edge1)
3✔
145
                        }
3✔
146
                }
147
                if edge2 != nil {
6✔
148
                        err := netann.ValidateChannelUpdateFields(0, edge2)
3✔
149
                        if err != nil {
3✔
150
                                log.Errorf("not sending invalid channel "+
×
151
                                        "update %v: %v", edge2, err)
×
152
                        } else {
3✔
153
                                updates = append(updates, edge2)
3✔
154
                        }
3✔
155
                }
156
        }
157

158
        // Next, we'll send out all the node announcements that have an update
159
        // within the horizon as well. We send these second to ensure that they
160
        // follow any active channels they have.
161
        nodeAnnsInHorizon, err := c.graph.NodeUpdatesInHorizon(
3✔
162
                startTime, endTime,
3✔
163
        )
3✔
164
        if err != nil {
3✔
165
                return nil, err
×
166
        }
×
167
        for _, nodeAnn := range nodeAnnsInHorizon {
6✔
168
                nodeAnn := nodeAnn
3✔
169

3✔
170
                // Ensure we only forward nodes that are publicly advertised to
3✔
171
                // prevent leaking information about nodes.
3✔
172
                isNodePublic, err := c.graph.IsPublicNode(nodeAnn.PubKeyBytes)
3✔
173
                if err != nil {
3✔
174
                        log.Errorf("Unable to determine if node %x is "+
×
175
                                "advertised: %v", nodeAnn.PubKeyBytes, err)
×
176
                        continue
×
177
                }
178

179
                if !isNodePublic {
6✔
180
                        log.Tracef("Skipping forwarding announcement for "+
3✔
181
                                "node %x due to being unadvertised",
3✔
182
                                nodeAnn.PubKeyBytes)
3✔
183
                        continue
3✔
184
                }
185

186
                nodeUpdate, err := nodeAnn.NodeAnnouncement(true)
3✔
187
                if err != nil {
3✔
188
                        return nil, err
×
189
                }
×
190

191
                updates = append(updates, nodeUpdate)
3✔
192
        }
193

194
        return updates, nil
3✔
195
}
196

197
// FilterKnownChanIDs takes a target chain, and a set of channel ID's, and
198
// returns a filtered set of chan ID's. This filtered set of chan ID's
199
// represents the ID's that we don't know of which were in the passed superSet.
200
//
201
// NOTE: This is part of the ChannelGraphTimeSeries interface.
202
func (c *ChanSeries) FilterKnownChanIDs(_ chainhash.Hash,
203
        superSet []graphdb.ChannelUpdateInfo,
204
        isZombieChan func(time.Time, time.Time) bool) (
205
        []lnwire.ShortChannelID, error) {
3✔
206

3✔
207
        newChanIDs, knownZombies, err := c.graph.FilterKnownChanIDs(superSet)
3✔
208
        if err != nil {
3✔
209
                return nil, err
×
210
        }
×
211

212
        for _, info := range knownZombies {
3✔
NEW
213
                // TODO(ziggie): Make sure that for the strict pruning case we
×
NEW
214
                // compare the pubkeys and whether the right timestamp is not
×
NEW
215
                // older than the `ChannelPruneExpiry`.
×
NEW
216
                //
×
NEW
217
                // NOTE: The timestamp data has no verification attached to it
×
NEW
218
                // in the `ReplyChannelRange` msg so we are trusting this data
×
NEW
219
                // at this point. However it is not critical because we are
×
NEW
220
                // just removing the channel from the db when the timestamps are
×
NEW
221
                // more recent. During the querying of the gossip msg
×
NEW
222
                // verification happens as usual. However we should start
×
NEW
223
                // punishing peers when they don't provide us honest data ?
×
NEW
224
                isStillZombie := isZombieChan(
×
NEW
225
                        info.Node1UpdateTimestamp, info.Node2UpdateTimestamp,
×
NEW
226
                )
×
NEW
227

×
NEW
228
                // If we have marked it as a zombie but the latest update
×
NEW
229
                // timestamps could bring it back from the dead, then we mark
×
NEW
230
                // it alive, and we let it be added to the set of IDs to query
×
NEW
231
                // our peer for.
×
NEW
232
                if !isStillZombie {
×
NEW
233
                        err = c.graph.MarkEdgeLive(
×
NEW
234
                                info.ShortChannelID.ToUint64(),
×
NEW
235
                        )
×
NEW
236
                        if err != nil {
×
NEW
237
                                return nil, err
×
NEW
238
                        }
×
239
                }
240
        }
241

242
        filteredIDs := make([]lnwire.ShortChannelID, 0, len(newChanIDs))
3✔
243
        for _, chanID := range newChanIDs {
6✔
244
                filteredIDs = append(
3✔
245
                        filteredIDs, lnwire.NewShortChanIDFromInt(chanID),
3✔
246
                )
3✔
247
        }
3✔
248

249
        return filteredIDs, nil
3✔
250
}
251

252
// FilterChannelRange returns the set of channels that we created between the
253
// start height and the end height. The channel IDs are grouped by their common
254
// block height. We'll use this respond to a remote peer's QueryChannelRange
255
// message.
256
//
257
// NOTE: This is part of the ChannelGraphTimeSeries interface.
258
func (c *ChanSeries) FilterChannelRange(_ chainhash.Hash, startHeight,
259
        endHeight uint32, withTimestamps bool) ([]graphdb.BlockChannelRange,
260
        error) {
3✔
261

3✔
262
        return c.graph.FilterChannelRange(
3✔
263
                startHeight, endHeight, withTimestamps,
3✔
264
        )
3✔
265
}
3✔
266

267
// FetchChanAnns returns a full set of channel announcements as well as their
268
// updates that match the set of specified short channel ID's.  We'll use this
269
// to reply to a QueryShortChanIDs message sent by a remote peer. The response
270
// will contain a unique set of ChannelAnnouncements, the latest ChannelUpdate
271
// for each of the announcements, and a unique set of NodeAnnouncements.
272
//
273
// NOTE: This is part of the ChannelGraphTimeSeries interface.
274
func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
275
        shortChanIDs []lnwire.ShortChannelID) ([]lnwire.Message, error) {
3✔
276

3✔
277
        chanIDs := make([]uint64, 0, len(shortChanIDs))
3✔
278
        for _, chanID := range shortChanIDs {
6✔
279
                chanIDs = append(chanIDs, chanID.ToUint64())
3✔
280
        }
3✔
281

282
        channels, err := c.graph.FetchChanInfos(chanIDs)
3✔
283
        if err != nil {
3✔
284
                return nil, err
×
285
        }
×
286

287
        // We'll use this map to ensure we don't send the same node
288
        // announcement more than one time as one node may have many channel
289
        // anns we'll need to send.
290
        nodePubsSent := make(map[route.Vertex]struct{})
3✔
291

3✔
292
        chanAnns := make([]lnwire.Message, 0, len(channels)*3)
3✔
293
        for _, channel := range channels {
6✔
294
                // If the channel doesn't have an authentication proof, then we
3✔
295
                // won't send it over as it may not yet be finalized, or be a
3✔
296
                // non-advertised channel.
3✔
297
                if channel.Info.AuthProof == nil {
3✔
298
                        continue
×
299
                }
300

301
                chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
3✔
302
                        channel.Info.AuthProof, channel.Info, channel.Policy1,
3✔
303
                        channel.Policy2,
3✔
304
                )
3✔
305
                if err != nil {
3✔
306
                        return nil, err
×
307
                }
×
308

309
                chanAnns = append(chanAnns, chanAnn)
3✔
310
                if edge1 != nil {
6✔
311
                        chanAnns = append(chanAnns, edge1)
3✔
312

3✔
313
                        // If this edge has a validated node announcement, that
3✔
314
                        // we haven't yet sent, then we'll send that as well.
3✔
315
                        nodePub := channel.Node2.PubKeyBytes
3✔
316
                        hasNodeAnn := channel.Node2.HaveNodeAnnouncement
3✔
317
                        if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
6✔
318
                                nodeAnn, err := channel.Node2.NodeAnnouncement(
3✔
319
                                        true,
3✔
320
                                )
3✔
321
                                if err != nil {
3✔
322
                                        return nil, err
×
323
                                }
×
324

325
                                chanAnns = append(chanAnns, nodeAnn)
3✔
326
                                nodePubsSent[nodePub] = struct{}{}
3✔
327
                        }
328
                }
329
                if edge2 != nil {
6✔
330
                        chanAnns = append(chanAnns, edge2)
3✔
331

3✔
332
                        // If this edge has a validated node announcement, that
3✔
333
                        // we haven't yet sent, then we'll send that as well.
3✔
334
                        nodePub := channel.Node1.PubKeyBytes
3✔
335
                        hasNodeAnn := channel.Node1.HaveNodeAnnouncement
3✔
336
                        if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
6✔
337
                                nodeAnn, err := channel.Node1.NodeAnnouncement(
3✔
338
                                        true,
3✔
339
                                )
3✔
340
                                if err != nil {
3✔
341
                                        return nil, err
×
342
                                }
×
343

344
                                chanAnns = append(chanAnns, nodeAnn)
3✔
345
                                nodePubsSent[nodePub] = struct{}{}
3✔
346
                        }
347
                }
348
        }
349

350
        return chanAnns, nil
3✔
351
}
352

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

3✔
361
        chanInfo, e1, e2, err := c.graph.FetchChannelEdgesByID(
3✔
362
                shortChanID.ToUint64(),
3✔
363
        )
3✔
364
        if err != nil {
3✔
365
                return nil, err
×
366
        }
×
367

368
        chanUpdates := make([]*lnwire.ChannelUpdate1, 0, 2)
3✔
369
        if e1 != nil {
6✔
370
                chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e1)
3✔
371
                if err != nil {
3✔
372
                        return nil, err
×
373
                }
×
374

375
                chanUpdates = append(chanUpdates, chanUpdate)
3✔
376
        }
377
        if e2 != nil {
6✔
378
                chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e2)
3✔
379
                if err != nil {
3✔
380
                        return nil, err
×
381
                }
×
382

383
                chanUpdates = append(chanUpdates, chanUpdate)
3✔
384
        }
385

386
        return chanUpdates, nil
3✔
387
}
388

389
// A compile-time assertion to ensure that ChanSeries meets the
390
// ChannelGraphTimeSeries interface.
391
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