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

lightningnetwork / lnd / 17794908832

17 Sep 2025 10:39AM UTC coverage: 54.627% (-12.0%) from 66.657%
17794908832

Pull #10133

github

web-flow
Merge d8213189d into cbed86e21
Pull Request #10133: Add `XFindBaseLocalChanAlias` RPC

6 of 20 new or added lines in 4 files covered. (30.0%)

23935 existing lines in 283 files now uncovered.

109276 of 200040 relevant lines covered (54.63%)

21898.82 hits per line

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

70.98
/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
// 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 base
246
// lookup flag indicates whether we want store a mapping from the alias to its
247
// base scid. The linkUpdate flag is used to signal whether this function should
248
// also trigger an update on the htlcswitch scid alias maps.
249
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
250
        gossip, baseLookup, linkUpdate bool) error {
3✔
251

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

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

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

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

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

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

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

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

3✔
306
        // Only store the gossiper map if gossip is true, or if the caller
3✔
307
        // explicitly asked to store this reverse mapping.
3✔
308
        if gossip || baseLookup {
4✔
309
                m.aliasToBase[alias] = baseScid
1✔
310
        }
1✔
311

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

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

UNCOV
324
        return nil
×
325
}
326

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

4✔
332
        m.RLock()
4✔
333
        defer m.RUnlock()
4✔
334

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

343
        return nil
1✔
344
}
345

346
// FindBaseSCID finds the base SCID for a given alias. This is used in the
347
// gossiper to find the correct SCID to lookup in the graph database. It can
348
// also be used to look up the base for manual aliases that were added over the
349
// RPC.
350
func (m *Manager) FindBaseSCID(
351
        alias lnwire.ShortChannelID) (lnwire.ShortChannelID, error) {
1✔
352

1✔
353
        m.RLock()
1✔
354
        defer m.RUnlock()
1✔
355

1✔
356
        base, ok := m.aliasToBase[alias]
1✔
357
        if ok {
2✔
358
                return base, nil
1✔
359
        }
1✔
360

UNCOV
361
        return lnwire.ShortChannelID{}, errNoBase
×
362
}
363

364
// DeleteSixConfs removes a mapping for the gossiper once six confirmations
365
// have been reached and the channel is public. At this point, only the
366
// confirmed SCID should be used.
UNCOV
367
func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
×
UNCOV
368
        m.Lock()
×
UNCOV
369
        defer m.Unlock()
×
UNCOV
370

×
UNCOV
371
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
×
UNCOV
372
                baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
×
UNCOV
373
                if err != nil {
×
374
                        return err
×
375
                }
×
376

UNCOV
377
                var baseBytes [8]byte
×
UNCOV
378
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
×
UNCOV
379
                return baseConfBucket.Put(baseBytes[:], []byte{})
×
UNCOV
380
        }, func() {})
×
UNCOV
381
        if err != nil {
×
382
                return err
×
383
        }
×
384

385
        // Now that the database state has been updated, we'll delete all of
386
        // the aliasToBase mappings for this SCID.
UNCOV
387
        for alias, base := range m.aliasToBase {
×
UNCOV
388
                if base.ToUint64() == baseScid.ToUint64() {
×
UNCOV
389
                        delete(m.aliasToBase, alias)
×
UNCOV
390
                }
×
391
        }
392

UNCOV
393
        return nil
×
394
}
395

396
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
397
func (m *Manager) DeleteLocalAlias(alias,
398
        baseScid lnwire.ShortChannelID) error {
3✔
399

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

3✔
411
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
412
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
413
                if err != nil {
3✔
414
                        return err
×
415
                }
×
416

417
                var aliasBytes [8]byte
3✔
418
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
3✔
419

3✔
420
                // If the user attempts to delete an alias that doesn't exist,
3✔
421
                // we'll want to inform them about it and not just do nothing.
3✔
422
                if aliasToBaseBucket.Get(aliasBytes[:]) == nil {
4✔
423
                        return ErrAliasNotFound
1✔
424
                }
1✔
425

426
                return aliasToBaseBucket.Delete(aliasBytes[:])
2✔
427
        }, func() {})
3✔
428
        if err != nil {
4✔
429
                return err
1✔
430
        }
1✔
431

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

439
        // We'll filter the alias set and remove the alias from it.
440
        aliasSet = fn.Filter(aliasSet, func(a lnwire.ShortChannelID) bool {
5✔
441
                return a.ToUint64() != alias.ToUint64()
3✔
442
        })
3✔
443

444
        // If the alias set is empty, we'll delete the base SCID from the
445
        // baseToSet map.
446
        if len(aliasSet) == 0 {
3✔
447
                delete(m.baseToSet, baseScid)
1✔
448
        } else {
2✔
449
                m.baseToSet[baseScid] = aliasSet
1✔
450
        }
1✔
451

452
        // Finally, we'll delete the aliasToBase mapping from the Manager's
453
        // cache.
454
        delete(m.aliasToBase, alias)
2✔
455

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

2✔
461
        return m.linkAliasUpdater(baseScid)
2✔
462
}
463

