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

lightningnetwork / lnd / 18016273007

25 Sep 2025 05:55PM UTC coverage: 54.653% (-12.0%) from 66.622%
18016273007

Pull #10248

github

web-flow
Merge 128443298 into b09b20c69
Pull Request #10248: Enforce TLV when creating a Route

25 of 30 new or added lines in 4 files covered. (83.33%)

23906 existing lines in 281 files now uncovered.

109536 of 200421 relevant lines covered (54.65%)

21816.97 hits per line

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

71.26
/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 {
3✔
UNCOV
160
                        // The key will the base SCID and the value will be
×
UNCOV
161
                        // empty. Existence in the bucket means the SCID is
×
UNCOV
162
                        // confirmed.
×
UNCOV
163
                        baseScid := lnwire.NewShortChanIDFromInt(
×
UNCOV
164
                                byteOrder.Uint64(k),
×
UNCOV
165
                        )
×
UNCOV
166
                        baseConfMap[baseScid] = struct{}{}
×
UNCOV
167
                        return nil
×
UNCOV
168
                })
×
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 {
3✔
UNCOV
179
                        // The key will be the alias SCID and the value will be
×
UNCOV
180
                        // the base SCID.
×
UNCOV
181
                        aliasScid := lnwire.NewShortChanIDFromInt(
×
UNCOV
182
                                byteOrder.Uint64(k),
×
UNCOV
183
                        )
×
UNCOV
184
                        baseScid := lnwire.NewShortChanIDFromInt(
×
UNCOV
185
                                byteOrder.Uint64(v),
×
UNCOV
186
                        )
×
UNCOV
187
                        aliasMap[aliasScid] = baseScid
×
UNCOV
188
                        return nil
×
UNCOV
189
                })
×
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 {
3✔
UNCOV
202
                        var chanID lnwire.ChannelID
×
UNCOV
203
                        copy(chanID[:], k)
×
UNCOV
204
                        alias := lnwire.NewShortChanIDFromInt(
×
UNCOV
205
                                byteOrder.Uint64(v),
×
UNCOV
206
                        )
×
UNCOV
207

×
UNCOV
208
                        peerAliasMap[chanID] = alias
×
UNCOV
209

×
UNCOV
210
                        return nil
×
UNCOV
211
                })
×
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 {
3✔
UNCOV
226
                m.baseToSet[baseSCID] = append(m.baseToSet[baseSCID], aliasSCID)
×
UNCOV
227

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

UNCOV
233
                m.aliasToBase[aliasSCID] = baseSCID
×
234
        }
235

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

3✔
239
        return nil
3✔
240
}
241

242
// addAliasCfg is a struct that hosts various options related to adding a local
243
// alias to the alias manager.
244
type addAliasCfg struct {
245
        // baseLookup signals that the alias should also store a reverse look-up
246
        // to the base scid.
247
        baseLookup bool
248
}
249

250
// AddLocalAliasOption is a functional option that modifies the configuration
251
// for adding a local alias.
252
type AddLocalAliasOption func(cfg *addAliasCfg)
253

254
// WithBaseLookup is a functional option that controls whether a reverse lookup
255
// will be stored from the alias to the base scid.
256
func WithBaseLookup() AddLocalAliasOption {
1✔
257
        return func(cfg *addAliasCfg) {
2✔
258
                cfg.baseLookup = true
1✔
259
        }
1✔
260
}
261

