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

lightningnetwork / lnd / 14277115115

05 Apr 2025 01:43AM UTC coverage: 58.056% (-11.0%) from 69.04%
14277115115

Pull #9670

github

web-flow
Merge a7e89c130 into f0ea5bf3b
Pull Request #9670: build: bump version to v0.19.0 rc2

96191 of 165688 relevant lines covered (58.06%)

1.22 hits per line

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

83.17
/aliasmgr/aliasmgr.go
1
package aliasmgr
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6
        "maps"
7
        "slices"
8
        "sync"
9

10
        "github.com/lightningnetwork/lnd/fn/v2"
11
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
12
        "github.com/lightningnetwork/lnd/kvdb"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
)
15

16
// UpdateLinkAliases is a function type for a function that locates the active
17
// link that matches the given shortID and triggers an update based on the
18
// latest values of the alias manager.
19
type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error
20

21
// ScidAliasMap is a map from a base short channel ID to a set of alias short
22
// channel IDs.
23
type ScidAliasMap map[lnwire.ShortChannelID][]lnwire.ShortChannelID
24

25
var (
26
        // aliasBucket stores aliases as keys and their base SCIDs as values.
27
        // This is used to populate the maps that the Manager uses. The keys
28
        // are alias SCIDs and the values are their respective base SCIDs. This
29
        // is used instead of the other way around (base -> alias...) because
30
        // updating an alias would require fetching all the existing aliases,
31
        // adding another one, and then flushing the write to disk. This is
32
        // inefficient compared to N 1:1 mappings at the cost of marginally
33
        // more disk space.
34
        aliasBucket = []byte("alias-bucket")
35

36
        // confirmedBucket stores whether or not a given base SCID should no
37
        // longer have entries in the ToBase maps. The key is the SCID that is
38
        // confirmed with 6 confirmations and is public, and the value is
39
        // empty.
40
        confirmedBucket = []byte("base-bucket")
41

42
        // aliasAllocBucket is a root-level bucket that stores the last alias
43
        // that was allocated. It is used to allocate a new alias when
44
        // requested.
45
        aliasAllocBucket = []byte("alias-alloc-bucket")
46

47
        // lastAliasKey is a key in the aliasAllocBucket whose value is the
48
        // last allocated alias ShortChannelID. This will be updated upon calls
49
        // to RequestAlias.
50
        lastAliasKey = []byte("last-alias-key")
51

52
        // invoiceAliasBucket is a root-level bucket that stores the alias
53
        // SCIDs that our peers send us in the channel_ready TLV. The keys are
54
        // the ChannelID generated from the FundingOutpoint and the values are
55
        // the remote peer's alias SCID.
56
        invoiceAliasBucket = []byte("invoice-alias-bucket")
57

58
        // byteOrder denotes the byte order of database (de)-serialization
59
        // operations.
60
        byteOrder = binary.BigEndian
61

62
        // AliasStartBlockHeight is the starting block height of the alias
63
        // range.
64
        AliasStartBlockHeight uint32 = 16_000_000
65

66
        // AliasEndBlockHeight is the ending block height of the alias range.
67
        AliasEndBlockHeight uint32 = 16_250_000
68

69
        // StartingAlias is the first alias ShortChannelID that will get
70
        // assigned by RequestAlias. The starting BlockHeight is chosen so that
71
        // legitimate SCIDs in integration tests aren't mistaken for an alias.
72
        StartingAlias = lnwire.ShortChannelID{
73
                BlockHeight: AliasStartBlockHeight,
74
                TxIndex:     0,
75
                TxPosition:  0,
76
        }
77

78
        // errNoBase is returned when a base SCID isn't found.
79
        errNoBase = fmt.Errorf("no base found")
80

81
        // errNoPeerAlias is returned when the peer's alias for a given
82
        // channel is not found.
83
        errNoPeerAlias = fmt.Errorf("no peer alias found")
84

85
        // ErrAliasNotFound is returned when the alias is not found and can't
86
        // be mapped to a base SCID.
87
        ErrAliasNotFound = fmt.Errorf("alias not found")
88
)
89

