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

lightningnetwork / lnd / 17832014233

18 Sep 2025 02:30PM UTC coverage: 57.196% (-9.4%) from 66.637%
17832014233

Pull #10133

github

web-flow
Merge 3e12b2767 into b34fc964b
Pull Request #10133: Add `XFindBaseLocalChanAlias` RPC

20 of 34 new or added lines in 4 files covered. (58.82%)

28528 existing lines in 459 files now uncovered.

99371 of 173739 relevant lines covered (57.2%)

1.78 hits per line

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

83.97
/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
        // aliasAllocBucket is a root-level bucket that stores the last alias
37
        // that was allocated. It is used to allocate a new alias when
38
        // requested.
39
        aliasAllocBucket = []byte("alias-alloc-bucket")
40

41
        // lastAliasKey is a key in the aliasAllocBucket whose value is the
42
        // last allocated alias ShortChannelID. This will be updated upon calls
43
        // to RequestAlias.
44
        lastAliasKey = []byte("last-alias-key")
45

46
        // invoiceAliasBucket is a root-level bucket that stores the alias
47
        // SCIDs that our peers send us in the channel_ready TLV. The keys are
48
        // the ChannelID generated from the FundingOutpoint and the values are
49
        // the remote peer's alias SCID.
50
        invoiceAliasBucket = []byte("invoice-alias-bucket")
51

52
        // byteOrder denotes the byte order of database (de)-serialization
53
        // operations.
54
        byteOrder = binary.BigEndian
55

56
        // AliasStartBlockHeight is the starting block height of the alias
57
        // range.
58
        AliasStartBlockHeight uint32 = 16_000_000
59

60
        // AliasEndBlockHeight is the ending block height of the alias range.
61
        AliasEndBlockHeight uint32 = 16_250_000
62

63
        // StartingAlias is the first alias ShortChannelID that will get
64
        // assigned by RequestAlias. The starting BlockHeight is chosen so that
65
        // legitimate SCIDs in integration tests aren't mistaken for an alias.
66
        StartingAlias = lnwire.ShortChannelID{
67
                BlockHeight: AliasStartBlockHeight,
68
                TxIndex:     0,
69
                TxPosition:  0,
70
        }
71

72
        // errNoBase is returned when a base SCID isn't found.
73
        errNoBase = fmt.Errorf("no base found")
74

75
        // errNoPeerAlias is returned when the peer's alias for a given
76
        // channel is not found.
77
        errNoPeerAlias = fmt.Errorf("no peer alias found")
78

79
        // ErrAliasNotFound is returned when the alias is not found and can't
80
        // be mapped to a base SCID.
81
        ErrAliasNotFound = fmt.Errorf("alias not found")
82
)
83

84
// Manager is a struct that handles aliases for LND. It has an underlying
85
// database that can allocate aliases for channels, stores the peer's last
86
// alias for use in our hop hints, and contains mappings that both the Switch
87
// and Gossiper use.
88
type Manager struct {
89
        backend kvdb.Backend
90

91
        // linkAliasUpdater is a function used by the alias manager to
92
        // facilitate live update of aliases in other subsystems.
93
        linkAliasUpdater UpdateLinkAliases
94

95
        // baseToSet is a mapping from the "base" SCID to the set of aliases
96
        // for this channel. This mapping includes all channels that
97
        // negotiated the option-scid-alias feature bit.
98
        baseToSet ScidAliasMap
99

100
        // aliasToBase is a mapping that maps all aliases for a given channel
101
        // to its base SCID. This is only used for channels that have
102
        // negotiated option-scid-alias feature bit.
103
        aliasToBase map[lnwire.ShortChannelID]lnwire.ShortChannelID
104

105
        // peerAlias is a cache for the alias SCIDs that our peers send us in
106
        // the channel_ready TLV. The keys are the ChannelID generated from
107
        // the FundingOutpoint and the values are the remote peer's alias SCID.
108
        // The values should match the ones stored in the "invoice-alias-bucket"
109
        // bucket.
110
        peerAlias map[lnwire.ChannelID]lnwire.ShortChannelID
111

112
        sync.RWMutex
113
}
114