262
// AddLocalAlias adds a database mapping from the passed alias to the passed
263
// base SCID. The gossip boolean marks whether or not to create a mapping
264
// that the gossiper will use. It is set to false for the upgrade path where
265
// the feature-bit is toggled on and there are existing channels. The linkUpdate
266
// flag is used to signal whether this function should also trigger an update on
267
// the htlcswitch scid alias maps.
268
//
269
// NOTE: The following aliases will not be persisted (will be lost on restart):
270
//   - Aliases that were created without gossip flag.
271
//   - Aliases that correspond to confirmed channels.
272
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
273
        gossip, linkUpdate bool, opts ...AddLocalAliasOption) error {
3✔
274

3✔
275
        cfg := addAliasCfg{}
3✔
276
        for _, opt := range opts {
4✔
277
                opt(&cfg)
1✔
278
        }
1✔
279

280
        // We need to lock the manager for the whole duration of this method,
281
        // except for the very last part where we call the link updater. In
282
        // order for us to safely use a defer _and_ still be able to manually
283
        // unlock, we use a sync.Once.
284
        m.Lock()
3✔
285
        unlockOnce := sync.Once{}
3✔
286
        unlock := func() {
9✔
287
                unlockOnce.Do(m.Unlock)
6✔
288
        }
6✔
289
        defer unlock()
3✔
290

3✔
291
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
292
                // If the caller does not want to allow the alias to be used
3✔
293
                // for a channel update, we'll mark it in the baseConfBucket.
3✔
294
                if !gossip {
6✔
295
                        var baseGossipBytes [8]byte
3✔
296
                        byteOrder.PutUint64(
3✔
297
                                baseGossipBytes[:], baseScid.ToUint64(),
3✔
298
                        )
3✔
299

3✔
300
                        confBucket, err := tx.CreateTopLevelBucket(
3✔
301
                                confirmedBucket,
3✔
302
                        )
3✔
303
                        if err != nil {
3✔
304
                                return err
×
305
                        }
×
306

307
                        err = confBucket.Put(baseGossipBytes[:], []byte{})
3✔
308
                        if err != nil {
3✔
309
                                return err
×
310
                        }
×
311
                }
312

313
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
314
                if err != nil {
3✔
315
                        return err
×
316
                }
×
317

318
                var (
3✔
319
                        aliasBytes [8]byte
3✔
320
                        baseBytes  [8]byte
3✔
321
                )
3✔
322

3✔
323
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
3✔
324
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
3✔
325
                return aliasToBaseBucket.Put(aliasBytes[:], baseBytes[:])
3✔
326
        }, func() {})
3✔
327
        if err != nil {
3✔
328
                return err
×
329
        }
×
330

331
        // Update the aliasToBase and baseToSet maps.
332
        m.baseToSet[baseScid] = append(m.baseToSet[baseScid], alias)
3✔
333

3✔
334
        // Only store the gossiper map if gossip is true, or if the caller
3✔
335
        // explicitly asked to store this reverse mapping.
3✔
336
        if gossip || cfg.baseLookup {
4✔
337
                m.aliasToBase[alias] = baseScid
1✔
338
        }
1✔
339

340
        // We definitely need to unlock the Manager before calling the link
341
        // updater. If we don't, we'll deadlock. We use a sync.Once to ensure
342
        // that we only unlock once.
343
        unlock()
3✔
344

3✔
345
        // Finally, we trigger a htlcswitch update if the flag is set, in order
3✔
346
        // for any future htlc that references the added alias to be properly
3✔
347
        // routed.
3✔
348
        if linkUpdate {
6✔
349
                return m.linkAliasUpdater(baseScid)
3✔
350
        }
3✔
351

UNCOV
352
        return nil
×
353
}
354

355
// GetAliases fetches the set of aliases stored under a given base SCID from
356
// write-through caches.
357
func (m *Manager) GetAliases(
358
        base lnwire.ShortChannelID) []lnwire.ShortChannelID {
4✔
359

4✔
360
        m.RLock()
4✔
361
        defer m.RUnlock()
4✔
362

4✔
363
        aliasSet, ok := m.baseToSet[base]
4✔
364
        if ok {
7✔
365
                // Copy the found alias slice.
3✔
366
                setCopy := make([]lnwire.ShortChannelID, len(aliasSet))
3✔
367
                copy(setCopy, aliasSet)
3✔
368
                return setCopy
3✔
369
        }
3✔
370

371
        return nil
1✔
372
}
373

374
// FindBaseSCID finds the base SCID for a given alias. This is used in the
375
// gossiper to find the correct SCID to lookup in the graph database. It can
376
// also be used to look up the base for manual aliases that were added over the
377
// RPC.
378
func (m *Manager) FindBaseSCID(
379
        alias lnwire.ShortChannelID) (lnwire.ShortChannelID, error) {
1✔
380

1✔
381
        m.RLock()
1✔
382
        defer m.RUnlock()
1✔
383

1✔
384
        base, ok := m.aliasToBase[alias]
1✔
385
        if ok {
2✔
386
                return base, nil
1✔
387
        }
1✔
388

UNCOV
389
        return lnwire.ShortChannelID{}, errNoBase
×
390
}
391

