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

lightningnetwork / lnd / 9915780197

13 Jul 2024 12:30AM UTC coverage: 49.268% (-9.1%) from 58.413%
9915780197

push

github

web-flow
Merge pull request #8653 from ProofOfKeags/fn-prim

DynComms [0/n]: `fn` package additions

92837 of 188433 relevant lines covered (49.27%)

1.55 hits per line

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

80.74
/aliasmgr/aliasmgr.go
1
package aliasmgr
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6
        "sync"
7

8
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
9
        "github.com/lightningnetwork/lnd/kvdb"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
var (
14
        // aliasBucket stores aliases as keys and their base SCIDs as values.
15
        // This is used to populate the maps that the Manager uses. The keys
16
        // are alias SCIDs and the values are their respective base SCIDs. This
17
        // is used instead of the other way around (base -> alias...) because
18
        // updating an alias would require fetching all the existing aliases,
19
        // adding another one, and then flushing the write to disk. This is
20
        // inefficient compared to N 1:1 mappings at the cost of marginally
21
        // more disk space.
22
        aliasBucket = []byte("alias-bucket")
23

24
        // confirmedBucket stores whether or not a given base SCID should no
25
        // longer have entries in the ToBase maps. The key is the SCID that is
26
        // confirmed with 6 confirmations and is public, and the value is
27
        // empty.
28
        confirmedBucket = []byte("base-bucket")
29

30
        // aliasAllocBucket is a root-level bucket that stores the last alias
31
        // that was allocated. It is used to allocate a new alias when
32
        // requested.
33
        aliasAllocBucket = []byte("alias-alloc-bucket")
34

35
        // lastAliasKey is a key in the aliasAllocBucket whose value is the
36
        // last allocated alias ShortChannelID. This will be updated upon calls
37
        // to RequestAlias.
38
        lastAliasKey = []byte("last-alias-key")
39

40
        // invoiceAliasBucket is a root-level bucket that stores the alias
41
        // SCIDs that our peers send us in the channel_ready TLV. The keys are
42
        // the ChannelID generated from the FundingOutpoint and the values are
43
        // the remote peer's alias SCID.
44
        invoiceAliasBucket = []byte("invoice-alias-bucket")
45

46
        // byteOrder denotes the byte order of database (de)-serialization
47
        // operations.
48
        byteOrder = binary.BigEndian
49

50
        // startBlockHeight is the starting block height of the alias range.
51
        startingBlockHeight = 16_000_000
52

53
        // endBlockHeight is the ending block height of the alias range.
54
        endBlockHeight = 16_250_000
55

56
        // StartingAlias is the first alias ShortChannelID that will get
57
        // assigned by RequestAlias. The starting BlockHeight is chosen so that
58
        // legitimate SCIDs in integration tests aren't mistaken for an alias.
59
        StartingAlias = lnwire.ShortChannelID{
60
                BlockHeight: uint32(startingBlockHeight),
61
                TxIndex:     0,
62
                TxPosition:  0,
63
        }
64

65
        // errNoBase is returned when a base SCID isn't found.
66
        errNoBase = fmt.Errorf("no base found")
67

68
        // errNoPeerAlias is returned when the peer's alias for a given
69
        // channel is not found.
70
        errNoPeerAlias = fmt.Errorf("no peer alias found")
71
)
72

73
// Manager is a struct that handles aliases for LND. It has an underlying
74
// database that can allocate aliases for channels, stores the peer's last
75
// alias for use in our hop hints, and contains mappings that both the Switch
76
// and Gossiper use.
77
type Manager struct {
78
        backend kvdb.Backend
79

80
        // baseToSet is a mapping from the "base" SCID to the set of aliases
81
        // for this channel. This mapping includes all channels that
82
        // negotiated the option-scid-alias feature bit.
83
        baseToSet map[lnwire.ShortChannelID][]lnwire.ShortChannelID
84

85
        // aliasToBase is a mapping that maps all aliases for a given channel
86
        // to its base SCID. This is only used for channels that have
87
        // negotiated option-scid-alias feature bit.
88
        aliasToBase map[lnwire.ShortChannelID]lnwire.ShortChannelID
89

90
        // peerAlias is a cache for the alias SCIDs that our peers send us in
91
        // the channel_ready TLV. The keys are the ChannelID generated from
92
        // the FundingOutpoint and the values are the remote peer's alias SCID.
93
        // The values should match the ones stored in the "invoice-alias-bucket"
94
        // bucket.
95
        peerAlias map[lnwire.ChannelID]lnwire.ShortChannelID
96

97
        sync.RWMutex
98
}
99

