Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 104 additions & 4 deletions aliasmgr/aliasmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (
"github.com/lightningnetwork/lnd/lnwire"
)

// UpdateLinkAliases locates the active link that matches the given
// shortID and triggers an update based on the latest values of the
// alias manager.
type UpdateLinkAliases func(shortID lnwire.ShortChannelID) error
Comment thread
guggero marked this conversation as resolved.
Outdated

var (
// aliasBucket stores aliases as keys and their base SCIDs as values.
// This is used to populate the maps that the Manager uses. The keys
Expand Down Expand Up @@ -77,6 +82,10 @@ var (
type Manager struct {
backend kvdb.Backend

// LinkUpdater is a function used by aliasmgr to facilitate live update
// of aliases in other subsystems.
LinkUpdater UpdateLinkAliases

// baseToSet is a mapping from the "base" SCID to the set of aliases
// for this channel. This mapping includes all channels that
// negotiated the option-scid-alias feature bit.
Expand All @@ -98,11 +107,14 @@ type Manager struct {
}

// NewManager initializes an alias Manager from the passed database backend.
func NewManager(db kvdb.Backend) (*Manager, error) {
func NewManager(db kvdb.Backend,
linkUpdater UpdateLinkAliases) (*Manager, error) {

m := &Manager{backend: db}
m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
m.LinkUpdater = linkUpdater

err := m.populateMaps()
return m, err
Expand Down Expand Up @@ -215,12 +227,22 @@ func (m *Manager) populateMaps() error {
// AddLocalAlias adds a database mapping from the passed alias to the passed
// base SCID. The gossip boolean marks whether or not to create a mapping
// that the gossiper will use. It is set to false for the upgrade path where
// the feature-bit is toggled on and there are existing channels.
// the feature-bit is toggled on and there are existing channels. The linkUpdate
// flag is used to signal whether this function should also trigger an update
// on the htlcswitch scid alias maps.
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
gossip bool) error {
gossip, linkUpdate bool) error {

// We need to lock the manager for the whole duration of this method,
// except for the very last part where we call the link updater. In
// order for us to safely use a defer _and_ still be able to manually
// unlock, we use a sync.Once.
m.Lock()
defer m.Unlock()
unlockOnce := sync.Once{}
unlock := func() {
unlockOnce.Do(m.Unlock)
}
defer unlock()

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

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

// Finally, we trigger a htlcswitch update if the flag is set, in order
// for any future htlc that references the added alias to be properly
// routed.
if linkUpdate {
return m.LinkUpdater(baseScid)
}

return nil
}

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

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

// We need to lock the manager for the whole duration of this method,
// except for the very last part where we call the link updater. In
// order for us to safely use a defer _and_ still be able to manually
// unlock, we use a sync.Once.
m.Lock()
unlockOnce := sync.Once{}
unlock := func() {
unlockOnce.Do(m.Unlock)
}
defer unlock()

err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
if err != nil {
return err
}

var aliasBytes [8]byte
byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())

return aliasToBaseBucket.Delete(aliasBytes[:])
}, func() {})
if err != nil {
return err
}

// Now that the database state has been updated, we'll delete the
// mapping from the Manager's maps.
aliasSet, ok := m.baseToSet[baseScid]
if !ok {
return nil
}

// We'll iterate through the alias set and remove the alias from the
// set.
for i, a := range aliasSet {
if a.ToUint64() == alias.ToUint64() {
aliasSet = append(aliasSet[:i], aliasSet[i+1:]...)
break
}
}

// If the alias set is empty, we'll delete the base SCID from the
// baseToSet map.
if len(aliasSet) == 0 {
delete(m.baseToSet, baseScid)
} else {
m.baseToSet[baseScid] = aliasSet
}

// Finally, we'll delete the aliasToBase mapping from the Manager's
// cache.
delete(m.aliasToBase, alias)

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

return m.LinkUpdater(baseScid)
Comment thread
GeorgeTsagk marked this conversation as resolved.
Outdated
}

// PutPeerAlias stores the peer's alias SCID once we learn of it in the
// channel_ready message.
func (m *Manager) PutPeerAlias(chanID lnwire.ChannelID,
Expand Down
97 changes: 95 additions & 2 deletions aliasmgr/aliasmgr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ func TestAliasStorePeerAlias(t *testing.T) {
require.NoError(t, err)
defer db.Close()

aliasStore, err := NewManager(db)
linkUpdater := func(shortID lnwire.ShortChannelID) error {
return nil
}

aliasStore, err := NewManager(db, linkUpdater)
require.NoError(t, err)

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

aliasStore, err := NewManager(db)
updateChan := make(chan struct{}, 1)

linkUpdater := func(shortID lnwire.ShortChannelID) error {
updateChan <- struct{}{}
return nil
}

aliasStore, err := NewManager(db, linkUpdater)
require.NoError(t, err)

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

// TestAliasLifecycle tests that the aliases can be created and deleted.
func TestAliasLifecycle(t *testing.T) {
t.Parallel()

// Create the backend database and use this to create the aliasStore.
dbPath := filepath.Join(t.TempDir(), "testdb")
db, err := kvdb.Create(
kvdb.BoltBackendName, dbPath, true, kvdb.DefaultDBTimeout,
)
require.NoError(t, err)
defer db.Close()

updateChan := make(chan struct{}, 1)

linkUpdater := func(shortID lnwire.ShortChannelID) error {
updateChan <- struct{}{}
return nil
Comment thread
GeorgeTsagk marked this conversation as resolved.
Outdated
}

aliasStore, err := NewManager(db, linkUpdater)
require.NoError(t, err)

const (
base = uint64(123123123)
alias = uint64(456456456)
)

// Parse the aliases and base to short channel ID format.
baseScid := lnwire.NewShortChanIDFromInt(base)
aliasScid := lnwire.NewShortChanIDFromInt(alias)
aliasScid2 := lnwire.NewShortChanIDFromInt(alias + 1)

// Add the first alias.
err = aliasStore.AddLocalAlias(aliasScid, baseScid, false, true)
require.NoError(t, err)

// The link updater should be called.
<-updateChan

// Query the aliases and verify the results.
aliasList := aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 1)
require.Contains(t, aliasList, aliasScid)

// Add the second alias.
err = aliasStore.AddLocalAlias(aliasScid2, baseScid, false, true)
require.NoError(t, err)

// The link updater should be called.
<-updateChan

// Query the aliases and verify the results.
aliasList = aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 2)
require.Contains(t, aliasList, aliasScid)
require.Contains(t, aliasList, aliasScid2)

// Delete the first alias.
err = aliasStore.DeleteLocalAlias(aliasScid, baseScid)
require.NoError(t, err)

// The link updater should be called.
<-updateChan

// Query the aliases and verify that first one doesn't exist anymore.
aliasList = aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 1)
require.Contains(t, aliasList, aliasScid2)
require.NotContains(t, aliasList, aliasScid)

// Delete the second alias.
err = aliasStore.DeleteLocalAlias(aliasScid2, baseScid)
require.NoError(t, err)

// The link updater should be called.
<-updateChan

// Query the aliases and verify that none exists.
aliasList = aliasStore.GetAliases(baseScid)
require.Len(t, aliasList, 0)
}