392
// DeleteSixConfs removes a mapping for the gossiper once six confirmations
393
// have been reached and the channel is public. At this point, only the
394
// confirmed SCID should be used.
UNCOV
395
func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
×
UNCOV
396
        m.Lock()
×
UNCOV
397
        defer m.Unlock()
×
UNCOV
398

×
UNCOV
399
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
×
UNCOV
400
                baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
×
UNCOV
401
                if err != nil {
×
402
                        return err
×
403
                }
×
404

UNCOV
405
                var baseBytes [8]byte
×
UNCOV
406
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
×
UNCOV
407
                return baseConfBucket.Put(baseBytes[:], []byte{})
×
UNCOV
408
        }, func() {})
×
UNCOV
409
        if err != nil {
×
410
                return err
×
411
        }
×
412

413
        // Now that the database state has been updated, we'll delete all of
414
        // the aliasToBase mappings for this SCID.
UNCOV
415
        for alias, base := range m.aliasToBase {
×
UNCOV
416
                if base.ToUint64() == baseScid.ToUint64() {
×
UNCOV
417
                        delete(m.aliasToBase, alias)
×
UNCOV
418
                }
×
419
        }
420

UNCOV
421
        return nil
×
422
}
423

424
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
425
func (m *Manager) DeleteLocalAlias(alias,
426
        baseScid lnwire.ShortChannelID) error {
3✔
427

3✔
428
        // We need to lock the manager for the whole duration of this method,
3✔
429
        // except for the very last part where we call the link updater. In
3✔
430
        // order for us to safely use a defer _and_ still be able to manually
3✔
431
        // unlock, we use a sync.Once.
3✔
432
        m.Lock()
3✔
433
        unlockOnce := sync.Once{}
3✔
434
        unlock := func() {
8✔
435
                unlockOnce.Do(m.Unlock)
5✔
436
        }
5✔
437
        defer unlock()
3✔
438

3✔
439
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
440
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
441
                if err != nil {
3✔
442
                        return err
×
443
                }
×
444

445
                var aliasBytes [8]byte
3✔
446
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
3✔
447

3✔
448
                // If the user attempts to delete an alias that doesn't exist,
3✔
449
                // we'll want to inform them about it and not just do nothing.
3✔
450
                if aliasToBaseBucket.Get(aliasBytes[:]) == nil {
4✔
451
                        return ErrAliasNotFound
1✔
452
                }
1✔
453

454
                return aliasToBaseBucket.Delete(aliasBytes[:])
2✔
455
        }, func() {})
3✔
456
        if err != nil {
4✔
457
                return err
1✔
458
        }
1✔
459

460
        // Now that the database state has been updated, we'll delete the
461
        // mapping from the Manager's maps.
462
        aliasSet, ok := m.baseToSet[baseScid]
2✔
463
        if !ok {
2✔
464
                return ErrAliasNotFound
×
465
        }
×
466

467
        // We'll filter the alias set and remove the alias from it.
468
        aliasSet = fn.Filter(aliasSet, func(a lnwire.ShortChannelID) bool {
5✔
469
                return a.ToUint64() != alias.ToUint64()
3✔
470
        })
3✔
471

472
        // If the alias set is empty, we'll delete the base SCID from the
473
        // baseToSet map.
474
        if len(aliasSet) == 0 {
3✔
475
                delete(m.baseToSet, baseScid)
1✔
476
        } else {
2✔
477
                m.baseToSet[baseScid] = aliasSet
1✔
478
        }
1✔
479

480
        // Finally, we'll delete the aliasToBase mapping from the Manager's
481
        // cache.
482
        delete(m.aliasToBase, alias)
2✔
483

2✔
484
        // We definitely need to unlock the Manager before calling the link
2✔
485
        // updater. If we don't, we'll deadlock. We use a sync.Once to ensure
2✔
486
        // that we only unlock once.
2✔
487
        unlock()
2✔
488

2✔
489
        return m.linkAliasUpdater(baseScid)
2✔
490
}
491