90
// Manager is a struct that handles aliases for LND. It has an underlying
91
// database that can allocate aliases for channels, stores the peer's last
92
// alias for use in our hop hints, and contains mappings that both the Switch
93
// and Gossiper use.
94
type Manager struct {
95
        backend kvdb.Backend
96

97
        // linkAliasUpdater is a function used by the alias manager to
98
        // facilitate live update of aliases in other subsystems.
99
        linkAliasUpdater UpdateLinkAliases
100

101
        // baseToSet is a mapping from the "base" SCID to the set of aliases
102
        // for this channel. This mapping includes all channels that
103
        // negotiated the option-scid-alias feature bit.
104
        baseToSet ScidAliasMap
105

106
        // aliasToBase is a mapping that maps all aliases for a given channel
107
        // to its base SCID. This is only used for channels that have
108
        // negotiated option-scid-alias feature bit.
109
        aliasToBase map[lnwire.ShortChannelID]lnwire.ShortChannelID
110

111
        // peerAlias is a cache for the alias SCIDs that our peers send us in
112
        // the channel_ready TLV. The keys are the ChannelID generated from
113
        // the FundingOutpoint and the values are the remote peer's alias SCID.
114
        // The values should match the ones stored in the "invoice-alias-bucket"
115
        // bucket.
116
        peerAlias map[lnwire.ChannelID]lnwire.ShortChannelID
117

118
        sync.RWMutex
119
}
120

121
// NewManager initializes an alias Manager from the passed database backend.
122
func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager,
123
        error) {
2✔
124

2✔
125
        m := &Manager{
2✔
126
                backend:          db,
2✔
127
                baseToSet:        make(ScidAliasMap),
2✔
128
                linkAliasUpdater: linkAliasUpdater,
2✔
129
        }
2✔
130

2✔
131
        m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
2✔
132
        m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
2✔
133

2✔
134
        err := m.populateMaps()
2✔
135
        return m, err
2✔
136
}
2✔
137

138
// populateMaps reads the database state and populates the maps.
139
func (m *Manager) populateMaps() error {
2✔
140
        // This map tracks the base SCIDs that are confirmed and don't need to
2✔
141
        // have entries in the *ToBase mappings as they won't be used in the
2✔
142
        // gossiper.
2✔
143
        baseConfMap := make(map[lnwire.ShortChannelID]struct{})
2✔
144

2✔
145
        // This map caches what is found in the database and is used to
2✔
146
        // populate the Manager's actual maps.
2✔
147
        aliasMap := make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
2✔
148

2✔
149
        // This map caches the ChannelID/alias SCIDs stored in the database and
2✔
150
        // is used to populate the Manager's cache.
2✔
151
        peerAliasMap := make(map[lnwire.ChannelID]lnwire.ShortChannelID)
2✔
152

2✔
153
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
4✔
154
                baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
2✔
155
                if err != nil {
2✔
156
                        return err
×
157
                }
×
158

159
                err = baseConfBucket.ForEach(func(k, v []byte) error {
4✔
160
                        // The key will the base SCID and the value will be
2✔
161
                        // empty. Existence in the bucket means the SCID is
2✔
162
                        // confirmed.
2✔
163
                        baseScid := lnwire.NewShortChanIDFromInt(
2✔
164
                                byteOrder.Uint64(k),
2✔
165
                        )
2✔
166
                        baseConfMap[baseScid] = struct{}{}
2✔
167
                        return nil
2✔
168
                })
2✔
169
                if err != nil {
2✔
170
                        return err
×
171
                }
×
172

173
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
2✔
174
                if err != nil {
2✔
175
                        return err
×
176
                }
×
177

178
                err = aliasToBaseBucket.ForEach(func(k, v []byte) error {
4✔
179
                        // The key will be the alias SCID and the value will be
2✔
180
                        // the base SCID.
2✔
181
                        aliasScid := lnwire.NewShortChanIDFromInt(
2✔
182
                                byteOrder.Uint64(k),
2✔
183
                        )
2✔
184
                        baseScid := lnwire.NewShortChanIDFromInt(
2✔
185
                                byteOrder.Uint64(v),
2✔
186
                        )
2✔
187
                        aliasMap[aliasScid] = baseScid
2✔
188
                        return nil
2✔
189
                })
2✔
190
                if err != nil {
2✔
191
                        return err
×
192
                }
×
193

194
                invAliasBucket, err := tx.CreateTopLevelBucket(
2✔
195
                        invoiceAliasBucket,
2✔
196
                )
2✔
197
                if err != nil {
2✔
198
                        return err
×
199
                }
×
200

201
                err = invAliasBucket.ForEach(func(k, v []byte) error {
4✔
202
                        var chanID lnwire.ChannelID
2✔
203
                        copy(chanID[:], k)
2✔
204
                        alias := lnwire.NewShortChanIDFromInt(
2✔
205
                                byteOrder.Uint64(v),
2✔
206
                        )
2✔
207

2✔
208
                        peerAliasMap[chanID] = alias
2✔
209

2✔
210
                        return nil
2✔
211
                })
2✔
212

213
                return err
2✔
214
        }, func() {
2✔
215
                baseConfMap = make(map[lnwire.ShortChannelID]struct{})
2✔
216
                aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
2✔
217
                peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
2✔
218
        })