100
// NewManager initializes an alias Manager from the passed database backend.
101
func NewManager(db kvdb.Backend) (*Manager, error) {
3✔
102
        m := &Manager{backend: db}
3✔
103
        m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
3✔
104
        m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
105
        m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
106

3✔
107
        err := m.populateMaps()
3✔
108
        return m, err
3✔
109
}
3✔
110

111
// populateMaps reads the database state and populates the maps.
112
func (m *Manager) populateMaps() error {
3✔
113
        // This map tracks the base SCIDs that are confirmed and don't need to
3✔
114
        // have entries in the *ToBase mappings as they won't be used in the
3✔
115
        // gossiper.
3✔
116
        baseConfMap := make(map[lnwire.ShortChannelID]struct{})
3✔
117

3✔
118
        // This map caches what is found in the database and is used to
3✔
119
        // populate the Manager's actual maps.
3✔
120
        aliasMap := make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
121

3✔
122
        // This map caches the ChannelID/alias SCIDs stored in the database and
3✔
123
        // is used to populate the Manager's cache.
3✔
124
        peerAliasMap := make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
125

3✔
126
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
127
                baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
3✔
128
                if err != nil {
3✔
129
                        return err
×
130
                }
×
131

132
                err = baseConfBucket.ForEach(func(k, v []byte) error {
6✔
133
                        // The key will the base SCID and the value will be
3✔
134
                        // empty. Existence in the bucket means the SCID is
3✔
135
                        // confirmed.
3✔
136
                        baseScid := lnwire.NewShortChanIDFromInt(
3✔
137
                                byteOrder.Uint64(k),
3✔
138
                        )
3✔
139
                        baseConfMap[baseScid] = struct{}{}
3✔
140
                        return nil
3✔
141
                })
3✔
142
                if err != nil {
3✔
143
                        return err
×
144
                }
×
145

146
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
147
                if err != nil {
3✔
148
                        return err
×
149
                }
×
150

151
                err = aliasToBaseBucket.ForEach(func(k, v []byte) error {
6✔
152
                        // The key will be the alias SCID and the value will be
3✔
153
                        // the base SCID.
3✔
154
                        aliasScid := lnwire.NewShortChanIDFromInt(
3✔
155
                                byteOrder.Uint64(k),
3✔
156
                        )
3✔
157
                        baseScid := lnwire.NewShortChanIDFromInt(
3✔
158
                                byteOrder.Uint64(v),
3✔
159
                        )
3✔
160
                        aliasMap[aliasScid] = baseScid
3✔
161
                        return nil
3✔
162
                })
3✔
163
                if err != nil {
3✔
164
                        return err
×
165
                }
×
166

167
                invAliasBucket, err := tx.CreateTopLevelBucket(
3✔
168
                        invoiceAliasBucket,
3✔
169
                )
3✔
170
                if err != nil {
3✔
171
                        return err
×
172
                }
×
173

174
                err = invAliasBucket.ForEach(func(k, v []byte) error {
6✔
175
                        var chanID lnwire.ChannelID
3✔
176
                        copy(chanID[:], k)
3✔
177
                        alias := lnwire.NewShortChanIDFromInt(
3✔
178
                                byteOrder.Uint64(v),
3✔
179
                        )
3✔
180

3✔
181
                        peerAliasMap[chanID] = alias
3✔
182

3✔
183
                        return nil
3✔
184
                })
3✔
185

186
                return err
3✔
187
        }, func() {
3✔
188
                baseConfMap = make(map[lnwire.ShortChannelID]struct{})
3✔
189
                aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
190
                peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
191
        })
