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

lightningnetwork / lnd / 12312390362

13 Dec 2024 08:44AM UTC coverage: 57.458% (+8.5%) from 48.92%
12312390362

Pull #9343

github

ellemouton
fn: rework the ContextGuard and add tests

In this commit, the ContextGuard struct is re-worked such that the
context that its new main WithCtx method provides is cancelled in sync
with a parent context being cancelled or with it's quit channel being
cancelled. Tests are added to assert the behaviour. In order for the
close of the quit channel to be consistent with the cancelling of the
derived context, the quit channel _must_ be contained internal to the
ContextGuard so that callers are only able to close the channel via the
exposed Quit method which will then take care to first cancel any
derived context that depend on the quit channel before returning.
Pull Request #9343: fn: expand the ContextGuard and add tests

101853 of 177264 relevant lines covered (57.46%)

24972.93 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
        "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 {
×
79
        return &ChanSeries{
×
80
                graph: graph,
×
81
        }
×
82
}
×
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) {
×
91
        chanID, err := c.graph.HighestChanID()
×
92
        if err != nil {
×
93
                return nil, err
×
94
        }
×
95

96
        shortChanID := lnwire.NewShortChanIDFromInt(chanID)
×
97
        return &shortChanID, nil
×
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) {
×
108

×
109
        var updates []lnwire.Message
×
110

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

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

135
                updates = append(updates, chanAnn)
×
136
                if edge1 != nil {
×
137
                        // We don't want to send channel updates that don't
×
138
                        // conform to the spec (anymore).
×
139
                        err := netann.ValidateChannelUpdateFields(0, edge1)
×
140
                        if err != nil {
×
141
                                log.Errorf("not sending invalid channel "+
×
142
                                        "update %v: %v", edge1, err)
×
143
                        } else {
×
144
                                updates = append(updates, edge1)
×
145
                        }
×
146
                }
147
                if edge2 != nil {
×
148
                        err := netann.ValidateChannelUpdateFields(0, edge2)
×
149
                        if err != nil {
×
150
                                log.Errorf("not sending invalid channel "+
×
151
                                        "update %v: %v", edge2, err)
×
152
                        } else {
×
153
                                updates = append(updates, edge2)
×
154
                        }
×
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(
×
162
                startTime, endTime,
×
163
        )
×
164
        if err != nil {
×
165
                return nil, err
×
166
        }
×
167
        for _, nodeAnn := range nodeAnnsInHorizon {
×
168
                nodeAnn := nodeAnn
×
169

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

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

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

191
                updates = append(updates, nodeUpdate)
×
192
        }
193

194
        return updates, nil
×
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) {
×
206

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

212
        filteredIDs := make([]lnwire.ShortChannelID, 0, len(newChanIDs))
×
213
        for _, chanID := range newChanIDs {
×
214
                filteredIDs = append(
×
215
                        filteredIDs, lnwire.NewShortChanIDFromInt(chanID),
×
216
                )
×
217
        }
×
218

219
        return filteredIDs, nil
×
220
}
221

222
// FilterChannelRange returns the set of channels that we created between the
223
// start height and the end height. The channel IDs are grouped by their common
224
// block height. We'll use this respond to a remote peer's QueryChannelRange
225
// message.
226
//
227
// NOTE: This is part of the ChannelGraphTimeSeries interface.
228
func (c *ChanSeries) FilterChannelRange(_ chainhash.Hash, startHeight,
229
        endHeight uint32, withTimestamps bool) ([]graphdb.BlockChannelRange,
230
        error) {
×
231

×
232
        return c.graph.FilterChannelRange(
×
233
                startHeight, endHeight, withTimestamps,
×
234
        )
×
235
}
×
236

237
// FetchChanAnns returns a full set of channel announcements as well as their
238
// updates that match the set of specified short channel ID's.  We'll use this
239
// to reply to a QueryShortChanIDs message sent by a remote peer. The response
240
// will contain a unique set of ChannelAnnouncements, the latest ChannelUpdate
241
// for each of the announcements, and a unique set of NodeAnnouncements.
242
//
243
// NOTE: This is part of the ChannelGraphTimeSeries interface.
244
func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
245
        shortChanIDs []lnwire.ShortChannelID) ([]lnwire.Message, error) {
×
246

×
247
        chanIDs := make([]uint64, 0, len(shortChanIDs))
×
248
        for _, chanID := range shortChanIDs {
×
249
                chanIDs = append(chanIDs, chanID.ToUint64())
×
250
        }
×
251

252
        channels, err := c.graph.FetchChanInfos(chanIDs)
×
253
        if err != nil {
×
254
                return nil, err
×
255
        }
×
256

257
        // We'll use this map to ensure we don't send the same node
258
        // announcement more than one time as one node may have many channel
259
        // anns we'll need to send.
260
        nodePubsSent := make(map[route.Vertex]struct{})
×
261

×
262
        chanAnns := make([]lnwire.Message, 0, len(channels)*3)
×
263
        for _, channel := range channels {
×
264
                // If the channel doesn't have an authentication proof, then we
×
265
                // won't send it over as it may not yet be finalized, or be a
×
266
                // non-advertised channel.
×
267
                if channel.Info.AuthProof == nil {
×
268
                        continue
×
269
                }
270

271
                chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
×
272
                        channel.Info.AuthProof, channel.Info, channel.Policy1,
×
273
                        channel.Policy2,
×
274
                )
×
275
                if err != nil {
×
276
                        return nil, err
×
277
                }
×
278

279
                chanAnns = append(chanAnns, chanAnn)
×
280
                if edge1 != nil {
×
281
                        chanAnns = append(chanAnns, edge1)
×
282

×
283
                        // If this edge has a validated node announcement, that
×
284
                        // we haven't yet sent, then we'll send that as well.
×
285
                        nodePub := channel.Node2.PubKeyBytes
×
286
                        hasNodeAnn := channel.Node2.HaveNodeAnnouncement
×
287
                        if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
×
288
                                nodeAnn, err := channel.Node2.NodeAnnouncement(
×
289
                                        true,
×
290
                                )
×
291
                                if err != nil {
×
292
                                        return nil, err
×
293
                                }
×
294

295
                                chanAnns = append(chanAnns, nodeAnn)
×
296
                                nodePubsSent[nodePub] = struct{}{}
×
297
                        }
298
                }