2✔
219
        if err != nil {
2✔
220
                return err
×
221
        }
×
222

223
        // Populate the baseToSet map regardless if the baseSCID is marked as
224
        // public with 6 confirmations.
225
        for aliasSCID, baseSCID := range aliasMap {
4✔
226
                m.baseToSet[baseSCID] = append(m.baseToSet[baseSCID], aliasSCID)
2✔
227

2✔
228
                // Skip if baseSCID is in the baseConfMap.
2✔
229
                if _, ok := baseConfMap[baseSCID]; ok {
4✔
230
                        continue
2✔
231
                }
232

233
                m.aliasToBase[aliasSCID] = baseSCID
2✔
234
        }
235

236
        // Populate the peer alias cache.
237
        m.peerAlias = peerAliasMap
2✔
238

2✔
239
        return nil
2✔
240
}
241

242
// AddLocalAlias adds a database mapping from the passed alias to the passed
243
// base SCID. The gossip boolean marks whether or not to create a mapping
244
// that the gossiper will use. It is set to false for the upgrade path where
245
// the feature-bit is toggled on and there are existing channels. The linkUpdate
246
// flag is used to signal whether this function should also trigger an update
247
// on the htlcswitch scid alias maps.
248
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
249
        gossip, linkUpdate bool) error {
2✔
250

2✔
251
        // We need to lock the manager for the whole duration of this method,
2✔
252
        // except for the very last part where we call the link updater. In
2✔
253
        // order for us to safely use a defer _and_ still be able to manually
2✔
254
        // unlock, we use a sync.Once.
2✔
255
        m.Lock()
2✔
256
        unlockOnce := sync.Once{}
2✔
257
        unlock := func() {
4✔
258
                unlockOnce.Do(m.Unlock)
2✔
259
        }
2✔
260
        defer unlock()
2✔
261

2✔
262
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
4✔
263
                // If the caller does not want to allow the alias to be used
2✔
264
                // for a channel update, we'll mark it in the baseConfBucket.
2✔
265
                if !gossip {
4✔
266
                        var baseGossipBytes [8]byte
2✔
267
                        byteOrder.PutUint64(
2✔
268
                                baseGossipBytes[:], baseScid.ToUint64(),
2✔
269
                        )
2✔
270

2✔
271
                        confBucket, err := tx.CreateTopLevelBucket(
2✔
272
                                confirmedBucket,
2✔
273
                        )
2✔
274
                        if err != nil {
2✔
275
                                return err
×
276
                        }
×
277

278
                        err = confBucket.Put(baseGossipBytes[:], []byte{})
2✔
279
                        if err != nil {
2✔
280
                                return err
×
281
                        }
×
282
                }
283

284
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
2✔
285
                if err != nil {
2✔
286
                        return err
×
287
                }
×
288

289
                var (
2✔
290
                        aliasBytes [8]byte
2✔
291
                        baseBytes  [8]byte
2✔
292
                )
2✔
293

2✔
294
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
2✔
295
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
2✔
296
                return aliasToBaseBucket.Put(aliasBytes[:], baseBytes[:])
2✔
297
        }, func() {})
