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

lightningnetwork / lnd / 19155841408

07 Nov 2025 02:03AM UTC coverage: 66.675% (-0.04%) from 66.712%
19155841408

Pull #10352

github

web-flow
Merge e4313eba8 into 096ab65b1
Pull Request #10352: [WIP] chainrpc: return Unavailable while notifier starts

137328 of 205965 relevant lines covered (66.68%)

21333.36 hits per line

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

88.6
/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) {
6✔
124

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

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

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

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

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

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

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

159
                err = baseConfBucket.ForEach(func(k, v []byte) error {
9✔
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 {
6✔
170
                        return err
×
171
                }
×
172

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

178
                err = aliasToBaseBucket.ForEach(func(k, v []byte) error {
9✔
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 {
6✔
191
                        return err
×
192
                }
×
193

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

201
                err = invAliasBucket.ForEach(func(k, v []byte) error {
9✔
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
6✔
214
        }, func() {
6✔
215
                baseConfMap = make(map[lnwire.ShortChannelID]struct{})
6✔
216
                aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
6✔
217
                peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
6✔
218
        })
6✔
219
        if err != nil {
6✔
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 {
9✔
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
6✔
238

6✔
239
        return nil
6✔
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 {
4✔
257
        return func(cfg *addAliasCfg) {
8✔
258
                cfg.baseLookup = true
4✔
259
        }
4✔
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 {
6✔
274

6✔
275
        cfg := addAliasCfg{}
6✔
276
        for _, opt := range opts {
10✔
277
                opt(&cfg)
4✔
278
        }
4✔
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()
6✔
285
        unlockOnce := sync.Once{}
6✔
286
        unlock := func() {
15✔
287
                unlockOnce.Do(m.Unlock)
9✔
288
        }
9✔
289
        defer unlock()
6✔
290

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

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

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

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

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

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

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

6✔
334
        // Only store the gossiper map if gossip is true, or if the caller
6✔
335
        // explicitly asked to store this reverse mapping.
6✔
336
        if gossip || cfg.baseLookup {
10✔
337
                m.aliasToBase[alias] = baseScid
4✔
338
        }
4✔
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()
6✔
344

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

352
        return nil
3✔
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 {
7✔
359

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

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

371
        return nil
4✔
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) {
4✔
380

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

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

389
        return lnwire.ShortChannelID{}, errNoBase
3✔
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.
395
func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
3✔
396
        m.Lock()
3✔
397
        defer m.Unlock()
3✔
398

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

405
                var baseBytes [8]byte
3✔
406
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
3✔
407
                return baseConfBucket.Put(baseBytes[:], []byte{})
3✔
408
        }, func() {})
3✔
409
        if err != nil {
3✔
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.
415
        for alias, base := range m.aliasToBase {
6✔
416
                if base.ToUint64() == baseScid.ToUint64() {
6✔
417
                        delete(m.aliasToBase, alias)
3✔
418
                }
3✔
419
        }
420

421
        return nil
3✔
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 {
6✔
427

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

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

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

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

454
                return aliasToBaseBucket.Delete(aliasBytes[:])
5✔
455
        }, func() {})
6✔
456
        if err != nil {
7✔
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]
5✔
463
        if !ok {
5✔
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 {
11✔
469
                return a.ToUint64() != alias.ToUint64()
6✔
470
        })
6✔
471

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

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

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

5✔
489
        return m.linkAliasUpdater(baseScid)
5✔
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 {
4✔
496

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

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

506
                var scratch [8]byte
4✔
507
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
4✔
508
                return bucket.Put(chanID[:], scratch[:])
4✔
509
        }, func() {})
4✔
510
        if err != nil {
4✔
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
4✔
517

4✔
518
        return nil
4✔
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) {
4✔
524

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

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

533
        return alias, nil
4✔
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) {
7✔
542
        var nextAlias lnwire.ShortChannelID
7✔
543

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

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

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

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

5✔
575
                        // If the very first alias is already assigned, we'll
5✔
576
                        // keep incrementing until we find an unassigned alias.
5✔
577
                        // This is to avoid collision with custom added SCID
5✔
578
                        // aliases that fall into the same range as the ones we
5✔
579
                        // generate here monotonically. Those custom SCIDs are
5✔
580
                        // stored in a different bucket, but we can just check
5✔
581
                        // the in-memory map for simplicity.
5✔
582
                        for {
10✔
583
                                if !haveAlias(nextAlias) {
10✔
584
                                        break
5✔
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
5✔
599
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
5✔
600
                        return bucket.Put(lastAliasKey, scratch[:])
5✔
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(
5✔
608
                        byteOrder.Uint64(lastBytes),
5✔
609
                )
5✔
610
                nextAlias = getNextScid(lastScid)
5✔
611

5✔
612
                // If the next alias is already assigned, we'll keep
5✔
613
                // incrementing until we find an unassigned alias. This is to
5✔
614
                // avoid collision with custom added SCID aliases that fall into
5✔
615
                // the same range as the ones we generate here monotonically.
5✔
616
                // Those custom SCIDs are stored in a different bucket, but we
5✔
617
                // can just check the in-memory map for simplicity.
5✔
618
                for {
11✔
619
                        if !haveAlias(nextAlias) {
11✔
620
                                break
5✔
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
5✔
633
                byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
5✔
634
                return bucket.Put(lastAliasKey, scratch[:])
5✔
635
        }, func() {
7✔
636
                nextAlias = lnwire.ShortChannelID{}
7✔
637
        })
7✔
638
        if err != nil {
7✔
639
                return nextAlias, err
×
640
        }
×
641

642
        return nextAlias, nil
7✔
643
}
644

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

3✔
651
        baseCopy := make(ScidAliasMap)
3✔
652

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

659
        return baseCopy
3✔
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 {
13✔
666
        var (
13✔
667
                next            lnwire.ShortChannelID
13✔
668
                incrementIdx    bool
13✔
669
                incrementHeight bool
13✔
670
        )
13✔
671

13✔
672
        // If the TxPosition is 65535, then it goes to 0 and we need to
13✔
673
        // increment the TxIndex.
13✔
674
        if last.TxPosition == 65535 {
15✔
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 {
14✔
681
                incrementIdx = false
1✔
682
                incrementHeight = true
1✔
683
        }
1✔
684

685
        switch {
13✔
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:
11✔
700
                next.BlockHeight = last.BlockHeight
11✔
701
                next.TxIndex = last.TxIndex
11✔
702
                next.TxPosition = last.TxPosition + 1
11✔
703
        }
704

705
        return next
13✔
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.
713
func IsAlias(scid lnwire.ShortChannelID) bool {
3✔
714
        return scid.BlockHeight >= AliasStartBlockHeight &&
3✔
715
                scid.BlockHeight < AliasEndBlockHeight
3✔
716
}
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