464
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
465
// channel_ready message.
466
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
467
        alias lnwire.ShortChannelID) error {
1✔
468

1✔
469
        m.Lock()
1✔
470
        defer m.Unlock()
1✔
471

1✔
472
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
2✔
473
                bucket, err := tx.CreateTopLevelBucket(invoiceAliasBucket)
1✔
474
                if err != nil {
1✔
475
                        return err
×
476
                }
×
477

478
                var scratch [8]byte
1✔
479
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
1✔
480
                return bucket.Put(chanID[:], scratch[:])
1✔
481
        }, func() {})
1✔
482
        if err != nil {
1✔
483
                return err
×
484
        }
×
485

486
        // Now that the database state has been updated, we can update it in
487
        // our cache.
488
        m.peerAlias[chanID] = alias
1✔
489

1✔
490
        return nil
1✔
491
}
492

493
// GetPeerAlias retrieves a peer's alias SCID by the channel's ChanID.
494
func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
495
        error) {
1✔
496

1✔
497
        m.RLock()
1✔
498
        defer m.RUnlock()
1✔
499

1✔
500
        alias, ok := m.peerAlias[chanID]
1✔
501
        if !ok || alias == hop.Source {
1✔
UNCOV
502
                return lnwire.ShortChannelID{}, errNoPeerAlias
×
UNCOV
503
        }
×
504

505
        return alias, nil
1✔
506
}
507

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

4✔
516
        m.RLock()
4✔
517
        defer m.RUnlock()
4✔
518

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

535
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
8✔
536
                bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
4✔
537
                if err != nil {
4✔
538
                        return err
×
539
                }
×
540

541
                lastBytes := bucket.Get(lastAliasKey)
4✔
542
                if lastBytes == nil {
6✔
543
                        // If the key does not exist, then we can write the
2✔
544
                        // StartingAlias to it.
2✔
545
                        nextAlias = StartingAlias
2✔
546

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

559
                                nextAlias = getNextScid(nextAlias)
×
560

×
561
                                // Abort if we've reached the end of the range.
×
562
                                if nextAlias.BlockHeight >=
×
563
                                        AliasEndBlockHeight {
×
564

×
565
                                        return fmt.Errorf("range for custom " +
×
566
                                                "aliases exhausted")
×
567
                                }
×
568
                        }
569

570
                        var scratch [8]byte
2✔
571
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
2✔
572
                        return bucket.Put(lastAliasKey, scratch[:])
2✔
573
                }
574

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

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

595
                        nextAlias = getNextScid(nextAlias)
1✔
596

1✔
597
                        // Abort if we've reached the end of the range.
1✔
598
                        if nextAlias.BlockHeight >= AliasEndBlockHeight {
1✔
599
                                return fmt.Errorf("range for custom " +
×
600
                                        "aliases exhausted")
×
601
                        }
×
602
                }
603

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

614
        return nextAlias, nil
4✔
615
}
616

617
// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
618
// layer.
UNCOV
619
func (m *Manager) ListAliases() ScidAliasMap {
×
UNCOV
620
        m.RLock()
×
UNCOV
621
        defer m.RUnlock()
×
UNCOV
622

×
UNCOV
623
        baseCopy := make(ScidAliasMap)
×
UNCOV
624

×
UNCOV
625
        for k, v := range m.baseToSet {
×
UNCOV
626
                setCopy := make([]lnwire.ShortChannelID, len(v))
×
UNCOV
627
                copy(setCopy, v)
×
UNCOV
628
                baseCopy[k] = setCopy
×
UNCOV
629
        }
×
630

UNCOV
631
        return baseCopy
×
632
}
633

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

10✔
644
        // If the TxPosition is 65535, then it goes to 0 and we need to
10✔
645
        // increment the TxIndex.
10✔
646
        if last.TxPosition == 65535 {
12✔
647
                incrementIdx = true
2✔
648
        }
2✔
649

650
        // If the TxIndex is 16777215 and we need to increment it, then it goes
651
        // to 0 and we need to increment the BlockHeight.
652
        if last.TxIndex == 16777215 && incrementIdx {
11✔
653
                incrementIdx = false
1✔
654
                incrementHeight = true
1✔
655
        }
1✔
656

657
        switch {
10✔
658
        // If we increment the TxIndex, then TxPosition goes to 0.
659
        case incrementIdx:
1✔
660
                next.BlockHeight = last.BlockHeight
1✔
661
                next.TxIndex = last.TxIndex + 1
1✔
662
                next.TxPosition = 0
1✔
663

664
        // If we increment the BlockHeight, then the Tx fields go to 0.
665
        case incrementHeight:
1✔
666
                next.BlockHeight = last.BlockHeight + 1
1✔
667
                next.TxIndex = 0
1✔
668
                next.TxPosition = 0
1✔
669

670
        // Otherwise, we only need to increment the TxPosition.
671
        default:
8✔
672
                next.BlockHeight = last.BlockHeight
8✔
673
                next.TxIndex = last.TxIndex
8✔
674
                next.TxPosition = last.TxPosition + 1
8✔
675
        }
676

677
        return next
10✔
678
}
679

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