2✔
298
        if err != nil {
2✔
299
                return err
×
300
        }
×
301

302
        // Update the aliasToBase and baseToSet maps.
303
        m.baseToSet[baseScid] = append(m.baseToSet[baseScid], alias)
2✔
304

2✔
305
        // Only store the gossiper map if gossip is true.
2✔
306
        if gossip {
4✔
307
                m.aliasToBase[alias] = baseScid
2✔
308
        }
2✔
309

310
        // We definitely need to unlock the Manager before calling the link
311
        // updater. If we don't, we'll deadlock. We use a sync.Once to ensure
312
        // that we only unlock once.
313
        unlock()
2✔
314

2✔
315
        // Finally, we trigger a htlcswitch update if the flag is set, in order
2✔
316
        // for any future htlc that references the added alias to be properly
2✔
317
        // routed.
2✔
318
        if linkUpdate {
4✔
319
                return m.linkAliasUpdater(baseScid)
2✔
320
        }
2✔
321

322
        return nil
2✔
323
}
324

325
// GetAliases fetches the set of aliases stored under a given base SCID from
326
// write-through caches.
327
func (m *Manager) GetAliases(
328
        base lnwire.ShortChannelID) []lnwire.ShortChannelID {
2✔
329

2✔
330
        m.RLock()
2✔
331
        defer m.RUnlock()
2✔
332

2✔
333
        aliasSet, ok := m.baseToSet[base]
2✔
334
        if ok {
4✔
335
                // Copy the found alias slice.
2✔
336
                setCopy := make([]lnwire.ShortChannelID, len(aliasSet))
2✔
337
                copy(setCopy, aliasSet)
2✔
338
                return setCopy
2✔
339
        }
2✔
340

341
        return nil
2✔
342
}
343

344
// FindBaseSCID finds the base SCID for a given alias. This is used in the
345
// gossiper to find the correct SCID to lookup in the graph database.
346
func (m *Manager) FindBaseSCID(
347
        alias lnwire.ShortChannelID) (lnwire.ShortChannelID, error) {
2✔
348

2✔
349
        m.RLock()
2✔
350
        defer m.RUnlock()
2✔
351

2✔
352
        base, ok := m.aliasToBase[alias]
2✔
353
        if ok {
4✔
354
                return base, nil
2✔
355
        }
2✔
356

357
        return lnwire.ShortChannelID{}, errNoBase
2✔
358
}
359

360
// DeleteSixConfs removes a mapping for the gossiper once six confirmations
361
// have been reached and the channel is public. At this point, only the
362
// confirmed SCID should be used.
363
func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
2✔
364
        m.Lock()
2✔
365
        defer m.Unlock()
2✔
366

2✔
367
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
4✔
368
                baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
2✔
369
                if err != nil {
2✔
370
                        return err
×
371
                }
×
372

373
                var baseBytes [8]byte
2✔
374
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
2✔
375
                return baseConfBucket.Put(baseBytes[:], []byte{})
2✔
376
        }, func() {})
2✔
377
        if err != nil {
2✔
378
                return err
×
379
        }
×
380

381
        // Now that the database state has been updated, we'll delete all of
382
        // the aliasToBase mappings for this SCID.
383
        for alias, base := range m.aliasToBase {
4✔
384
                if base.ToUint64() == baseScid.ToUint64() {
4✔
385
                        delete(m.aliasToBase, alias)
2✔
386
                }
2✔
387
        }