3✔
192
        if err != nil {
3✔
193
                return err
×
194
        }
×
195

196
        // Populate the baseToSet map regardless if the baseSCID is marked as
197
        // public with 6 confirmations.
198
        for aliasSCID, baseSCID := range aliasMap {
6✔
199
                m.baseToSet[baseSCID] = append(m.baseToSet[baseSCID], aliasSCID)
3✔
200

3✔
201
                // Skip if baseSCID is in the baseConfMap.
3✔
202
                if _, ok := baseConfMap[baseSCID]; ok {
6✔
203
                        continue
3✔
204
                }
205

206
                m.aliasToBase[aliasSCID] = baseSCID
3✔
207
        }
208

209
        // Populate the peer alias cache.
210
        m.peerAlias = peerAliasMap
3✔
211

3✔
212
        return nil
3✔
213
}
214

215
// AddLocalAlias adds a database mapping from the passed alias to the passed
216
// base SCID. The gossip boolean marks whether or not to create a mapping
217
// that the gossiper will use. It is set to false for the upgrade path where
218
// the feature-bit is toggled on and there are existing channels.
219
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
220
        gossip bool) error {
3✔
221

3✔
222
        m.Lock()
3✔
223
        defer m.Unlock()
3✔
224

3✔
225
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
226
                // If the caller does not want to allow the alias to be used
3✔
227
                // for a channel update, we'll mark it in the baseConfBucket.
3✔
228
                if !gossip {
6✔
229
                        var baseGossipBytes [8]byte
3✔
230
                        byteOrder.PutUint64(
3✔
231
                                baseGossipBytes[:], baseScid.ToUint64(),
3✔
232
                        )
3✔
233

3✔
234
                        confBucket, err := tx.CreateTopLevelBucket(
3✔
235
                                confirmedBucket,
3✔
236
                        )
3✔
237
                        if err != nil {
3✔
238
                                return err
×
239
                        }
×
240

241
                        err = confBucket.Put(baseGossipBytes[:], []byte{})
3✔
242
                        if err != nil {
3✔
243
                                return err
×
244
                        }
×
245
                }
246

247
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
248
                if err != nil {
3✔
249
                        return err
×
250
                }
×
251

252
                var (
3✔
253
                        aliasBytes [8]byte
3✔
254
                        baseBytes  [8]byte
3✔
255
                )
3✔
256

3✔
257
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
3✔
258
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
3✔
259
                return aliasToBaseBucket.Put(aliasBytes[:], baseBytes[:])
3✔
260
        }, func() {})
3✔
261
        if err != nil {
3✔
262
                return err
×
263
        }
×
264

265
        // Update the aliasToBase and baseToSet maps.
266
        m.baseToSet[baseScid] = append(m.baseToSet[baseScid], alias)
3✔
267

3✔
268
        // Only store the gossiper map if gossip is true.
3✔
269
        if gossip {
6✔
270
                m.aliasToBase[alias] = baseScid
3✔
271
        }
3✔
272

273
        return nil
3✔
274
}
275

276
// GetAliases fetches the set of aliases stored under a given base SCID from
277
// write-through caches.
278
func (m *Manager) GetAliases(
279
        base lnwire.ShortChannelID) []lnwire.ShortChannelID {
3✔
280

3✔
281
        m.RLock()
3✔
282
        defer m.RUnlock()
3✔
283

3✔
284
        aliasSet, ok := m.baseToSet[base]
3✔
285
        if ok {
6✔
286
                // Copy the found alias slice.
3✔
287
                setCopy := make([]lnwire.ShortChannelID, len(aliasSet))
3✔
288
                copy(setCopy, aliasSet)
3✔
289
                return setCopy
3✔
290
        }
3✔
291

292
        return nil
3✔
293
}
294