299
                if edge2 != nil {
×
300
                        chanAnns = append(chanAnns, edge2)
×
301

×
302
                        // If this edge has a validated node announcement, that
×
303
                        // we haven't yet sent, then we'll send that as well.
×
304
                        nodePub := channel.Node1.PubKeyBytes
×
305
                        hasNodeAnn := channel.Node1.HaveNodeAnnouncement
×
306
                        if _, ok := nodePubsSent[nodePub]; !ok && hasNodeAnn {
×
307
                                nodeAnn, err := channel.Node1.NodeAnnouncement(
×
308
                                        true,
×
309
                                )
×
310
                                if err != nil {
×
311
                                        return nil, err
×
312
                                }
×
313

314
                                chanAnns = append(chanAnns, nodeAnn)
×
315
                                nodePubsSent[nodePub] = struct{}{}
×
316
                        }
317
                }
318
        }
319

320
        return chanAnns, nil
×
321
}
322

323
// FetchChanUpdates returns the latest channel update messages for the
324
// specified short channel ID. If no channel updates are known for the channel,
325
// then an empty slice will be returned.
326
//
327
// NOTE: This is part of the ChannelGraphTimeSeries interface.
328
func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash,
329
        shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, error) {
×
330

×
331
        chanInfo, e1, e2, err := c.graph.FetchChannelEdgesByID(
×
332
                shortChanID.ToUint64(),
×
333
        )
×
334
        if err != nil {
×
335
                return nil, err
×
336
        }
×
337

338
        chanUpdates := make([]*lnwire.ChannelUpdate1, 0, 2)
×
339
        if e1 != nil {
×
340
                chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e1)
×
341
                if err != nil {
×
342
                        return nil, err
×
343
                }
×
344

345
                chanUpdates = append(chanUpdates, chanUpdate)
×
346
        }
347
        if e2 != nil {
×
348
                chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e2)
×
349
                if err != nil {
×
350
                        return nil, err
×
351
                }
×
352

353
                chanUpdates = append(chanUpdates, chanUpdate)
×
354
        }
355

356
        return chanUpdates, nil
×
357
}
358

359
// A compile-time assertion to ensure that ChanSeries meets the
360
// ChannelGraphTimeSeries interface.
361
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