492
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
493
// channel_ready message.
494
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
495
        alias lnwire.ShortChannelID) error {
1✔
496

1✔
497
        m.Lock()
1✔
498
        defer m.Unlock()
1✔
499

1✔
500
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
2✔
501
                bucket, err := tx.CreateTopLevelBucket(invoiceAliasBucket)
1✔
502
                if err != nil {
1✔
503
                        return err
×
504
                }
×
505

506
                var scratch [8]byte
1✔
507
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
1✔
508
                return bucket.Put(chanID[:], scratch[:])
1✔
509
        }, func() {})
1✔
510
        if err != nil {
1✔
511
                return err
×
512
        }
×
513

514
        // Now that the database state has been updated, we can update it in
515
        // our cache.
516
        m.peerAlias[chanID] = alias
1✔
517

1✔
518
        return nil
1✔
519
}
520

521
// GetPeerAlias retrieves a peer's alias SCID by the channel's ChanID.
522
func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
523
        error) {
1✔
524

1✔
525
        m.RLock()
1✔
526
        defer m.RUnlock()
1✔
527

1✔
528
        alias, ok := m.peerAlias[chanID]
1✔
529
        if !ok || alias == hop.Source {
1✔
UNCOV
530
                return lnwire.ShortChannelID{}, errNoPeerAlias
×
UNCOV
531
        }
×
532

533
        return alias, nil
1✔
534
}
535

536
// RequestAlias returns a new ALIAS ShortChannelID to the caller by allocating
537
// the next un-allocated ShortChannelID. The starting ShortChannelID is
538
// 16000000:0:0 and the ending ShortChannelID is 16250000:16777215:65535. This
539
// gives roughly 2^58 possible ALIAS ShortChannelIDs which ensures this space
540
// won't get exhausted.
541
func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
4✔
542
        var nextAlias lnwire.ShortChannelID
4✔
543

4✔
544
        m.RLock()
4✔
545
        defer m.RUnlock()
4✔
546

4✔
547
        // haveAlias returns true if the passed alias is already assigned to a
4✔
548
        // channel in the baseToSet map.
4✔
549
        haveAlias := func(maybeNextAlias lnwire.ShortChannelID) bool {
9✔
550
                return fn.Any(
5✔
551
                        slices.Collect(maps.Values(m.baseToSet)),
5✔
552
                        func(aliasList []lnwire.ShortChannelID) bool {
7✔
553
                                return fn.Any(
2✔
554
                                        aliasList,
2✔
555
                                        func(alias lnwire.ShortChannelID) bool {
4✔
556
                                                return alias == maybeNextAlias
2✔
557
                                        },
2✔
558
                                )
559
                        },
560
                )
561
        }
562

563
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
8✔
564
                bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
4✔
565
                if err != nil {
4✔
566
                        return err
×
567
                }
×
568

569
                lastBytes := bucket.Get(lastAliasKey)
4✔
570
                if lastBytes == nil {
6✔
571
                        // If the key does not exist, then we can write the
2✔
572
                        // StartingAlias to it.
2✔
573
                        nextAlias = StartingAlias
2✔
574

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

587
                                nextAlias = getNextScid(nextAlias)
×
588

×
589
                                // Abort if we've reached the end of the range.
×
590
                                if nextAlias.BlockHeight >=
×
591
                                        AliasEndBlockHeight {
×
592

×
593
                                        return fmt.Errorf("range for custom " +
×
594
                                                "aliases exhausted")
×
595
                                }
×
596
                        }
597

598
                        var scratch [8]byte
2✔
599
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
2✔
600
                        return bucket.Put(lastAliasKey, scratch[:])
2✔
601
                }
602

603
                // Otherwise the key does exist so we can convert the retrieved
604
                // lastAlias to a ShortChannelID and use it to assign the next
605
                // ShortChannelID. This next ShortChannelID will then be
606
                // persisted in the database.
607
                lastScid := lnwire.NewShortChanIDFromInt(
2✔
608
                        byteOrder.Uint64(lastBytes),
2✔
609
                )
2✔
610
                nextAlias = getNextScid(lastScid)
2✔
611

2✔
612
                // If the next alias is already assigned, we'll keep
2✔
613
                // incrementing until we find an unassigned alias. This is to
2✔
614
                // avoid collision with custom added SCID aliases that fall into
2✔
615
                // the same range as the ones we generate here monotonically.
2✔
616
                // Those custom SCIDs are stored in a different bucket, but we
2✔
617
                // can just check the in-memory map for simplicity.