295
// FindBaseSCID finds the base SCID for a given alias. This is used in the
296
// gossiper to find the correct SCID to lookup in the graph database.
297
func (m *Manager) FindBaseSCID(
298
        alias lnwire.ShortChannelID) (lnwire.ShortChannelID, error) {
3✔
299

3✔
300
        m.RLock()
3✔
301
        defer m.RUnlock()
3✔
302

3✔
303
        base, ok := m.aliasToBase[alias]
3✔
304
        if ok {
6✔
305
                return base, nil
3✔
306
        }
3✔
307

308
        return lnwire.ShortChannelID{}, errNoBase
3✔
309
}
310

311
// DeleteSixConfs removes a mapping for the gossiper once six confirmations
312
// have been reached and the channel is public. At this point, only the
313
// confirmed SCID should be used.
314
func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
3✔
315
        m.Lock()
3✔
316
        defer m.Unlock()
3✔
317

3✔
318
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
319
                baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
3✔
320
                if err != nil {
3✔
321
                        return err
×
322
                }
×
323

324
                var baseBytes [8]byte
3✔
325
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
3✔
326
                return baseConfBucket.Put(baseBytes[:], []byte{})
3✔
327
        }, func() {})
3✔
328
        if err != nil {
3✔
329
                return err
×
330
        }
×
331

332
        // Now that the database state has been updated, we'll delete all of
333
        // the aliasToBase mappings for this SCID.
334
        for alias, base := range m.aliasToBase {
6✔
335
                if base.ToUint64() == baseScid.ToUint64() {
6✔
336
                        delete(m.aliasToBase, alias)
3✔
337
                }
3✔
338
        }
339

340
        return nil
3✔
341
}
342

343
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
344
// channel_ready message.
345
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
346
        alias lnwire.ShortChannelID) error {
3✔
347

3✔
348
        m.Lock()
3✔
349
        defer m.Unlock()
3✔
350

3✔
351
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
352
                bucket, err := tx.CreateTopLevelBucket(invoiceAliasBucket)
3✔
353
                if err != nil {
3✔
354
                        return err
×
355
                }
×
356

357
                var scratch [8]byte
3✔
358
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
3✔
359
                return bucket.Put(chanID[:], scratch[:])
3✔
360
        }, func() {})
3✔
361
        if err != nil {
3✔
362
                return err
×
363
        }
×
364

365
        // Now that the database state has been updated, we can update it in
366
        // our cache.
367
        m.peerAlias[chanID] = alias
3✔
368

3✔
369
        return nil
3✔
370
}
371

372
// GetPeerAlias retrieves a peer's alias SCID by the channel's ChanID.
373
func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
374
        error) {
3✔
375

3✔
376
        m.RLock()
3✔
377
        defer m.RUnlock()
3✔
378

3✔
379
        alias, ok := m.peerAlias[chanID]
3✔
380
        if !ok || alias == hop.Source {
6✔
381
                return lnwire.ShortChannelID{}, errNoPeerAlias
3✔
382
        }
3✔
383

384
        return alias, nil
3✔
385
}
386

387
// RequestAlias returns a new ALIAS ShortChannelID to the caller by allocating
388
// the next un-allocated ShortChannelID. The starting ShortChannelID is
389
// 16000000:0:0 and the ending ShortChannelID is 16250000:16777215:65535. This
390
// gives roughly 2^58 possible ALIAS ShortChannelIDs which ensures this space
391
// won't get exhausted.
392
func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
3✔
393
        var nextAlias lnwire.ShortChannelID
3✔
394

3✔
395
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
396
                bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
3✔
397
                if err != nil {
3✔
398
                        return err
×
399
                }
×
400

401
                lastBytes := bucket.Get(lastAliasKey)
3✔
402
                if lastBytes == nil {
6✔
403
                        // If the key does not exist, then we can write the
3✔
404
                        // StartingAlias to it.
3✔
405
                        nextAlias = StartingAlias
3✔
406

3✔
407
                        var scratch [8]byte
3✔
408
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
3✔
409
                        return bucket.Put(lastAliasKey, scratch[:])
3✔
410
                }