115
// NewManager initializes an alias Manager from the passed database backend.
116
func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager,
117
        error) {
3✔
118

3✔
119
        m := &Manager{
3✔
120
                backend:          db,
3✔
121
                baseToSet:        make(ScidAliasMap),
3✔
122
                linkAliasUpdater: linkAliasUpdater,
3✔
123
        }
3✔
124

3✔
125
        m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
126
        m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
127

3✔
128
        err := m.populateMaps()
3✔
129
        return m, err
3✔
130
}
3✔
131

132
// populateMaps reads the database state and populates the maps.
133
func (m *Manager) populateMaps() error {
3✔
134
        // This map caches what is found in the database and is used to
3✔
135
        // populate the Manager's actual maps.
3✔
136
        aliasMap := make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
137

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

3✔
142
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
143
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
144
                if err != nil {
3✔
145
                        return err
×
146
                }
×
147

148
                err = aliasToBaseBucket.ForEach(func(k, v []byte) error {
6✔
149
                        // The key will be the alias SCID and the value will be
3✔
150
                        // the base SCID.
3✔
151
                        aliasScid := lnwire.NewShortChanIDFromInt(
3✔
152
                                byteOrder.Uint64(k),
3✔
153
                        )
3✔
154
                        baseScid := lnwire.NewShortChanIDFromInt(
3✔
155
                                byteOrder.Uint64(v),
3✔
156
                        )
3✔
157
                        aliasMap[aliasScid] = baseScid
3✔
158
                        return nil
3✔
159
                })
3✔
160
                if err != nil {
3✔
161
                        return err
×
162
                }
×
163

164
                invAliasBucket, err := tx.CreateTopLevelBucket(
3✔
165
                        invoiceAliasBucket,
3✔
166
                )
3✔
167
                if err != nil {
3✔
168
                        return err
×
169
                }
×
170

171
                err = invAliasBucket.ForEach(func(k, v []byte) error {
6✔
172
                        var chanID lnwire.ChannelID
3✔
173
                        copy(chanID[:], k)
3✔
174
                        alias := lnwire.NewShortChanIDFromInt(
3✔
175
                                byteOrder.Uint64(v),
3✔
176
                        )
3✔
177

3✔
178
                        peerAliasMap[chanID] = alias
3✔
179

3✔
180
                        return nil
3✔
181
                })
3✔
182

183
                return err
3✔
184
        }, func() {
3✔
185
                aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
186
                peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
187
        })
3✔
188
        if err != nil {
3✔
189
                return err
×
190
        }
×
191

192
        // Populate the baseToSet and aliasToBase maps. Any aliases that were
193
        // present before the channel confirmation have been deleted, so it's
194
        // safe to restore any entries that are present in the bucket.
195
        for aliasSCID, baseSCID := range aliasMap {
6✔
196
                m.baseToSet[baseSCID] = append(m.baseToSet[baseSCID], aliasSCID)
3✔
197
                m.aliasToBase[aliasSCID] = baseSCID
3✔
198
        }
3✔
199

200
        // Populate the peer alias cache.
201
        m.peerAlias = peerAliasMap
3✔
202

3✔
203
        return nil
3✔
204
}
205

206
// AddLocalAlias adds a database mapping from the passed alias to the passed
207
// base SCID. The linkUpdate flag is used to signal whether this function should
208
// also trigger an update on the htlcswitch scid alias maps. Any aliases that
209
// may have been added before the channel reaches the conf height will be
210
// deleted when the channel gets confirmed.
211
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
212
        linkUpdate bool) error {
3✔
213

3✔
214
        // We need to lock the manager for the whole duration of this method,
3✔
215
        // except for the very last part where we call the link updater. In
3✔
216
        // order for us to safely use a defer _and_ still be able to manually
3✔
217
        // unlock, we use a sync.Once.
3✔
218
        m.Lock()
3✔
219
        unlockOnce := sync.Once{}
3✔
220
        unlock := func() {
6✔
221
                unlockOnce.Do(m.Unlock)
3✔
222
        }
3✔
223
        defer unlock()
3✔
224

3✔
225
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
226
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
227
                if err != nil {
3✔
228
                        return err
×
229
                }
×
230

231
                var (
3✔
232
                        aliasBytes [8]byte
3✔
233
                        baseBytes  [8]byte
3✔
234
                )
3✔
235

3✔
236
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
3✔
237
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
3✔
238
                return aliasToBaseBucket.Put(aliasBytes[:], baseBytes[:])
3✔
239
        }, func() {})
