@@ -5,11 +5,22 @@ import (
55 "fmt"
66 "sync"
77
8+ "github.com/lightningnetwork/lnd/fn"
89 "github.com/lightningnetwork/lnd/htlcswitch/hop"
910 "github.com/lightningnetwork/lnd/kvdb"
1011 "github.com/lightningnetwork/lnd/lnwire"
12+ "golang.org/x/exp/maps"
1113)
1214
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+
1324var (
1425 // aliasBucket stores aliases as keys and their base SCIDs as values.
1526 // This is used to populate the maps that the Manager uses. The keys
@@ -47,17 +58,18 @@ var (
4758 // operations.
4859 byteOrder = binary .BigEndian
4960
50- // startBlockHeight is the starting block height of the alias range.
51- startingBlockHeight = 16_000_000
61+ // AliasStartBlockHeight is the starting block height of the alias
62+ // range.
63+ AliasStartBlockHeight uint32 = 16_000_000
5264
53- // endBlockHeight is the ending block height of the alias range.
54- endBlockHeight = 16_250_000
65+ // AliasEndBlockHeight is the ending block height of the alias range.
66+ AliasEndBlockHeight uint32 = 16_250_000
5567
5668 // StartingAlias is the first alias ShortChannelID that will get
5769 // assigned by RequestAlias. The starting BlockHeight is chosen so that
5870 // legitimate SCIDs in integration tests aren't mistaken for an alias.
5971 StartingAlias = lnwire.ShortChannelID {
60- BlockHeight : uint32 ( startingBlockHeight ) ,
72+ BlockHeight : AliasStartBlockHeight ,
6173 TxIndex : 0 ,
6274 TxPosition : 0 ,
6375 }
6880 // errNoPeerAlias is returned when the peer's alias for a given
6981 // channel is not found.
7082 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" )
7187)
7288
7389// Manager is a struct that handles aliases for LND. It has an underlying
@@ -77,10 +93,14 @@ var (
7793type Manager struct {
7894 backend kvdb.Backend
7995
96+ // linkAliasUpdater is a function used by the alias manager to
97+ // facilitate live update of aliases in other subsystems.
98+ linkAliasUpdater UpdateLinkAliases
99+
80100 // baseToSet is a mapping from the "base" SCID to the set of aliases
81101 // for this channel. This mapping includes all channels that
82102 // negotiated the option-scid-alias feature bit.
83- baseToSet map [lnwire. ShortChannelID ][]lnwire. ShortChannelID
103+ baseToSet ScidAliasMap
84104
85105 // aliasToBase is a mapping that maps all aliases for a given channel
86106 // to its base SCID. This is only used for channels that have
@@ -98,9 +118,15 @@ type Manager struct {
98118}
99119
100120// NewManager initializes an alias Manager from the passed database backend.
101- func NewManager (db kvdb.Backend ) (* Manager , error ) {
102- m := & Manager {backend : db }
103- m .baseToSet = make (map [lnwire.ShortChannelID ][]lnwire.ShortChannelID )
121+ func NewManager (db kvdb.Backend , linkAliasUpdater UpdateLinkAliases ) (* Manager ,
122+ error ) {
123+
124+ m := & Manager {
125+ backend : db ,
126+ baseToSet : make (ScidAliasMap ),
127+ linkAliasUpdater : linkAliasUpdater ,
128+ }
129+
104130 m .aliasToBase = make (map [lnwire.ShortChannelID ]lnwire.ShortChannelID )
105131 m .peerAlias = make (map [lnwire.ChannelID ]lnwire.ShortChannelID )
106132
@@ -215,12 +241,22 @@ func (m *Manager) populateMaps() error {
215241// AddLocalAlias adds a database mapping from the passed alias to the passed
216242// base SCID. The gossip boolean marks whether or not to create a mapping
217243// that the gossiper will use. It is set to false for the upgrade path where
218- // the feature-bit is toggled on and there are existing channels.
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.
219247func (m * Manager ) AddLocalAlias (alias , baseScid lnwire.ShortChannelID ,
220- gossip bool ) error {
248+ gossip , linkUpdate bool ) error {
221249
250+ // We need to lock the manager for the whole duration of this method,
251+ // except for the very last part where we call the link updater. In
252+ // order for us to safely use a defer _and_ still be able to manually
253+ // unlock, we use a sync.Once.
222254 m .Lock ()
223- defer m .Unlock ()
255+ unlockOnce := sync.Once {}
256+ unlock := func () {
257+ unlockOnce .Do (m .Unlock )
258+ }
259+ defer unlock ()
224260
225261 err := kvdb .Update (m .backend , func (tx kvdb.RwTx ) error {
226262 // If the caller does not want to allow the alias to be used
@@ -270,6 +306,18 @@ func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
270306 m .aliasToBase [alias ] = baseScid
271307 }
272308
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 ()
313+
314+ // Finally, we trigger a htlcswitch update if the flag is set, in order
315+ // for any future htlc that references the added alias to be properly
316+ // routed.
317+ if linkUpdate {
318+ return m .linkAliasUpdater (baseScid )
319+ }
320+
273321 return nil
274322}
275323
@@ -340,6 +388,74 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
340388 return nil
341389}
342390
391+ // DeleteLocalAlias removes a mapping from the database and the Manager's maps.
392+ func (m * Manager ) DeleteLocalAlias (alias ,
393+ baseScid lnwire.ShortChannelID ) error {
394+
395+ // We need to lock the manager for the whole duration of this method,
396+ // except for the very last part where we call the link updater. In
397+ // order for us to safely use a defer _and_ still be able to manually
398+ // unlock, we use a sync.Once.
399+ m .Lock ()
400+ unlockOnce := sync.Once {}
401+ unlock := func () {
402+ unlockOnce .Do (m .Unlock )
403+ }
404+ defer unlock ()
405+
406+ err := kvdb .Update (m .backend , func (tx kvdb.RwTx ) error {
407+ aliasToBaseBucket , err := tx .CreateTopLevelBucket (aliasBucket )
408+ if err != nil {
409+ return err
410+ }
411+
412+ var aliasBytes [8 ]byte
413+ byteOrder .PutUint64 (aliasBytes [:], alias .ToUint64 ())
414+
415+ // If the user attempts to delete an alias that doesn't exist,
416+ // we'll want to inform them about it and not just do nothing.
417+ if aliasToBaseBucket .Get (aliasBytes [:]) == nil {
418+ return ErrAliasNotFound
419+ }
420+
421+ return aliasToBaseBucket .Delete (aliasBytes [:])
422+ }, func () {})
423+ if err != nil {
424+ return err
425+ }
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 ]
430+ if ! ok {
431+ return ErrAliasNotFound
432+ }
433+
434+ // We'll filter the alias set and remove the alias from it.
435+ aliasSet = fn .Filter (func (a lnwire.ShortChannelID ) bool {
436+ return a .ToUint64 () != alias .ToUint64 ()
437+ }, aliasSet )
438+
439+ // If the alias set is empty, we'll delete the base SCID from the
440+ // baseToSet map.
441+ if len (aliasSet ) == 0 {
442+ delete (m .baseToSet , baseScid )
443+ } else {
444+ m .baseToSet [baseScid ] = aliasSet
445+ }
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 )
450+
451+ // We definitely need to unlock the Manager before calling the link
452+ // updater. If we don't, we'll deadlock. We use a sync.Once to ensure
453+ // that we only unlock once.
454+ unlock ()
455+
456+ return m .linkAliasUpdater (baseScid )
457+ }
458+
343459// PutPeerAlias stores the peer's alias SCID once we learn of it in the
344460// channel_ready message.
345461func (m * Manager ) PutPeerAlias (chanID lnwire.ChannelID ,
@@ -392,6 +508,19 @@ func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
392508func (m * Manager ) RequestAlias () (lnwire.ShortChannelID , error ) {
393509 var nextAlias lnwire.ShortChannelID
394510
511+ m .RLock ()
512+ defer m .RUnlock ()
513+
514+ // haveAlias returns true if the passed alias is already assigned to a
515+ // channel in the baseToSet map.
516+ haveAlias := func (maybeNextAlias lnwire.ShortChannelID ) bool {
517+ return fn .Any (func (aliasList []lnwire.ShortChannelID ) bool {
518+ return fn .Any (func (alias lnwire.ShortChannelID ) bool {
519+ return alias == maybeNextAlias
520+ }, aliasList )
521+ }, maps .Values (m .baseToSet ))
522+ }
523+
395524 err := kvdb .Update (m .backend , func (tx kvdb.RwTx ) error {
396525 bucket , err := tx .CreateTopLevelBucket (aliasAllocBucket )
397526 if err != nil {
@@ -404,6 +533,29 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
404533 // StartingAlias to it.
405534 nextAlias = StartingAlias
406535
536+ // If the very first alias is already assigned, we'll
537+ // keep incrementing until we find an unassigned alias.
538+ // This is to avoid collision with custom added SCID
539+ // aliases that fall into the same range as the ones we
540+ // generate here monotonically. Those custom SCIDs are
541+ // stored in a different bucket, but we can just check
542+ // the in-memory map for simplicity.
543+ for {
544+ if ! haveAlias (nextAlias ) {
545+ break
546+ }
547+
548+ nextAlias = getNextScid (nextAlias )
549+
550+ // Abort if we've reached the end of the range.
551+ if nextAlias .BlockHeight >=
552+ AliasEndBlockHeight {
553+
554+ return fmt .Errorf ("range for custom " +
555+ "aliases exhausted" )
556+ }
557+ }
558+
407559 var scratch [8 ]byte
408560 byteOrder .PutUint64 (scratch [:], nextAlias .ToUint64 ())
409561 return bucket .Put (lastAliasKey , scratch [:])
@@ -418,6 +570,26 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
418570 )
419571 nextAlias = getNextScid (lastScid )
420572
573+ // If the next alias is already assigned, we'll keep
574+ // incrementing until we find an unassigned alias. This is to
575+ // avoid collision with custom added SCID aliases that fall into
576+ // the same range as the ones we generate here monotonically.
577+ // Those custom SCIDs are stored in a different bucket, but we
578+ // can just check the in-memory map for simplicity.
579+ for {
580+ if ! haveAlias (nextAlias ) {
581+ break
582+ }
583+
584+ nextAlias = getNextScid (nextAlias )
585+
586+ // Abort if we've reached the end of the range.
587+ if nextAlias .BlockHeight >= AliasEndBlockHeight {
588+ return fmt .Errorf ("range for custom " +
589+ "aliases exhausted" )
590+ }
591+ }
592+
421593 var scratch [8 ]byte
422594 byteOrder .PutUint64 (scratch [:], nextAlias .ToUint64 ())
423595 return bucket .Put (lastAliasKey , scratch [:])
@@ -433,11 +605,11 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
433605
434606// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
435607// layer.
436- func (m * Manager ) ListAliases () map [lnwire. ShortChannelID ][]lnwire. ShortChannelID {
608+ func (m * Manager ) ListAliases () ScidAliasMap {
437609 m .RLock ()
438610 defer m .RUnlock ()
439611
440- baseCopy := make (map [lnwire. ShortChannelID ][]lnwire. ShortChannelID )
612+ baseCopy := make (ScidAliasMap )
441613
442614 for k , v := range m .baseToSet {
443615 setCopy := make ([]lnwire.ShortChannelID , len (v ))
@@ -496,10 +668,10 @@ func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {
496668
497669// IsAlias returns true if the passed SCID is an alias. The function determines
498670// this by looking at the BlockHeight. If the BlockHeight is greater than
499- // startingBlockHeight and less than endBlockHeight , then it is an alias
671+ // AliasStartBlockHeight and less than AliasEndBlockHeight , then it is an alias
500672// assigned by RequestAlias. These bounds only apply to aliases we generate.
501673// Our peers are free to use any range they choose.
502674func IsAlias (scid lnwire.ShortChannelID ) bool {
503- return scid .BlockHeight >= uint32 ( startingBlockHeight ) &&
504- scid .BlockHeight < uint32 ( endBlockHeight )
675+ return scid .BlockHeight >= AliasStartBlockHeight &&
676+ scid .BlockHeight < AliasEndBlockHeight
505677}
0 commit comments