3✔
411

412
                // Otherwise the key does exist so we can convert the retrieved
413
                // lastAlias to a ShortChannelID and use it to assign the next
414
                // ShortChannelID. This next ShortChannelID will then be
415
                // persisted in the database.
416
                lastScid := lnwire.NewShortChanIDFromInt(
3✔
417
                        byteOrder.Uint64(lastBytes),
3✔
418
                )
3✔
419
                nextAlias = getNextScid(lastScid)
3✔
420

3✔
421
                var scratch [8]byte
3✔
422
                byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
3✔
423
                return bucket.Put(lastAliasKey, scratch[:])
3✔
424
        }, func() {
3✔
425
                nextAlias = lnwire.ShortChannelID{}
3✔
426
        })
3✔
427
        if err != nil {
3✔
428
                return nextAlias, err
×
429
        }
×
430

431
        return nextAlias, nil
3✔
432
}
433

434
// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
435
// layer.
436
func (m *Manager) ListAliases() map[lnwire.ShortChannelID][]lnwire.ShortChannelID {
×
437
        m.RLock()
×
438
        defer m.RUnlock()
×
439

×
440
        baseCopy := make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
×
441

×
442
        for k, v := range m.baseToSet {
×
443
                setCopy := make([]lnwire.ShortChannelID, len(v))
×
444
                copy(setCopy, v)
×
445
                baseCopy[k] = setCopy
×
446
        }
×
447

448
        return baseCopy
×
449
}
450

451
// getNextScid is a utility function that returns the next SCID for a given
452
// alias SCID. The BlockHeight ranges from [16000000, 16250000], the TxIndex
453
// ranges from [1, 16777215], and the TxPosition ranges from [1, 65535].
454
func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {
3✔
455
        var (
3✔
456
                next            lnwire.ShortChannelID
3✔
457
                incrementIdx    bool
3✔
458
                incrementHeight bool
3✔
459
        )
3✔
460

3✔
461
        // If the TxPosition is 65535, then it goes to 0 and we need to
3✔
462
        // increment the TxIndex.
3✔
463
        if last.TxPosition == 65535 {
3✔
464
                incrementIdx = true
×
465
        }
×
466

467
        // If the TxIndex is 16777215 and we need to increment it, then it goes
468
        // to 0 and we need to increment the BlockHeight.
469
        if last.TxIndex == 16777215 && incrementIdx {
3✔
470
                incrementIdx = false
×
471
                incrementHeight = true
×
472
        }
×
473

474
        switch {
3✔
475
        // If we increment the TxIndex, then TxPosition goes to 0.
476
        case incrementIdx:
×
477
                next.BlockHeight = last.BlockHeight
×
478
                next.TxIndex = last.TxIndex + 1
×
479
                next.TxPosition = 0
×
480

481
        // If we increment the BlockHeight, then the Tx fields go to 0.
482
        case incrementHeight:
×
483
                next.BlockHeight = last.BlockHeight + 1
×
484
                next.TxIndex = 0
×
485
                next.TxPosition = 0
×
486

487
        // Otherwise, we only need to increment the TxPosition.
488
        default:
3✔
489
                next.BlockHeight = last.BlockHeight
3✔
490
                next.TxIndex = last.TxIndex
3✔
491
                next.TxPosition = last.TxPosition + 1
3✔
492
        }
493

494
        return next
3✔
495
}
496

497
// IsAlias returns true if the passed SCID is an alias. The function determines
498
// this by looking at the BlockHeight. If the BlockHeight is greater than
499
// startingBlockHeight and less than endBlockHeight, then it is an alias
500
// assigned by RequestAlias. These bounds only apply to aliases we generate.
501
// Our peers are free to use any range they choose.
502
func IsAlias(scid lnwire.ShortChannelID) bool {
3✔
503
        return scid.BlockHeight >= uint32(startingBlockHeight) &&
3✔
504
                scid.BlockHeight < uint32(endBlockHeight)
3✔
505
}
3✔
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