3✔
240
        if err != nil {
3✔
241
                return err
×
242
        }
×
243

244
        // Update the aliasToBase and baseToSet maps.
245
        m.baseToSet[baseScid] = append(m.baseToSet[baseScid], alias)
3✔
246
        m.aliasToBase[alias] = baseScid
3✔
247

3✔
248
        // We definitely need to unlock the Manager before calling the link
3✔
249
        // updater. If we don't, we'll deadlock. We use a sync.Once to ensure
3✔
250
        // that we only unlock once.
3✔
251
        unlock()
3✔
252

3✔
253
        // Finally, we trigger a htlcswitch update if the flag is set, in order
3✔
254
        // for any future htlc that references the added alias to be properly
3✔
255
        // routed.
3✔
256
        if linkUpdate {
6✔
257
                return m.linkAliasUpdater(baseScid)
3✔
258
        }
3✔
259

260
        return nil
3✔
261
}
262

263
// GetAliases fetches the set of aliases stored under a given base SCID from
264
// write-through caches.
265
func (m *Manager) GetAliases(
266
        base lnwire.ShortChannelID) []lnwire.ShortChannelID {
3✔
267

3✔
268
        m.RLock()
3✔
269
        defer m.RUnlock()
3✔
270

3✔
271
        aliasSet, ok := m.baseToSet[base]
3✔
272
        if ok {
6✔
273
                // Copy the found alias slice.
3✔
274
                setCopy := make([]lnwire.ShortChannelID, len(aliasSet))
3✔
275
                copy(setCopy, aliasSet)
3✔
276
                return setCopy
3✔
277
        }
3✔
278

279
        return nil
3✔
280
}
281

282
// FindBaseSCID finds the base SCID for a given alias. This is used in the
283
// gossiper to find the correct SCID to lookup in the graph database. It can
284
// also be used to look up the base for manual aliases that were added over the
285
// RPC.
286
func (m *Manager) FindBaseSCID(
287
        alias lnwire.ShortChannelID) (lnwire.ShortChannelID, error) {
3✔
288

3✔
289
        m.RLock()
3✔
290
        defer m.RUnlock()
3✔
291

3✔
292
        base, ok := m.aliasToBase[alias]
3✔
293
        if ok {
6✔
294
                return base, nil
3✔
295
        }
3✔
296

297
        return lnwire.ShortChannelID{}, errNoBase
3✔
298
}
299

300
// DeleteSixConfs removes a mapping for the gossiper once six confirmations
301
// have been reached and the channel is public. At this point, only the
302
// confirmed SCID should be used.
303
func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
3✔
304
        m.Lock()
3✔
305
        defer m.Unlock()
3✔
306

3✔
307
        // Now we'll delete all of the alias mappings for this base scid,
3✔
308
        // purging them both from memory and storage. This will also prevent the
3✔
309
        // mappings from being re-populated on restart.
3✔
310
        for alias, base := range m.aliasToBase {
6✔
311
                if base.ToUint64() == baseScid.ToUint64() {
6✔
312
                        m.deleteAlias(alias, base)
3✔
313
                }
3✔
314
        }
315

316
        return nil
3✔
317
}
318

