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

lightningnetwork / lnd / 12986279612

27 Jan 2025 09:51AM UTC coverage: 57.652% (-1.1%) from 58.788%
12986279612

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
Pull Request #9447: sweep: start tracking input spending status in the fee bumper

83 of 87 new or added lines in 2 files covered. (95.4%)

19578 existing lines in 256 files now uncovered.

103448 of 179434 relevant lines covered (57.65%)

24884.58 hits per line

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

68.27
/aliasmgr/aliasmgr.go
1
package aliasmgr
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6
        "sync"
7

8
        "github.com/lightningnetwork/lnd/fn/v2"
9
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
10
        "github.com/lightningnetwork/lnd/kvdb"
11
        "github.com/lightningnetwork/lnd/lnwire"
12
        "golang.org/x/exp/maps"
13
)
14

15
// UpdateLinkAliases is a function type for a function that locates the active
16
// link that matches the given shortID and triggers an update based on the
17
// latest values of the alias manager.
18
type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error
19

20
// ScidAliasMap is a map from a base short channel ID to a set of alias short
21
// channel IDs.
22
type ScidAliasMap map[lnwire.ShortChannelID][]lnwire.ShortChannelID
23

24
var (
25
        // aliasBucket stores aliases as keys and their base SCIDs as values.
26
        // This is used to populate the maps that the Manager uses. The keys
27
        // are alias SCIDs and the values are their respective base SCIDs. This
28
        // is used instead of the other way around (base -> alias...) because
29
        // updating an alias would require fetching all the existing aliases,
30
        // adding another one, and then flushing the write to disk. This is
31
        // inefficient compared to N 1:1 mappings at the cost of marginally
32
        // more disk space.
33
        aliasBucket = []byte("alias-bucket")
34

35
        // confirmedBucket stores whether or not a given base SCID should no
36
        // longer have entries in the ToBase maps. The key is the SCID that is
37
        // confirmed with 6 confirmations and is public, and the value is
38
        // empty.
39
        confirmedBucket = []byte("base-bucket")
40

41
        // aliasAllocBucket is a root-level bucket that stores the last alias
42
        // that was allocated. It is used to allocate a new alias when
43
        // requested.
44
        aliasAllocBucket = []byte("alias-alloc-bucket")
45

46
        // lastAliasKey is a key in the aliasAllocBucket whose value is the
47
        // last allocated alias ShortChannelID. This will be updated upon calls
48
        // to RequestAlias.
49
        lastAliasKey = []byte("last-alias-key")
50

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

57
        // byteOrder denotes the byte order of database (de)-serialization
58
        // operations.
59
        byteOrder = binary.BigEndian
60

61
        // AliasStartBlockHeight is the starting block height of the alias
62
        // range.
63
        AliasStartBlockHeight uint32 = 16_000_000
64

65
        // AliasEndBlockHeight is the ending block height of the alias range.
66
        AliasEndBlockHeight uint32 = 16_250_000
67

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

77
        // errNoBase is returned when a base SCID isn't found.
78
        errNoBase = fmt.Errorf("no base found")
79

80
        // errNoPeerAlias is returned when the peer's alias for a given
81
        // channel is not found.
82
        errNoPeerAlias = fmt.Errorf("no peer alias found")
83

84
        // ErrAliasNotFound is returned when the alias is not found and can't
85
        // be mapped to a base SCID.
86
        ErrAliasNotFound = fmt.Errorf("alias not found")
87
)
88

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

96
        // linkAliasUpdater is a function used by the alias manager to
97
        // facilitate live update of aliases in other subsystems.
98
        linkAliasUpdater UpdateLinkAliases
99

100
        // baseToSet is a mapping from the "base" SCID to the set of aliases
101
        // for this channel. This mapping includes all channels that
102
        // negotiated the option-scid-alias feature bit.
103
        baseToSet ScidAliasMap
104

105
        // aliasToBase is a mapping that maps all aliases for a given channel
106
        // to its base SCID. This is only used for channels that have
107
        // negotiated option-scid-alias feature bit.
108
        aliasToBase map[lnwire.ShortChannelID]lnwire.ShortChannelID
109

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

117
        sync.RWMutex
118
}
119

120
// NewManager initializes an alias Manager from the passed database backend.
121
func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager,
122
        error) {
3✔
123

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

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

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

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

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

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

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

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

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

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

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

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

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

×
UNCOV
209
                        return nil
×
UNCOV
210
                })
×
211

212
                return err
3✔
213
        }, func() {
3✔
214
                baseConfMap = make(map[lnwire.ShortChannelID]struct{})
3✔
215
                aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
3✔
216
                peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
3✔
217
        })
3✔
218
        if err != nil {
3✔
219
                return err
×
220
        }
×
221

222
        // Populate the baseToSet map regardless if the baseSCID is marked as
223
        // public with 6 confirmations.
224
        for aliasSCID, baseSCID := range aliasMap {
3✔
UNCOV
225
                m.baseToSet[baseSCID] = append(m.baseToSet[baseSCID], aliasSCID)
×
UNCOV
226

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

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

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

3✔
238
        return nil
3✔
239
}
240

241
// AddLocalAlias adds a database mapping from the passed alias to the passed
242
// base SCID. The gossip boolean marks whether or not to create a mapping
243
// that the gossiper will use. It is set to false for the upgrade path where
244
// the feature-bit is toggled on and there are existing channels. The linkUpdate
245
// flag is used to signal whether this function should also trigger an update
246
// on the htlcswitch scid alias maps.
247
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
248
        gossip, linkUpdate bool) error {
3✔
249

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

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

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

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

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

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

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

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

3✔
304
        // Only store the gossiper map if gossip is true.
3✔
305
        if gossip {
3✔
UNCOV
306
                m.aliasToBase[alias] = baseScid
×
UNCOV
307
        }
×
308

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

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

UNCOV
321
        return nil
×
322
}
323

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

