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

lightningnetwork / lnd / 15736109134

18 Jun 2025 02:46PM UTC coverage: 58.197% (-10.1%) from 68.248%
15736109134

Pull #9752

github

web-flow
Merge d2634a68c into 31c74f20f
Pull Request #9752: routerrpc: reject payment to invoice that don't have payment secret or blinded paths

6 of 13 new or added lines in 2 files covered. (46.15%)

28331 existing lines in 455 files now uncovered.

97860 of 168153 relevant lines covered (58.2%)

1.81 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) {
3✔
124

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

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

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

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

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

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

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

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

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

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

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

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

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

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

213
                return err
3✔
214
        }, func() {
3✔
215
                baseConfMap = make(map[lnwire.ShortChannelID]struct{})
3✔
216
                aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
217
                peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
218
        })
3✔
219
        if err != nil {
3✔
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 {
6✔
226
                m.baseToSet[baseSCID] = append(m.baseToSet[baseSCID], aliasSCID)
3✔
227

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

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

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

3✔
239
        return nil
3✔
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 {
3✔
250

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

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

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

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

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

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

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

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

3✔
305
        // Only store the gossiper map if gossip is true.
3✔
306
        if gossip {
6✔
307
                m.aliasToBase[alias] = baseScid
3✔
308
        }
3✔
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()
3✔
314

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

322
        return nil
3✔
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 {
3✔
329

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

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

341
        return nil
3✔
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) {
3✔
348

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

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

357
        return lnwire.ShortChannelID{}, errNoBase
3✔
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 {
3✔
364
        m.Lock()
3✔
365
        defer m.Unlock()
3✔
366

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

373
                var baseBytes [8]byte
3✔
374
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
3✔
375
                return baseConfBucket.Put(baseBytes[:], []byte{})
3✔
376
        }, func() {})
3✔
377
        if err != nil {
3✔
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 {
6✔
384
                if base.ToUint64() == baseScid.ToUint64() {
6✔
385
                        delete(m.aliasToBase, alias)
3✔
386
                }
3✔
387
        }
388

389
        return nil
3✔
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 {
3✔
395

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

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

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

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

422
                return aliasToBaseBucket.Delete(aliasBytes[:])
3✔
423
        }, func() {})
3✔
424
        if err != nil {
3✔
UNCOV
425
                return err
×
UNCOV
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]
3✔
431
        if !ok {
3✔
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 {
6✔
437
                return a.ToUint64() != alias.ToUint64()
3✔
438
        })
3✔
439

440
        // If the alias set is empty, we'll delete the base SCID from the
441
        // baseToSet map.
442
        if len(aliasSet) == 0 {
3✔
UNCOV
443
                delete(m.baseToSet, baseScid)
×
444
        } else {
3✔
445
                m.baseToSet[baseScid] = aliasSet
3✔
446
        }
3✔
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)
3✔
451

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

3✔
457
        return m.linkAliasUpdater(baseScid)
3✔
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 {
3✔
464

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

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

474
                var scratch [8]byte
3✔
475
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
3✔
476
                return bucket.Put(chanID[:], scratch[:])
3✔
477
        }, func() {})
3✔
478
        if err != nil {
3✔
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
3✔
485

3✔
486
        return nil
3✔
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) {
3✔
492

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

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

501
        return alias, nil
3✔
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) {
3✔
510
        var nextAlias lnwire.ShortChannelID
3✔
511

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

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

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

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

3✔
543
                        // If the very first alias is already assigned, we'll
3✔
544
                        // keep incrementing until we find an unassigned alias.
3✔
545
                        // This is to avoid collision with custom added SCID
3✔
546
                        // aliases that fall into the same range as the ones we
3✔
547
                        // generate here monotonically. Those custom SCIDs are
3✔
548
                        // stored in a different bucket, but we can just check
3✔
549
                        // the in-memory map for simplicity.
3✔
550
                        for {
6✔
551
                                if !haveAlias(nextAlias) {
6✔
552
                                        break
3✔
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
3✔
567
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
3✔
568
                        return bucket.Put(lastAliasKey, scratch[:])
3✔
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(
3✔
576
                        byteOrder.Uint64(lastBytes),
3✔
577
                )
3✔
578
                nextAlias = getNextScid(lastScid)
3✔
579

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

UNCOV
591
                        nextAlias = getNextScid(nextAlias)
×
UNCOV
592

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

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

610
        return nextAlias, nil
3✔
611
}
612

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

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

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

627
        return baseCopy
3✔
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 {
3✔
634
        var (
3✔
635
                next            lnwire.ShortChannelID
3✔
636
                incrementIdx    bool
3✔
637
                incrementHeight bool
3✔
638
        )
3✔
639

3✔
640
        // If the TxPosition is 65535, then it goes to 0 and we need to
3✔
641
        // increment the TxIndex.
3✔
642
        if last.TxPosition == 65535 {
3✔
UNCOV
643
                incrementIdx = true
×
UNCOV
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 {
3✔
UNCOV
649
                incrementIdx = false
×
UNCOV
650
                incrementHeight = true
×
UNCOV
651
        }
×
652

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

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

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

673
        return next
3✔
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 {
3✔
682
        return scid.BlockHeight >= AliasStartBlockHeight &&
3✔
683
                scid.BlockHeight < AliasEndBlockHeight
3✔
684
}
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