319
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
320
func (m *Manager) DeleteLocalAlias(alias,
321
        baseScid lnwire.ShortChannelID) error {
3✔
322

3✔
323
        // We need to lock the manager for the whole duration of this method,
3✔
324
        // except for the very last part where we call the link updater. In
3✔
325
        // order for us to safely use a defer _and_ still be able to manually
3✔
326
        // unlock, we use a sync.Once.
3✔
327
        m.Lock()
3✔
328
        unlockOnce := sync.Once{}
3✔
329
        unlock := func() {
6✔
330
                unlockOnce.Do(m.Unlock)
3✔
331
        }
3✔
332
        defer unlock()
3✔
333

3✔
334
        // We now call the internal method to delete the alias from storage and
3✔
335
        // memory.
3✔
336
        err := m.deleteAlias(alias, baseScid)
3✔
337
        if err != nil {
3✔
NEW
338
                return err
×
NEW
339
        }
×
340

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

3✔
346
        return m.linkAliasUpdater(baseScid)
3✔
347
}
348

349
// deleteAlias deletes an internal local alias mapping from both the database
350
// and any in-memory mappings.
351
//
352
// NOTE: Caller must acquire lock before calling.
353
func (m *Manager) deleteAlias(alias, baseScid lnwire.ShortChannelID) error {
3✔
354
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
355
                aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
3✔
356
                if err != nil {
3✔
357
                        return err
×
358
                }
×
359

360
                var aliasBytes [8]byte
3✔
361
                byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
3✔
362

3✔
363
                // If the user attempts to delete an alias that doesn't exist,
3✔
364
                // we'll want to inform them about it and not just do nothing.
3✔
365
                if aliasToBaseBucket.Get(aliasBytes[:]) == nil {
3✔
UNCOV
366
                        return ErrAliasNotFound
×
UNCOV
367
                }
×
368

369
                return aliasToBaseBucket.Delete(aliasBytes[:])
3✔
370
        }, func() {})
3✔
371
        if err != nil {
3✔
UNCOV
372
                return err
×
UNCOV
373
        }
×
374

375
        // Now that the database state has been updated, we'll delete the
376
        // mapping from the Manager's maps.
377
        aliasSet, ok := m.baseToSet[baseScid]
3✔
378
        if !ok {
3✔
379
                return ErrAliasNotFound
×
380
        }
×
381

382
        // We'll filter the alias set and remove the alias from it.
383
        aliasSet = fn.Filter(aliasSet, func(a lnwire.ShortChannelID) bool {
6✔
384
                return a.ToUint64() != alias.ToUint64()
3✔
385
        })
3✔
386

387
        // If the alias set is empty, we'll delete the base SCID from the
388
        // baseToSet map.
389
        if len(aliasSet) == 0 {
6✔
390
                delete(m.baseToSet, baseScid)
3✔
391
        } else {
6✔
392
                m.baseToSet[baseScid] = aliasSet
3✔
393
        }
3✔
394

395
        // Finally, we'll delete the aliasToBase mapping from the Manager's
396
        // cache.
397
        delete(m.aliasToBase, alias)
3✔
398

3✔
399
        return nil
3✔
400
}
401

402
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
403
// channel_ready message.
404
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
405
        alias lnwire.ShortChannelID) error {
3✔
406

3✔
407
        m.Lock()
3✔
408
        defer m.Unlock()
3✔
409

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

416
                var scratch [8]byte
3✔
417
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
3✔
418
                return bucket.Put(chanID[:], scratch[:])
3✔
419
        }, func() {})
3✔
420
        if err != nil {
3✔
421
                return err
×
422
        }
×
423

424
        // Now that the database state has been updated, we can update it in
425
        // our cache.
426
        m.peerAlias[chanID] = alias
3✔
427

3✔
428
        return nil
3✔
429
}
430

431
// GetPeerAlias retrieves a peer's alias SCID by the channel's ChanID.
432
func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
433
        error) {
3✔
434

3✔
435
        m.RLock()
3✔
436
        defer m.RUnlock()
3✔
437

3✔
438
        alias, ok := m.peerAlias[chanID]
3✔
439
        if !ok || alias == hop.Source {
6✔
440
                return lnwire.ShortChannelID{}, errNoPeerAlias
3✔
441
        }
3✔
442

443
        return alias, nil
3✔
444
}
445

