Skip to content

Commit b009db3

Browse files
authored
Merge pull request #8509 from GeorgeTsagk/scid-alias-rpcs
Add more RPCs for scid aliases
2 parents a908c57 + d9a59c6 commit b009db3

25 files changed

+1602
-248
lines changed

aliasmgr/aliasmgr.go

Lines changed: 104 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import (
1010
"github.com/lightningnetwork/lnd/lnwire"
1111
)
1212

13+
// UpdateLinkAliases locates the active link that matches the given
14+
// shortID and triggers an update based on the latest values of the
15+
// alias manager.
16+
type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error
17+
1318
var (
1419
// aliasBucket stores aliases as keys and their base SCIDs as values.
1520
// This is used to populate the maps that the Manager uses. The keys
@@ -77,6 +82,10 @@ var (
7782
type Manager struct {
7883
backend kvdb.Backend
7984

85+
// LinkUpdater is a function used by aliasmgr to facilitate live update
86+
// of aliases in other subsystems.
87+
LinkUpdater UpdateLinkAliases
88+
8089
// baseToSet is a mapping from the "base" SCID to the set of aliases
8190
// for this channel. This mapping includes all channels that
8291
// negotiated the option-scid-alias feature bit.
@@ -98,11 +107,14 @@ type Manager struct {
98107
}
99108

100109
// NewManager initializes an alias Manager from the passed database backend.
101-
func NewManager(db kvdb.Backend) (*Manager, error) {
110+
func NewManager(db kvdb.Backend,
111+
linkUpdater UpdateLinkAliases) (*Manager, error) {
112+
102113
m := &Manager{backend: db}
103114
m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
104115
m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
105116
m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
117+
m.LinkUpdater = linkUpdater
106118

107119
err := m.populateMaps()
108120
return m, err
@@ -215,12 +227,22 @@ func (m *Manager) populateMaps() error {
215227
// AddLocalAlias adds a database mapping from the passed alias to the passed
216228
// base SCID. The gossip boolean marks whether or not to create a mapping
217229
// 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.
230+
// the feature-bit is toggled on and there are existing channels. The linkUpdate
231+
// flag is used to signal whether this function should also trigger an update
232+
// on the htlcswitch scid alias maps.
219233
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
220-
gossip bool) error {
234+
gossip, linkUpdate bool) error {
221235

236+
// We need to lock the manager for the whole duration of this method,
237+
// except for the very last part where we call the link updater. In
238+
// order for us to safely use a defer _and_ still be able to manually
239+
// unlock, we use a sync.Once.
222240
m.Lock()
223-
defer m.Unlock()
241+
unlockOnce := sync.Once{}
242+
unlock := func() {
243+
unlockOnce.Do(m.Unlock)
244+
}
245+
defer unlock()
224246

225247
err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
226248
// If the caller does not want to allow the alias to be used
@@ -270,6 +292,18 @@ func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
270292
m.aliasToBase[alias] = baseScid
271293
}
272294

295+
// We definitely need to unlock the Manager before calling the link
296+
// updater. If we don't, we'll deadlock. We use a sync.Once to ensure
297+
// that we only unlock once.
298+
unlock()
299+
300+
// Finally, we trigger a htlcswitch update if the flag is set, in order
301+
// for any future htlc that references the added alias to be properly
302+
// routed.
303+
if linkUpdate {
304+
return m.LinkUpdater(baseScid)
305+
}
306+
273307
return nil
274308
}
275309

@@ -340,6 +374,72 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
340374
return nil
341375
}
342376

377+
// DeleteLocalAlias removes a mapping from the database and the Manager's maps.
378+
func (m *Manager) DeleteLocalAlias(alias,
379+
baseScid lnwire.ShortChannelID) error {
380+
381+
// We need to lock the manager for the whole duration of this method,
382+
// except for the very last part where we call the link updater. In
383+
// order for us to safely use a defer _and_ still be able to manually
384+
// unlock, we use a sync.Once.
385+
m.Lock()
386+
unlockOnce := sync.Once{}
387+
unlock := func() {
388+
unlockOnce.Do(m.Unlock)
389+
}
390+
defer unlock()
391+
392+
err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
393+
aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
394+
if err != nil {
395+
return err
396+
}
397+
398+
var aliasBytes [8]byte
399+
byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
400+
401+
return aliasToBaseBucket.Delete(aliasBytes[:])
402+
}, func() {})
403+
if err != nil {
404+
return err
405+
}
406+
407+
// Now that the database state has been updated, we'll delete the
408+
// mapping from the Manager's maps.
409+
aliasSet, ok := m.baseToSet[baseScid]
410+
if !ok {
411+
return nil
412+
}
413+
414+
// We'll iterate through the alias set and remove the alias from the
415+
// set.
416+
for i, a := range aliasSet {
417+
if a.ToUint64() == alias.ToUint64() {
418+
aliasSet = append(aliasSet[:i], aliasSet[i+1:]...)
419+
break
420+
}
421+
}
422+
423+
// If the alias set is empty, we'll delete the base SCID from the
424+
// baseToSet map.
425+
if len(aliasSet) == 0 {
426+
delete(m.baseToSet, baseScid)
427+
} else {
428+
m.baseToSet[baseScid] = aliasSet
429+
}
430+
431+
// Finally, we'll delete the aliasToBase mapping from the Manager's
432+
// cache.
433+
delete(m.aliasToBase, alias)
434+
435+
// We definitely need to unlock the Manager before calling the link
436+
// updater. If we don't, we'll deadlock. We use a sync.Once to ensure
437+
// that we only unlock once.
438+
unlock()
439+
440+
return m.LinkUpdater(baseScid)
441+
}
442+
343443
// PutPeerAlias stores the peer's alias SCID once we learn of it in the
344444
// channel_ready message.
345445
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,

aliasmgr/aliasmgr_test.go

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ func TestAliasStorePeerAlias(t *testing.T) {
2323
require.NoError(t, err)
2424
defer db.Close()
2525

26-
aliasStore, err := NewManager(db)
26+
linkUpdater := func(shortID lnwire.ShortChannelID) error {
27+
return nil
28+
}
29+
30+
aliasStore, err := NewManager(db, linkUpdater)
2731
require.NoError(t, err)
2832

2933
var chanID1 [32]byte
@@ -52,7 +56,14 @@ func TestAliasStoreRequest(t *testing.T) {
5256
require.NoError(t, err)
5357
defer db.Close()
5458

55-
aliasStore, err := NewManager(db)
59+
updateChan := make(chan struct{}, 1)
60+
61+
linkUpdater := func(shortID lnwire.ShortChannelID) error {
62+
updateChan <- struct{}{}
63+
return nil
64+
}
65+
66+
aliasStore, err := NewManager(db, linkUpdater)
5667
require.NoError(t, err)
5768

5869
// We'll assert that the very first alias we receive is StartingAlias.
@@ -68,6 +79,88 @@ func TestAliasStoreRequest(t *testing.T) {
6879
require.Equal(t, nextAlias, alias2)
6980
}
7081

82+
// TestAliasLifecycle tests that the aliases can be created and deleted.
83+
func TestAliasLifecycle(t *testing.T) {
84+
t.Parallel()
85+
86+
// Create the backend database and use this to create the aliasStore.
87+
dbPath := filepath.Join(t.TempDir(), "testdb")
88+
db, err := kvdb.Create(
89+
kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
90+
)
91+
require.NoError(t, err)
92+
defer db.Close()
93+
94+
updateChan := make(chan struct{}, 1)
95+
96+
linkUpdater := func(shortID lnwire.ShortChannelID) error {
97+
updateChan <- struct{}{}
98+
return nil
99+
}
100+
101+
aliasStore, err := NewManager(db, linkUpdater)
102+
require.NoError(t, err)
103+
104+
const (
105+
base = uint64(123123123)
106+
alias = uint64(456456456)
107+
)
108+
109+
// Parse the aliases and base to short channel ID format.
110+
baseScid := lnwire.NewShortChanIDFromInt(base)
111+
aliasScid := lnwire.NewShortChanIDFromInt(alias)
112+
aliasScid2 := lnwire.NewShortChanIDFromInt(alias + 1)
113+
114+
// Add the first alias.
115+
err = aliasStore.AddLocalAlias(aliasScid, baseScid, false, true)
116+
require.NoError(t, err)
117+
118+
// The link updater should be called.
119+
<-updateChan
120+
121+
// Query the aliases and verify the results.
122+
aliasList := aliasStore.GetAliases(baseScid)
123+
require.Len(t, aliasList, 1)
124+
require.Contains(t, aliasList, aliasScid)
125+
126+
// Add the second alias.
127+
err = aliasStore.AddLocalAlias(aliasScid2, baseScid, false, true)
128+
require.NoError(t, err)
129+
130+
// The link updater should be called.
131+
<-updateChan
132+
133+
// Query the aliases and verify the results.
134+
aliasList = aliasStore.GetAliases(baseScid)
135+
require.Len(t, aliasList, 2)
136+
require.Contains(t, aliasList, aliasScid)
137+
require.Contains(t, aliasList, aliasScid2)
138+
139+
// Delete the first alias.
140+
err = aliasStore.DeleteLocalAlias(aliasScid, baseScid)
141+
require.NoError(t, err)
142+
143+
// The link updater should be called.
144+
<-updateChan
145+
146+
// Query the aliases and verify that first one doesn't exist anymore.
147+
aliasList = aliasStore.GetAliases(baseScid)
148+
require.Len(t, aliasList, 1)
149+
require.Contains(t, aliasList, aliasScid2)
150+
require.NotContains(t, aliasList, aliasScid)
151+
152+
// Delete the second alias.
153+
err = aliasStore.DeleteLocalAlias(aliasScid2, baseScid)
154+
require.NoError(t, err)
155+
156+
// The link updater should be called.
157+
<-updateChan
158+
159+
// Query the aliases and verify that none exists.
160+
aliasList = aliasStore.GetAliases(baseScid)
161+
require.Len(t, aliasList, 0)
162+
}
163+
71164
// TestGetNextScid tests that given a current lnwire.ShortChannelID,
72165
// getNextScid returns the expected alias to use next.
73166
func TestGetNextScid(t *testing.T) {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
* Some new experimental [RPCs for managing SCID
2+
aliases](https://github.com/lightningnetwork/lnd/pull/8509) were added under
3+
the routerrpc package. These methods allow manually adding and deleting scid
4+
aliases locally to your node.
5+
> NOTE: these new RPC methods are marked as experimental
6+
(`XAddLocalChanAliases` & `XDeleteLocalChanAliases`) and upon calling
7+
them the aliases will not be communicated with the channel peer.

funding/interfaces.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ type aliasHandler interface {
3636
GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID, error)
3737

3838
// AddLocalAlias persists an alias to an underlying alias store.
39-
AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool) error
39+
AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool,
40+
bool) error
4041

4142
// GetAliases returns the set of aliases given the main SCID of a
4243
// channel. This SCID will be an alias for zero-conf channels and will

funding/manager.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,7 @@ func (f *Manager) advancePendingChannelState(
12491249
// Persist the alias to the alias database.
12501250
baseScid := channel.ShortChannelID
12511251
err := f.cfg.AliasManager.AddLocalAlias(
1252-
baseScid, baseScid, true,
1252+
baseScid, baseScid, true, false,
12531253
)
12541254
if err != nil {
12551255
return fmt.Errorf("error adding local alias to "+
@@ -3108,7 +3108,7 @@ func (f *Manager) handleFundingConfirmation(
31083108
}
31093109

31103110
err = f.cfg.AliasManager.AddLocalAlias(
3111-
aliasScid, confChannel.shortChanID, true,
3111+
aliasScid, confChannel.shortChanID, true, false,
31123112
)
31133113
if err != nil {
31143114
return fmt.Errorf("unable to request alias: %w", err)
@@ -3274,7 +3274,7 @@ func (f *Manager) sendChannelReady(completeChan *channeldb.OpenChannel,
32743274

32753275
err = f.cfg.AliasManager.AddLocalAlias(
32763276
alias, completeChan.ShortChannelID,
3277-
false,
3277+
false, false,
32783278
)
32793279
if err != nil {
32803280
return err
@@ -3853,7 +3853,7 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen
38533853
}
38543854

38553855
err = f.cfg.AliasManager.AddLocalAlias(
3856-
alias, channel.ShortChannelID, false,
3856+
alias, channel.ShortChannelID, false, false,
38573857
)
38583858
if err != nil {
38593859
log.Errorf("unable to add local alias: %v",

funding/manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func (m *mockAliasMgr) GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID,
161161
}
162162

163163
func (m *mockAliasMgr) AddLocalAlias(lnwire.ShortChannelID,
164-
lnwire.ShortChannelID, bool) error {
164+
lnwire.ShortChannelID, bool, bool) error {
165165

166166
return nil
167167
}

0 commit comments

Comments
 (0)