388

389
        return nil
2✔
390
}
391

392
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
393
func (m *Manager) DeleteLocalAlias(alias,
394
        baseScid lnwire.ShortChannelID) error {
2✔
395

2✔
396
        // We need to lock the manager for the whole duration of this method,
2✔
397
        // except for the very last part where we call the link updater. In
2✔
398
        // order for us to safely use a defer _and_ still be able to manually
2✔
399
        // unlock, we use a sync.Once.
2✔
400
        m.Lock()
2✔
401
        unlockOnce := sync.Once{}
2✔
402
        unlock := func() {
4✔
403
                unlockOnce.Do(m.Unlock)
2✔
404
        }
2✔
405
        defer unlock()
2✔
406

2✔
407
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
4✔
408
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
2✔
409
                if err != nil {
2✔
410
                        return err
×
411
                }
×
412

413
                var aliasBytes [8]byte
2✔
414
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
2✔
415

2✔
416
                // If the user attempts to delete an alias that doesn't exist,
2✔
417
                // we'll want to inform them about it and not just do nothing.
2✔
418
                if aliasToBaseBucket.Get(aliasBytes[:]) == nil {
2✔
419
                        return ErrAliasNotFound
×
420
                }
×
421

422
                return aliasToBaseBucket.Delete(aliasBytes[:])
2✔
423
        }, func() {})
2✔
424
        if err != nil {
2✔
425
                return err
×
426
        }
×
427

428
        // Now that the database state has been updated, we'll delete the
429
        // mapping from the Manager's maps.
430
        aliasSet, ok := m.baseToSet[baseScid]
2✔
431
        if !ok {
2✔
432
                return ErrAliasNotFound
×
433
        }
×
434

435
        // We'll filter the alias set and remove the alias from it.
436
        aliasSet = fn.Filter(aliasSet, func(a lnwire.ShortChannelID) bool {
4✔
437
                return a.ToUint64() != alias.ToUint64()
2✔
438
        })
2✔
439

440
        // If the alias set is empty, we'll delete the base SCID from the
441
        // baseToSet map.
442
        if len(aliasSet) == 0 {
2✔
443
                delete(m.baseToSet, baseScid)
×
444
        } else {
2✔
445
                m.baseToSet[baseScid] = aliasSet
2✔
446
        }
2✔
447

448
        // Finally, we'll delete the aliasToBase mapping from the Manager's
449
        // cache (but this is only set if we gossip the alias).
450
        delete(m.aliasToBase, alias)
2✔
451

2✔
452
        // We definitely need to unlock the Manager before calling the link
2✔
453
        // updater. If we don't, we'll deadlock. We use a sync.Once to ensure
2✔
454
        // that we only unlock once.
2✔
455
        unlock()
2✔
456

2✔
457
        return m.linkAliasUpdater(baseScid)
2✔
458
}
459

460
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
461
// channel_ready message.
462
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
463
        alias lnwire.ShortChannelID) error {
2✔
464

2✔
465
        m.Lock()
2✔
466
        defer m.Unlock()
2✔
467

2✔
468
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
4✔
469
                bucket, err := tx.CreateTopLevelBucket(invoiceAliasBucket)
2✔
470
                if err != nil {
2✔
471
                        return err
×
472
                }
×
473

474
                var scratch [8]byte
2✔
475
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
2✔
476
                return bucket.Put(chanID[:], scratch[:])
2✔
477
        }, func() {})
2✔
478
        if err != nil {
2✔
479
                return err
×
480
        }
×
481

482
        // Now that the database state has been updated, we can update it in
483
        // our cache.
484
        m.peerAlias[chanID] = alias
2✔
485

2✔
486
        return nil
2✔
487
}
488

489
// GetPeerAlias retrieves a peer's alias SCID by the channel's ChanID.
490
func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
491
        error) {
2✔
492

2✔
493
        m.RLock()
2✔
494
        defer m.RUnlock()
2✔
495

2✔
496
        alias, ok := m.peerAlias[chanID]
2✔
497
        if !ok || alias == hop.Source {
4✔
498
                return lnwire.ShortChannelID{}, errNoPeerAlias
2✔
499
        }
2✔
500

501
        return alias, nil
2✔
502
}
503