4✔
329
        m.RLock()
4✔
330
        defer m.RUnlock()
4✔
331

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

340
        return nil
1✔
341
}
342

343
// FindBaseSCID finds the base SCID for a given alias. This is used in the
344
// gossiper to find the correct SCID to lookup in the graph database.
345
func (m *Manager) FindBaseSCID(
UNCOV
346
        alias lnwire.ShortChannelID) (lnwire.ShortChannelID, error) {
×
UNCOV
347

×
UNCOV
348
        m.RLock()
×
UNCOV
349
        defer m.RUnlock()
×
UNCOV
350

×
UNCOV
351
        base, ok := m.aliasToBase[alias]
×
UNCOV
352
        if ok {
×
UNCOV
353
                return base, nil
×
UNCOV
354
        }
×
355

UNCOV
356
        return lnwire.ShortChannelID{}, errNoBase
×
357
}
358

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

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

UNCOV
372
                var baseBytes [8]byte
×
UNCOV
373
                byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
×
UNCOV
374
                return baseConfBucket.Put(baseBytes[:], []byte{})
×
UNCOV
375
        }, func() {})
×
UNCOV
376
        if err != nil {
×
377
                return err
×
378
        }
×
379

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

UNCOV
388
        return nil
×
389
}
390

391
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
392
func (m *Manager) DeleteLocalAlias(alias,
393
        baseScid lnwire.ShortChannelID) error {
3✔
394

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

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

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

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

421
                return aliasToBaseBucket.Delete(aliasBytes[:])
2✔
422
        }, func() {})
3✔
423
        if err != nil {
4✔
424
                return err
1✔
425
        }
1✔
426

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

434
        // We'll filter the alias set and remove the alias from it.
435
        aliasSet = fn.Filter(aliasSet, func(a lnwire.ShortChannelID) bool {
5✔
436
                return a.ToUint64() != alias.ToUint64()
3✔
437
        })
3✔
438

439
        // If the alias set is empty, we'll delete the base SCID from the
440
        // baseToSet map.
441
        if len(aliasSet) == 0 {
3✔
442
                delete(m.baseToSet, baseScid)
1✔
443
        } else {
2✔
444
                m.baseToSet[baseScid] = aliasSet
1✔
445
        }
1✔
446

447
        // Finally, we'll delete the aliasToBase mapping from the Manager's
448
        // cache (but this is only set if we gossip the alias).
449
        delete(m.aliasToBase, alias)
2✔
450

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

2✔
456
        return m.linkAliasUpdater(baseScid)
2✔
457
}
458

459
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
460
// channel_ready message.
461
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
462
        alias lnwire.ShortChannelID) error {
1✔
463

1✔
464
        m.Lock()
1✔
465
        defer m.Unlock()
1✔
466

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

473
                var scratch [8]byte
1✔
474
                byteOrder.PutUint64(scratch[:], alias.ToUint64())
1✔
475
                return bucket.Put(chanID[:], scratch[:])
1✔
476
        }, func() {})
1✔
477
        if err != nil {
1✔
478
                return err
×
479
        }
×
480

481
        // Now that the database state has been updated, we can update it in
482
        // our cache.
483
        m.peerAlias[chanID] = alias
1✔
484

1✔
485
        return nil
1✔
486
}
487

488
// GetPeerAlias retrieves a peer's alias SCID by the channel's ChanID.
489
func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
490
        error) {
1✔
491

1✔
492
        m.RLock()
1✔
493
        defer m.RUnlock()
1✔
494

1✔
495
        alias, ok := m.peerAlias[chanID]
1✔
496
        if !ok || alias == hop.Source {
1✔
UNCOV
497
                return lnwire.ShortChannelID{}, errNoPeerAlias
×
UNCOV
498
        }
×
499

500
        return alias, nil
1✔
501
}
502

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

4✔
511
        m.RLock()
4✔
512
        defer m.RUnlock()
4✔
513

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

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

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

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

554
                                nextAlias = getNextScid(nextAlias)
×
555

×
556
                                // Abort if we've reached the end of the range.
×
557
                                if nextAlias.BlockHeight >=
×
558
                                        AliasEndBlockHeight {
×
559

×
560
                                        return fmt.Errorf("range for custom " +
×
561
                                                "aliases exhausted")
×
562
                                }
×
563
                        }
564

565
                        var scratch [8]byte
2✔
566
                        byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
2✔
567
                        return bucket.Put(lastAliasKey, scratch[:])
2✔
568
                }
569

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

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

590
                        nextAlias = getNextScid(nextAlias)
1✔
591

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

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

609
        return nextAlias, nil
4✔
610
}
611

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

×
UNCOV
618
        baseCopy := make(ScidAliasMap)
×
UNCOV
619

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

UNCOV
626
        return baseCopy
×
627
}
628

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

10✔
639
        // If the TxPosition is 65535, then it goes to 0 and we need to
10✔
640
        // increment the TxIndex.
10✔
641
        if last.TxPosition == 65535 {
12✔
642
                incrementIdx = true
2✔
643
        }
2✔
644

645
        // If the TxIndex is 16777215 and we need to increment it, then it goes
646
        // to 0 and we need to increment the BlockHeight.
647
        if last.TxIndex == 16777215 && incrementIdx {
11✔
648
                incrementIdx = false
1✔
649
                incrementHeight = true
1✔
650
        }
1✔
651

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

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

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

672
        return next
10✔
673
}
674

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