2✔
618
                for {
5✔
619
                        if !haveAlias(nextAlias) {
5✔
620
                                break
2✔
621
                        }
622

623
                        nextAlias = getNextScid(nextAlias)
1✔
624

1✔
625
                        // Abort if we've reached the end of the range.
1✔
626
                        if nextAlias.BlockHeight >= AliasEndBlockHeight {
1✔
627
                                return fmt.Errorf("range for custom " +
×
628
                                        "aliases exhausted")
×
629
                        }
×
630
                }
631

632
                var scratch [8]byte
2✔
633
                byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
2✔
634
                return bucket.Put(lastAliasKey, scratch[:])
2✔
635
        }, func() {
4✔
636
                nextAlias = lnwire.ShortChannelID{}
4✔
637
        })
4✔
638
        if err != nil {
4✔
639
                return nextAlias, err
×
640
        }
×
641

642
        return nextAlias, nil
4✔
643
}
644

645
// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
646
// layer.
UNCOV
647
func (m *Manager) ListAliases() ScidAliasMap {
×
UNCOV
648
        m.RLock()
×
UNCOV
649
        defer m.RUnlock()
×
UNCOV
650

×
UNCOV
651
        baseCopy := make(ScidAliasMap)
×
UNCOV
652

×
UNCOV
653
        for k, v := range m.baseToSet {
×
UNCOV
654
                setCopy := make([]lnwire.ShortChannelID, len(v))
×
UNCOV
655
                copy(setCopy, v)
×
UNCOV
656
                baseCopy[k] = setCopy
×
UNCOV
657
        }
×
658

UNCOV
659
        return baseCopy
×
660
}
661

662
// getNextScid is a utility function that returns the next SCID for a given
663
// alias SCID. The BlockHeight ranges from [16000000, 16250000], the TxIndex
664
// ranges from [1, 16777215], and the TxPosition ranges from [1, 65535].
665
func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {
10✔
666
        var (
10✔
667
                next            lnwire.ShortChannelID
10✔
668
                incrementIdx    bool
10✔
669
                incrementHeight bool
10✔
670
        )
10✔
671

10✔
672
        // If the TxPosition is 65535, then it goes to 0 and we need to
10✔
673
        // increment the TxIndex.
10✔
674
        if last.TxPosition == 65535 {
12✔
675
                incrementIdx = true
2✔
676
        }
2✔
677

678
        // If the TxIndex is 16777215 and we need to increment it, then it goes
679
        // to 0 and we need to increment the BlockHeight.
680
        if last.TxIndex == 16777215 && incrementIdx {
11✔
681
                incrementIdx = false
1✔
682
                incrementHeight = true
1✔
683
        }
1✔
684

685
        switch {
10✔
686
        // If we increment the TxIndex, then TxPosition goes to 0.
687
        case incrementIdx:
1✔
688
                next.BlockHeight = last.BlockHeight
1✔
689
                next.TxIndex = last.TxIndex + 1
1✔
690
                next.TxPosition = 0
1✔
691

692
        // If we increment the BlockHeight, then the Tx fields go to 0.
693
        case incrementHeight:
1✔
694
                next.BlockHeight = last.BlockHeight + 1
1✔
695
                next.TxIndex = 0
1✔
696
                next.TxPosition = 0
1✔
697

698
        // Otherwise, we only need to increment the TxPosition.
699
        default:
8✔
700
                next.BlockHeight = last.BlockHeight
8✔
701
                next.TxIndex = last.TxIndex
8✔
702
                next.TxPosition = last.TxPosition + 1
8✔
703
        }
704

705
        return next
10✔
706
}
707

708
// IsAlias returns true if the passed SCID is an alias. The function determines
709
// this by looking at the BlockHeight. If the BlockHeight is greater than
710
// AliasStartBlockHeight and less than AliasEndBlockHeight, then it is an alias
711
// assigned by RequestAlias. These bounds only apply to aliases we generate.
712
// Our peers are free to use any range they choose.
UNCOV
713
func IsAlias(scid lnwire.ShortChannelID) bool {
×
UNCOV
714
        return scid.BlockHeight >= AliasStartBlockHeight &&
×
UNCOV
715
                scid.BlockHeight < AliasEndBlockHeight
×
UNCOV
716
}
×
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