504
// RequestAlias returns a new ALIAS ShortChannelID to the caller by allocating
505
// the next un-allocated ShortChannelID. The starting ShortChannelID is
506
// 16000000:0:0 and the ending ShortChannelID is 16250000:16777215:65535. This
507
// gives roughly 2^58 possible ALIAS ShortChannelIDs which ensures this space
508
// won't get exhausted.
509
func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
2✔
510
        var nextAlias lnwire.ShortChannelID
2✔
511

2✔
512
        m.RLock()
2✔
513
        defer m.RUnlock()
2✔
514

2✔
515
        // haveAlias returns true if the passed alias is already assigned to a
2✔
516
        // channel in the baseToSet map.
2✔
517
        haveAlias := func(maybeNextAlias lnwire.ShortChannelID) bool {
4✔
518
                return fn.Any(
2✔
519
                        slices.Collect(maps.Values(m.baseToSet)),
2✔
520
                        func(aliasList []lnwire.ShortChannelID) bool {
4✔
521
                                return fn.Any(
2✔
522
                                        aliasList,
2✔
523
                                        func(alias lnwire.ShortChannelID) bool {
4✔
524
                                                return alias == maybeNextAlias
2✔
525
                                        },
2✔
526
                                )
527
                        },
528
                )
529
        }
530

531
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
4✔
532
                bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
2✔
533
                if err != nil {
2✔
534
                        return err
×
535
                }
×
536

537
                lastBytes := bucket.Get(lastAliasKey)
2✔
538
                if lastBytes == nil {
4✔
539
                        // If the key does not exist, then we can write the
2✔
540
                        // StartingAlias to it.
2✔
541
                        nextAlias = StartingAlias
2✔
542

2✔
543
                        // If the very first alias is already assigned, we'll
2✔
544
                        // keep incrementing until we find an unassigned alias.
2✔
545
                        // This is to avoid collision with custom added SCID
2✔
546
                        // aliases that fall into the same range as the ones we
2✔
547
                        // generate here monotonically. Those custom SCIDs are
2✔
548
                        // stored in a different bucket, but we can just check
2✔
549
                        // the in-memory map for simplicity.
2✔
550
                        for {
4✔
551
                                if !haveAlias(nextAlias) {
4✔
552
                                        break
2✔
553
                                }
554

555
                                nextAlias = getNextScid(nextAlias)
×
556

×
557
                                // Abort if we've reached the end of the range.
×
558
                                if nextAlias.BlockHeight >=
×
559
                                        AliasEndBlockHeight {
×
560

×
561
                                        return fmt.Errorf("range for custom " +
×
562
                                                "aliases exhausted")
×
563
                                }
×
564
                        }
565

566
                        var scratch [8]byte
2✔
567
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
2✔
568
                        return bucket.Put(lastAliasKey, scratch[:])
2✔
569
                }
570

571
                // Otherwise the key does exist so we can convert the retrieved
572
                // lastAlias to a ShortChannelID and use it to assign the next
573
                // ShortChannelID. This next ShortChannelID will then be
574
                // persisted in the database.
575
                lastScid := lnwire.NewShortChanIDFromInt(
2✔
576
                        byteOrder.Uint64(lastBytes),
2✔
577
                )
2✔
578
                nextAlias = getNextScid(lastScid)
2✔
579

2✔
580
                // If the next alias is already assigned, we'll keep
2✔
581
                // incrementing until we find an unassigned alias. This is to
2✔
582
                // avoid collision with custom added SCID aliases that fall into
2✔
583
                // the same range as the ones we generate here monotonically.
2✔
584
                // Those custom SCIDs are stored in a different bucket, but we
2✔
585
                // can just check the in-memory map for simplicity.