// TestGetNextScid tests that given a current lnwire.ShortChannelID,
// getNextScid returns the expected alias to use next.
func TestGetNextScid(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions docs/release-notes/release-notes-0.18.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* Some new experimental [RPCs for managing SCID
aliases](https://github.com/lightningnetwork/lnd/pull/8509) were added under
the routerrpc package. These methods allow manually adding and deleting scid
aliases locally to your node.
> NOTE: these new RPC methods are marked as experimental
(`XAddLocalChanAliases` & `XDeleteLocalChanAliases`) and upon calling
them the aliases will not be communicated with the channel peer.
3 changes: 2 additions & 1 deletion funding/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ type aliasHandler interface {
GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID, error)

// AddLocalAlias persists an alias to an underlying alias store.
AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool) error
AddLocalAlias(lnwire.ShortChannelID, lnwire.ShortChannelID, bool,
bool) error

// GetAliases returns the set of aliases given the main SCID of a
// channel. This SCID will be an alias for zero-conf channels and will
Expand Down
8 changes: 4 additions & 4 deletions funding/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,7 @@ func (f *Manager) advancePendingChannelState(
// Persist the alias to the alias database.
baseScid := channel.ShortChannelID
err := f.cfg.AliasManager.AddLocalAlias(
baseScid, baseScid, true,
baseScid, baseScid, true, false,
)
if err != nil {
return fmt.Errorf("error adding local alias to "+
Expand Down Expand Up @@ -3108,7 +3108,7 @@ func (f *Manager) handleFundingConfirmation(
}

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

err = f.cfg.AliasManager.AddLocalAlias(
alias, completeChan.ShortChannelID,
false,
false, false,
)
if err != nil {
return err
Expand Down Expand Up @@ -3853,7 +3853,7 @@ func (f *Manager) handleChannelReady(peer lnpeer.Peer, //nolint:funlen
}

err = f.cfg.AliasManager.AddLocalAlias(
alias, channel.ShortChannelID, false,
alias, channel.ShortChannelID, false, false,
)
if err != nil {
log.Errorf("unable to add local alias: %v",
Expand Down
2 changes: 1 addition & 1 deletion funding/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (m *mockAliasMgr) GetPeerAlias(lnwire.ChannelID) (lnwire.ShortChannelID,
}

func (m *mockAliasMgr) AddLocalAlias(lnwire.ShortChannelID,
lnwire.ShortChannelID, bool) error {
lnwire.ShortChannelID, bool, bool) error {

return nil
}
Expand Down
Loading