Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
113 changes: 99 additions & 14 deletions aliasmgr/aliasmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ var (
// operations.
byteOrder = binary.BigEndian

// AliasFlagNone indicates standard alias behavior where the alias
// will not be added to aliasToBase after the channel is confirmed.
AliasFlagNone byte = 0x00

// AliasFlagPersistent indicates the alias should persist in the
// aliasToBase map even after the base SCID is confirmed. This is
// useful for manually added aliases that need to remain accessible
// via FindBaseSCID after 6 confirmations.
AliasFlagPersistent byte = 0x01

// AliasStartBlockHeight is the starting block height of the alias
// range.
AliasStartBlockHeight uint32 = 16_000_000
Expand Down Expand Up @@ -146,6 +156,12 @@ func (m *Manager) populateMaps() error {
// populate the Manager's actual maps.
aliasMap := make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)

// This map tracks aliases that have the persistent flag set and should
// remain in aliasToBase even after confirmation.
persistentAliasMap := make(
map[lnwire.ShortChannelID]lnwire.ShortChannelID,
)

// This map caches the ChannelID/alias SCIDs stored in the database and
// is used to populate the Manager's cache.
peerAliasMap := make(map[lnwire.ChannelID]lnwire.ShortChannelID)
Expand Down Expand Up @@ -177,14 +193,23 @@ func (m *Manager) populateMaps() error {

err = aliasToBaseBucket.ForEach(func(k, v []byte) error {
// The key will be the alias SCID and the value will be
// the base SCID.
// the base SCID (8 bytes) optionally followed by flags
// (1 byte).
aliasScid := lnwire.NewShortChanIDFromInt(
byteOrder.Uint64(k),
)
baseScid := lnwire.NewShortChanIDFromInt(
byteOrder.Uint64(v),
byteOrder.Uint64(v[:8]),
)
aliasMap[aliasScid] = baseScid

// Check if the persistent flag is set. Backward
// compatible: old entries have len(v) == 8, new entries
// have len(v) == 9.
if len(v) > 8 && (v[8]&AliasFlagPersistent) != 0 {
persistentAliasMap[aliasScid] = baseScid
}

return nil
})
if err != nil {
Expand Down Expand Up @@ -214,6 +239,9 @@ func (m *Manager) populateMaps() error {
}, func() {
baseConfMap = make(map[lnwire.ShortChannelID]struct{})
aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
persistentAliasMap = make(
map[lnwire.ShortChannelID]lnwire.ShortChannelID,
)
peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
})
if err != nil {
Expand All @@ -233,6 +261,13 @@ func (m *Manager) populateMaps() error {
m.aliasToBase[aliasSCID] = baseSCID
}

// Add persistent aliases to aliasToBase even if they're confirmed.
// This allows FindBaseSCID to work for manually-added aliases that
// should survive confirmation.
for aliasSCID, baseSCID := range persistentAliasMap {
m.aliasToBase[aliasSCID] = baseSCID
}

// Populate the peer alias cache.
m.peerAlias = peerAliasMap

Expand All @@ -252,7 +287,8 @@ type addAliasCfg struct {
type AddLocalAliasOption func(cfg *addAliasCfg)

// WithBaseLookup is a functional option that controls whether a reverse lookup
// will be stored from the alias to the base scid.
// will be stored from the alias to the base scid. This reverse lookup is
// persisted and will not be wiped when the channel is confirmed.
func WithBaseLookup() AddLocalAliasOption {
return func(cfg *addAliasCfg) {
cfg.baseLookup = true
Expand All @@ -268,7 +304,8 @@ func WithBaseLookup() AddLocalAliasOption {
//
// NOTE: The following aliases will not be persisted (will be lost on restart):
// - Aliases that were created without gossip flag.
// - Aliases that correspond to confirmed channels.
// - Aliases that correspond to confirmed channels (unless WithBaseLookup
// option is used).
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
gossip, linkUpdate bool, opts ...AddLocalAliasOption) error {

Expand Down Expand Up @@ -315,14 +352,24 @@ func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
return err
}

var (
aliasBytes [8]byte
baseBytes [8]byte
)

var aliasBytes [8]byte
byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
return aliasToBaseBucket.Put(aliasBytes[:], baseBytes[:])

// Write base SCID (8 bytes) + flags (1 byte).
// Always write 9 bytes for consistency and future extensibility.
var valueBytes [9]byte
byteOrder.PutUint64(valueBytes[:8], baseScid.ToUint64())

// Set the persistent flag if baseLookup is requested.
// The baseLookup option indicates this is a manually-added
// alias that should persist even after confirmation.
if cfg.baseLookup {
valueBytes[8] = AliasFlagPersistent
} else {
valueBytes[8] = AliasFlagNone
}

return aliasToBaseBucket.Put(aliasBytes[:], valueBytes[:])
}, func() {})
if err != nil {
return err
Expand Down Expand Up @@ -396,6 +443,9 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
m.Lock()
defer m.Unlock()

// Track which aliases are persistent so we don't delete them.
persistentAliases := make(map[lnwire.ShortChannelID]struct{})

err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
baseConfBucket, err := tx.CreateTopLevelBucket(confirmedBucket)
if err != nil {
Expand All @@ -404,16 +454,51 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {

var baseBytes [8]byte
byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
return baseConfBucket.Put(baseBytes[:], []byte{})
}, func() {})
err = baseConfBucket.Put(baseBytes[:], []byte{})
if err != nil {
return err
}

// Check which aliases for this base are marked as persistent.
aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
if err != nil {
return err
}

return aliasToBaseBucket.ForEach(func(k, v []byte) error {
// Check if this entry maps to our baseScid.
entryBase := lnwire.NewShortChanIDFromInt(
byteOrder.Uint64(v[:8]),
)
if entryBase.ToUint64() != baseScid.ToUint64() {
return nil
}

// Check if persistent flag is set.
if len(v) > 8 && (v[8]&AliasFlagPersistent) != 0 {
aliasScid := lnwire.NewShortChanIDFromInt(
byteOrder.Uint64(k),
)
persistentAliases[aliasScid] = struct{}{}
}

return nil
})
Comment on lines +468 to +486

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of aliasToBaseBucket.ForEach here introduces a full scan of the aliasBucket. While this is consistent with the existing database design which optimizes for writes, it could lead to performance issues on nodes with a very large number of aliases, as DeleteSixConfs holds a write lock on the database and a mutex on the manager during this operation.

A potential improvement could be to introduce a new secondary index (a new kvdb bucket) that maps baseScid to its persistent aliases (baseScid -> []persistentAliases). This would make lookups in DeleteSixConfs much faster (O(log N) instead of O(N)), at the cost of slightly more complexity when adding or deleting persistent aliases (requiring a read-modify-write on the new bucket).

Given that DeleteSixConfs is called for every confirmed channel, while manual alias management is likely less frequent, this trade-off might be worthwhile for long-term scalability.

}, func() {
persistentAliases = make(map[lnwire.ShortChannelID]struct{})
})
if err != nil {
return err
}

// Now that the database state has been updated, we'll delete all of
// the aliasToBase mappings for this SCID.
// the aliasToBase mappings for this SCID, except persistent ones.
for alias, base := range m.aliasToBase {
if base.ToUint64() == baseScid.ToUint64() {
_, isPersistent := persistentAliases[alias]
if isPersistent {
continue
}
delete(m.aliasToBase, alias)
}
}
Expand Down
Loading
Loading