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

lightningnetwork / lnd / 13536249039

26 Feb 2025 03:42AM UTC coverage: 57.462% (-1.4%) from 58.835%
13536249039

Pull #8453

github

Roasbeef
peer: update chooseDeliveryScript to gen script if needed

In this commit, we update `chooseDeliveryScript` to generate a new
script if needed. This allows us to fold in a few other lines that
always followed this function into this expanded function.

The tests have been updated accordingly.
Pull Request #8453: [4/4] - multi: integrate new rbf coop close FSM into the existing peer flow

275 of 1318 new or added lines in 22 files covered. (20.86%)

19521 existing lines in 257 files now uncovered.

103858 of 180741 relevant lines covered (57.46%)

24750.23 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