Skip to content

Commit 83f9ab0

Browse files
committed
aliasmgr: allow persisting manually added aliases
Previously we were able to manually add scid aliases for channels but that would slightly misbehave especially around persistence and restarts. By default when a channel gets confirmed it won't get any of its aliases reloaded in memory. We change the existing base-lookup flag to also signal that an alias may be forcefully persisted and reloaded to the in-memory maps when the alias manager starts up. We achieve this by appending an extra byte to all the alias storage entries. Previously we'd have 8 bytes, and now we extend the entry to have 9 bytes. For old entries, the x00 byte is assumed which signals non-persistency. Any new entries will always have 9 bytes stored with the last byte serving as the signal for persistence.
1 parent 1e15efc commit 83f9ab0

File tree

1 file changed

+95
-20
lines changed

1 file changed

+95
-20
lines changed

aliasmgr/aliasmgr.go

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ var (
8787
ErrAliasNotFound = fmt.Errorf("alias not found")
8888
)
8989

90+
const (
91+
// AliasFlagNone indicates standard alias behavior where the alias
92+
// will not be added to aliasToBase after the channel is confirmed.
93+
AliasFlagNone byte = 0x00
94+
95+
// AliasFlagPersistent indicates the alias should persist in the
96+
// aliasToBase map even after the base SCID is confirmed. This is
97+
// useful for manually added aliases that need to remain accessible
98+
// via FindBaseSCID after 6 confirmations.
99+
AliasFlagPersistent byte = 0x01
100+
)
101+
90102
// Manager is a struct that handles aliases for LND. It has an underlying
91103
// database that can allocate aliases for channels, stores the peer's last
92104
// alias for use in our hop hints, and contains mappings that both the Switch
@@ -146,6 +158,12 @@ func (m *Manager) populateMaps() error {
146158
// populate the Manager's actual maps.
147159
aliasMap := make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
148160

161+
// This map tracks aliases that have the persistent flag set and should
162+
// remain in aliasToBase even after confirmation.
163+
persistentAliasMap := make(
164+
map[lnwire.ShortChannelID]lnwire.ShortChannelID,
165+
)
166+
149167
// This map caches the ChannelID/alias SCIDs stored in the database and
150168
// is used to populate the Manager's cache.
151169
peerAliasMap := make(map[lnwire.ChannelID]lnwire.ShortChannelID)
@@ -177,14 +195,23 @@ func (m *Manager) populateMaps() error {
177195

178196
err = aliasToBaseBucket.ForEach(func(k, v []byte) error {
179197
// The key will be the alias SCID and the value will be
180-
// the base SCID.
198+
// the base SCID (8 bytes) optionally followed by flags
199+
// (1 byte).
181200
aliasScid := lnwire.NewShortChanIDFromInt(
182201
byteOrder.Uint64(k),
183202
)
184203
baseScid := lnwire.NewShortChanIDFromInt(
185-
byteOrder.Uint64(v),
204+
byteOrder.Uint64(v[:8]),
186205
)
187206
aliasMap[aliasScid] = baseScid
207+
208+
// Check if the persistent flag is set. Backward
209+
// compatible: old entries have len(v) == 8, new entries
210+
// have len(v) == 9.
211+
if len(v) > 8 && (v[8]&AliasFlagPersistent) != 0 {
212+
persistentAliasMap[aliasScid] = baseScid
213+
}
214+
188215
return nil
189216
})
190217
if err != nil {
@@ -214,6 +241,9 @@ func (m *Manager) populateMaps() error {
214241
}, func() {
215242
baseConfMap = make(map[lnwire.ShortChannelID]struct{})
216243
aliasMap = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
244+
persistentAliasMap = make(
245+
map[lnwire.ShortChannelID]lnwire.ShortChannelID,
246+
)
217247
peerAliasMap = make(map[lnwire.ChannelID]lnwire.ShortChannelID)
218248
})
219249
if err != nil {
@@ -233,6 +263,13 @@ func (m *Manager) populateMaps() error {
233263
m.aliasToBase[aliasSCID] = baseSCID
234264
}
235265

266+
// Add persistent aliases to aliasToBase even if they're confirmed.
267+
// This allows FindBaseSCID to work for manually-added aliases that
268+
// should survive confirmation.
269+
for aliasSCID, baseSCID := range persistentAliasMap {
270+
m.aliasToBase[aliasSCID] = baseSCID
271+
}
272+
236273
// Populate the peer alias cache.
237274
m.peerAlias = peerAliasMap
238275

@@ -252,7 +289,8 @@ type addAliasCfg struct {
252289
type AddLocalAliasOption func(cfg *addAliasCfg)
253290

254291
// WithBaseLookup is a functional option that controls whether a reverse lookup
255-
// will be stored from the alias to the base scid.
292+
// will be stored from the alias to the base scid. This reverse lookup is
293+
// persisted and will not be wiped when the channel is confirmed.
256294
func WithBaseLookup() AddLocalAliasOption {
257295
return func(cfg *addAliasCfg) {
258296
cfg.baseLookup = true
@@ -268,7 +306,8 @@ func WithBaseLookup() AddLocalAliasOption {
268306
//
269307
// NOTE: The following aliases will not be persisted (will be lost on restart):
270308
// - Aliases that were created without gossip flag.
271-
// - Aliases that correspond to confirmed channels.
309+
// - Aliases that correspond to confirmed channels (unless WithBaseLookup
310+
// option is used).
272311
func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
273312
gossip, linkUpdate bool, opts ...AddLocalAliasOption) error {
274313

@@ -315,14 +354,25 @@ func (m *Manager) AddLocalAlias(alias, baseScid lnwire.ShortChannelID,
315354
return err
316355
}
317356

318-
var (
319-
aliasBytes [8]byte
320-
baseBytes [8]byte
321-
)
322-
357+
var aliasBytes [8]byte
323358
byteOrder.PutUint64(aliasBytes[:], alias.ToUint64())
324-
byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
325-
return aliasToBaseBucket.Put(aliasBytes[:], baseBytes[:])
359+
360+
// Write base SCID (8 bytes) + flags (1 byte).
361+
// Always write 9 bytes for consistency and future
362+
// extensibility.
363+
var valueBytes [9]byte
364+
byteOrder.PutUint64(valueBytes[:8], baseScid.ToUint64())
365+
366+
// Set the persistent flag if baseLookup is requested.
367+
// The baseLookup option indicates this is a manually-added
368+
// alias that should persist even after confirmation.
369+
if cfg.baseLookup {
370+
valueBytes[8] = AliasFlagPersistent
371+
} else {
372+
valueBytes[8] = AliasFlagNone
373+
}
374+
375+
return aliasToBaseBucket.Put(aliasBytes[:], valueBytes[:])
326376
}, func() {})
327377
if err != nil {
328378
return err
@@ -404,20 +454,45 @@ func (m *Manager) DeleteSixConfs(baseScid lnwire.ShortChannelID) error {
404454

405455
var baseBytes [8]byte
406456
byteOrder.PutUint64(baseBytes[:], baseScid.ToUint64())
407-
return baseConfBucket.Put(baseBytes[:], []byte{})
457+
err = baseConfBucket.Put(baseBytes[:], []byte{})
458+
if err != nil {
459+
return err
460+
}
461+
462+
// Now we'll iterate over the alias bucket and delete all of
463+
// the aliases that do not have the persistency flag set.
464+
aliasToBaseBucket, err := tx.CreateTopLevelBucket(aliasBucket)
465+
if err != nil {
466+
return err
467+
}
468+
469+
return aliasToBaseBucket.ForEach(func(k, v []byte) error {
470+
// Check if this entry maps to our baseScid.
471+
entryBase := lnwire.NewShortChanIDFromInt(
472+
byteOrder.Uint64(v[:8]),
473+
)
474+
if entryBase.ToUint64() != baseScid.ToUint64() {
475+
return nil
476+
}
477+
478+
// Check whether the persistency flag is set.
479+
persist := len(v) > 8 && (v[8]&AliasFlagPersistent) != 0
480+
481+
aliasScid := lnwire.NewShortChanIDFromInt(
482+
byteOrder.Uint64(k),
483+
)
484+
485+
if !persist {
486+
delete(m.aliasToBase, aliasScid)
487+
}
488+
489+
return nil
490+
})
408491
}, func() {})
409492
if err != nil {
410493
return err
411494
}
412495

413-
// Now that the database state has been updated, we'll delete all of
414-
// the aliasToBase mappings for this SCID.
415-
for alias, base := range m.aliasToBase {
416-
if base.ToUint64() == baseScid.ToUint64() {
417-
delete(m.aliasToBase, alias)
418-
}
419-
}
420-
421496
return nil
422497
}
423498

0 commit comments

Comments
 (0)