446
// RequestAlias returns a new ALIAS ShortChannelID to the caller by allocating
447
// the next un-allocated ShortChannelID. The starting ShortChannelID is
448
// 16000000:0:0 and the ending ShortChannelID is 16250000:16777215:65535. This
449
// gives roughly 2^58 possible ALIAS ShortChannelIDs which ensures this space
450
// won't get exhausted.
451
func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
3✔
452
        var nextAlias lnwire.ShortChannelID
3✔
453

3✔
454
        m.RLock()
3✔
455
        defer m.RUnlock()
3✔
456

3✔
457
        // haveAlias returns true if the passed alias is already assigned to a
3✔
458
        // channel in the baseToSet map.
3✔
459
        haveAlias := func(maybeNextAlias lnwire.ShortChannelID) bool {
6✔
460
                return fn.Any(
3✔
461
                        slices.Collect(maps.Values(m.baseToSet)),
3✔
462
                        func(aliasList []lnwire.ShortChannelID) bool {
6✔
463
                                return fn.Any(
3✔
464
                                        aliasList,
3✔
465
                                        func(alias lnwire.ShortChannelID) bool {
6✔
466
                                                return alias == maybeNextAlias
3✔
467
                                        },
3✔
468
                                )
469
                        },
470
                )
471
        }
472

473
        err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
6✔
474
                bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
3✔
475
                if err != nil {
3✔
476
                        return err
×
477
                }
×
478

479
                lastBytes := bucket.Get(lastAliasKey)
3✔
480
                if lastBytes == nil {
6✔
481
                        // If the key does not exist, then we can write the
3✔
482
                        // StartingAlias to it.
3✔
483
                        nextAlias = StartingAlias
3✔
484

3✔
485
                        // If the very first alias is already assigned, we'll
3✔
486
                        // keep incrementing until we find an unassigned alias.
3✔
487
                        // This is to avoid collision with custom added SCID
3✔
488
                        // aliases that fall into the same range as the ones we
3✔
489
                        // generate here monotonically. Those custom SCIDs are
3✔
490
                        // stored in a different bucket, but we can just check
3✔
491
                        // the in-memory map for simplicity.
3✔
492
                        for {
6✔
493
                                if !haveAlias(nextAlias) {
6✔
494
                                        break
3✔
495
                                }
496

497
                                nextAlias = getNextScid(nextAlias)
×
498

×
499
                                // Abort if we've reached the end of the range.
×
500
                                if nextAlias.BlockHeight >=
×
501
                                        AliasEndBlockHeight {
×
502

×
503
                                        return fmt.Errorf("range for custom " +
×
504
                                                "aliases exhausted")
×
505
                                }
×
506
                        }
507

508
                        var scratch [8]byte
3✔
509
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
3✔
510
                        return bucket.Put(lastAliasKey, scratch[:])
3✔
511
                }
512

513
                // Otherwise the key does exist so we can convert the retrieved
514
                // lastAlias to a ShortChannelID and use it to assign the next
515
                // ShortChannelID. This next ShortChannelID will then be
516
                // persisted in the database.
517
                lastScid := lnwire.NewShortChanIDFromInt(
3✔
518
                        byteOrder.Uint64(lastBytes),
3✔
519
                )
3✔
520
                nextAlias = getNextScid(lastScid)
3✔
521

3✔
522
                // If the next alias is already assigned, we'll keep
3✔
523
                // incrementing until we find an unassigned alias. This is to
3✔
524
                // avoid collision with custom added SCID aliases that fall into
3✔
525
                // the same range as the ones we generate here monotonically.
3✔
526
                // Those custom SCIDs are stored in a different bucket, but we
3✔
527
                // can just check the in-memory map for simplicity.