2✔
586
                for {
4✔
587
                        if !haveAlias(nextAlias) {
4✔
588
                                break
2✔
589
                        }
590

591
                        nextAlias = getNextScid(nextAlias)
×
592

×
593
                        // Abort if we've reached the end of the range.
×
594
                        if nextAlias.BlockHeight >= AliasEndBlockHeight {
×
595
                                return fmt.Errorf("range for custom " +
×
596
                                        "aliases exhausted")
×
597
                        }
×
598
                }
599

600
                var scratch [8]byte
2✔
601
                byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
2✔
602
                return bucket.Put(lastAliasKey, scratch[:])
2✔
603
        }, func() {
2✔
604
                nextAlias = lnwire.ShortChannelID{}
2✔
605
        })
2✔
606
        if err != nil {
2✔
607
                return nextAlias, err
×
608
        }
×
609

610
        return nextAlias, nil
2✔
611
}
612

613
// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
614
// layer.
615
func (m *Manager) ListAliases() ScidAliasMap {
2✔
616
        m.RLock()
2✔
617
        defer m.RUnlock()
2✔
618

2✔
619
        baseCopy := make(ScidAliasMap)
2✔
620

2✔
621
        for k, v := range m.baseToSet {
4✔
622
                setCopy := make([]lnwire.ShortChannelID, len(v))
2✔
623
                copy(setCopy, v)
2✔
624
                baseCopy[k] = setCopy
2✔
625
        }
2✔
626

627
        return baseCopy
2✔
628
}
629

630
// getNextScid is a utility function that returns the next SCID for a given
631
// alias SCID. The BlockHeight ranges from [16000000, 16250000], the TxIndex
632
// ranges from [1, 16777215], and the TxPosition ranges from [1, 65535].
633
func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {
2✔
634
        var (
2✔
635
                next            lnwire.ShortChannelID
2✔
636
                incrementIdx    bool
2✔
637
                incrementHeight bool
2✔
638
        )
2✔
639

2✔
640
        // If the TxPosition is 65535, then it goes to 0 and we need to
2✔
641
        // increment the TxIndex.
2✔
642
        if last.TxPosition == 65535 {
2✔
643
                incrementIdx = true
×
644
        }
×
645

646
        // If the TxIndex is 16777215 and we need to increment it, then it goes
647
        // to 0 and we need to increment the BlockHeight.
648
        if last.TxIndex == 16777215 && incrementIdx {
2✔
649
                incrementIdx = false
×
650
                incrementHeight = true
×
651
        }
×
652

653
        switch {
2✔
654
        // If we increment the TxIndex, then TxPosition goes to 0.
655
        case incrementIdx:
×
656
                next.BlockHeight = last.BlockHeight
×
657
                next.TxIndex = last.TxIndex + 1
×
658
                next.TxPosition = 0
×
659

660
        // If we increment the BlockHeight, then the Tx fields go to 0.
661
        case incrementHeight:
×
662
                next.BlockHeight = last.BlockHeight + 1
×
663
                next.TxIndex = 0
×
664
                next.TxPosition = 0
×
665

666
        // Otherwise, we only need to increment the TxPosition.
667
        default:
2✔
668
                next.BlockHeight = last.BlockHeight
2✔
669
                next.TxIndex = last.TxIndex
2✔
670
                next.TxPosition = last.TxPosition + 1
2✔
671
        }
672

673
        return next
2✔
674
}
675

676
// IsAlias returns true if the passed SCID is an alias. The function determines
677
// this by looking at the BlockHeight. If the BlockHeight is greater than
678
// AliasStartBlockHeight and less than AliasEndBlockHeight, then it is an alias
679
// assigned by RequestAlias. These bounds only apply to aliases we generate.
680
// Our peers are free to use any range they choose.
681
func IsAlias(scid lnwire.ShortChannelID) bool {
2✔
682
        return scid.BlockHeight >= AliasStartBlockHeight &&
2✔
683
                scid.BlockHeight < AliasEndBlockHeight
2✔
684
}
2✔
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