3✔
528
                for {
6✔
529
                        if !haveAlias(nextAlias) {
6✔
530
                                break
3✔
531
                        }
532

UNCOV
533
                        nextAlias = getNextScid(nextAlias)
×
UNCOV
534

×
UNCOV
535
                        // Abort if we've reached the end of the range.
×
UNCOV
536
                        if nextAlias.BlockHeight >= AliasEndBlockHeight {
×
537
                                return fmt.Errorf("range for custom " +
×
538
                                        "aliases exhausted")
×
539
                        }
×
540
                }
541

542
                var scratch [8]byte
3✔
543
                byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
3✔
544
                return bucket.Put(lastAliasKey, scratch[:])
3✔
545
        }, func() {
3✔
546
                nextAlias = lnwire.ShortChannelID{}
3✔
547
        })
3✔
548
        if err != nil {
3✔
549
                return nextAlias, err
×
550
        }
×
551

552
        return nextAlias, nil
3✔
553
}
554

555
// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
556
// layer.
557
func (m *Manager) ListAliases() ScidAliasMap {
3✔
558
        m.RLock()
3✔
559
        defer m.RUnlock()
3✔
560

3✔
561
        baseCopy := make(ScidAliasMap)
3✔
562

3✔
563
        for k, v := range m.baseToSet {
6✔
564
                setCopy := make([]lnwire.ShortChannelID, len(v))
3✔
565
                copy(setCopy, v)
3✔
566
                baseCopy[k] = setCopy
3✔
567
        }
3✔
568

569
        return baseCopy
3✔
570
}
571

572
// getNextScid is a utility function that returns the next SCID for a given
573
// alias SCID. The BlockHeight ranges from [16000000, 16250000], the TxIndex
574
// ranges from [1, 16777215], and the TxPosition ranges from [1, 65535].
575
func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {
3✔
576
        var (
3✔
577
                next            lnwire.ShortChannelID
3✔
578
                incrementIdx    bool
3✔
579
                incrementHeight bool
3✔
580
        )
3✔
581

3✔
582
        // If the TxPosition is 65535, then it goes to 0 and we need to
3✔
583
        // increment the TxIndex.
3✔
584
        if last.TxPosition == 65535 {
3✔
UNCOV
585
                incrementIdx = true
×
UNCOV
586
        }
×
587

588
        // If the TxIndex is 16777215 and we need to increment it, then it goes
589
        // to 0 and we need to increment the BlockHeight.
590
        if last.TxIndex == 16777215 && incrementIdx {
3✔
UNCOV
591
                incrementIdx = false
×
UNCOV
592
                incrementHeight = true
×
UNCOV
593
        }
×
594

595
        switch {
3✔
596
        // If we increment the TxIndex, then TxPosition goes to 0.
UNCOV
597
        case incrementIdx:
×
UNCOV
598
                next.BlockHeight = last.BlockHeight
×
UNCOV
599
                next.TxIndex = last.TxIndex + 1
×
UNCOV
600
                next.TxPosition = 0
×
601

602
        // If we increment the BlockHeight, then the Tx fields go to 0.
UNCOV
603
        case incrementHeight:
×
UNCOV
604
                next.BlockHeight = last.BlockHeight + 1
×
UNCOV
605
                next.TxIndex = 0
×
UNCOV
606
                next.TxPosition = 0
×
607

608
        // Otherwise, we only need to increment the TxPosition.
609
        default:
3✔
610
                next.BlockHeight = last.BlockHeight
3✔
611
                next.TxIndex = last.TxIndex
3✔
612
                next.TxPosition = last.TxPosition + 1
3✔
613
        }
614

615
        return next
3✔
616
}
617

618
// IsAlias returns true if the passed SCID is an alias. The function determines
619
// this by looking at the BlockHeight. If the BlockHeight is greater than
620
// AliasStartBlockHeight and less than AliasEndBlockHeight, then it is an alias
621
// assigned by RequestAlias. These bounds only apply to aliases we generate.
622
// Our peers are free to use any range they choose.
623
func IsAlias(scid lnwire.ShortChannelID) bool {
3✔
624
        return scid.BlockHeight >= AliasStartBlockHeight &&
3✔
625
                scid.BlockHeight < AliasEndBlockHeight
3✔
626
}
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