Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[custom channels 2/5]: Extract PART2 from mega staging branch #9049

Merged
merged 28 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e31cdc4
aliasmgr: add delete local alias method
GeorgeTsagk Mar 4, 2024
ea92d0a
multi: refresh htlcswitch aliases on aliasmgr update
GeorgeTsagk Mar 12, 2024
80dfaeb
aliasmgr: add map type alias
guggero Aug 9, 2024
466f550
aliasmgr: avoid collision when requesting alias
guggero Aug 9, 2024
604bd81
aliasmgr: export alias start and end ranges
guggero Aug 9, 2024
70f82bc
routerrpc: add XAddLocalChanAliases & XDeleteLocalChanAliases
GeorgeTsagk Feb 29, 2024
705e567
itest: add dynamic scid alias routing test
GeorgeTsagk Mar 8, 2024
17c0a70
lnwire: add unit tests for `ExtraOpaqueData.PackRecords`
ffranr May 3, 2024
af50694
lnwire: add `ExtraOpaqueData` helper functions and methods
ffranr May 3, 2024
81f6a80
lnwire: add custom records field to type `UpdateAddHtlc`
ffranr Apr 13, 2024
8d1059f
lnwire: add custom records field to type `UpdateFulfillHtlc`
ffranr May 3, 2024
abca4b8
htlcswitch: add resume modified HTLC action to switch
ffranr Apr 13, 2024
fb14d8c
routerrpc: extend HTLC forward interceptor resp with modification fields
ffranr Apr 13, 2024
ccea257
itest: add itest for field modification HTLC interception response
ffranr Apr 13, 2024
aa86020
lnwallet: extract diskCommit, remove unused error return value
guggero Sep 2, 2024
878f964
multi: use wire records on payment and intercept flows
GeorgeTsagk Apr 16, 2024
42e358e
lnrpc: add wire records fields to payment+interceptor RPCs
GeorgeTsagk Apr 16, 2024
1b31835
channeldb+routing: persist first hop custom records
guggero Aug 30, 2024
afdceab
lnrpc: add first hop custom records to RPC payment info
guggero Aug 30, 2024
5cb68a5
lnwallet: expose commitment blob from channel
GeorgeTsagk May 15, 2024
5dcda25
htlcswitch: expose custom channel blob from link
GeorgeTsagk May 2, 2024
5b4de5f
routing: add TlvTrafficShaper to bandwidth hints
GeorgeTsagk Apr 19, 2024
f04fa54
routing: skip amtInRange for custom HTLCs
Roasbeef Jul 12, 2024
c391430
lnd: use impl cfg TlvTrafficShaper
GeorgeTsagk Apr 19, 2024
4804cbf
channeldb+routing: persist first hop custom data for route
guggero Aug 30, 2024
aa17543
routing: use first hop records on path finding
GeorgeTsagk May 2, 2024
81c8331
lnrpc: add first hop custom data to route
guggero Aug 30, 2024
427d41d
itest: add interceptor and first hop data tests
GeorgeTsagk Apr 16, 2024
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
206 changes: 189 additions & 17 deletions aliasmgr/aliasmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ import (
"fmt"
"sync"

"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnwire"
"golang.org/x/exp/maps"
)

// UpdateLinkAliases is a function type for a function that 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
guggero marked this conversation as resolved.
Show resolved Hide resolved

// ScidAliasMap is a map from a base short channel ID to a set of alias short
// channel IDs.
type ScidAliasMap map[lnwire.ShortChannelID][]lnwire.ShortChannelID

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 @@ -47,17 +58,18 @@ var (
// operations.
byteOrder = binary.BigEndian

// startBlockHeight is the starting block height of the alias range.
startingBlockHeight = 16_000_000
// AliasStartBlockHeight is the starting block height of the alias
// range.
AliasStartBlockHeight uint32 = 16_000_000

// endBlockHeight is the ending block height of the alias range.
endBlockHeight = 16_250_000
// AliasEndBlockHeight is the ending block height of the alias range.
AliasEndBlockHeight uint32 = 16_250_000

// StartingAlias is the first alias ShortChannelID that will get
// assigned by RequestAlias. The starting BlockHeight is chosen so that
// legitimate SCIDs in integration tests aren't mistaken for an alias.
StartingAlias = lnwire.ShortChannelID{
BlockHeight: uint32(startingBlockHeight),
BlockHeight: AliasStartBlockHeight,
TxIndex: 0,
TxPosition: 0,
}
Expand All @@ -68,6 +80,10 @@ var (
// errNoPeerAlias is returned when the peer's alias for a given
// channel is not found.
errNoPeerAlias = fmt.Errorf("no peer alias found")

// ErrAliasNotFound is returned when the alias is not found and can't
// be mapped to a base SCID.
ErrAliasNotFound = fmt.Errorf("alias not found")
)

// Manager is a struct that handles aliases for LND. It has an underlying
Expand All @@ -77,10 +93,14 @@ var (
type Manager struct {
backend kvdb.Backend

// linkAliasUpdater is a function used by the alias manager to
// facilitate live update of aliases in other subsystems.
linkAliasUpdater 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.
baseToSet map[lnwire.ShortChannelID][]lnwire.ShortChannelID
baseToSet ScidAliasMap

// aliasToBase is a mapping that maps all aliases for a given channel
// to its base SCID. This is only used for channels that have
Expand All @@ -98,9 +118,15 @@ type Manager struct {
}

// NewManager initializes an alias Manager from the passed database backend.
func NewManager(db kvdb.Backend) (*Manager, error) {
m := &Manager{backend: db}
m.baseToSet = make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
func NewManager(db kvdb.Backend, linkAliasUpdater UpdateLinkAliases) (*Manager,
error) {

m := &Manager{
backend: db,
baseToSet: make(ScidAliasMap),
linkAliasUpdater: linkAliasUpdater,
}

m.aliasToBase = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
m.peerAlias = make(map[lnwire.ChannelID]lnwire.ShortChannelID)

Expand Down Expand Up @@ -215,12 +241,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 {
ProofOfKeags marked this conversation as resolved.
Show resolved Hide resolved

// 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 +306,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.linkAliasUpdater(baseScid)
}

return nil
}

Expand Down Expand Up @@ -340,6 +388,74 @@ 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())

// If the user attempts to delete an alias that doesn't exist,
// we'll want to inform them about it and not just do nothing.
if aliasToBaseBucket.Get(aliasBytes[:]) == nil {
return ErrAliasNotFound
}

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 ErrAliasNotFound
}

// We'll filter the alias set and remove the alias from it.
aliasSet = fn.Filter(func(a lnwire.ShortChannelID) bool {
return a.ToUint64() != alias.ToUint64()
}, aliasSet)

// 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 (but this is only set if we gossip the alias).
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.linkAliasUpdater(baseScid)
}

// 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 Expand Up @@ -392,6 +508,19 @@ func (m *Manager) GetPeerAlias(chanID lnwire.ChannelID) (lnwire.ShortChannelID,
func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
var nextAlias lnwire.ShortChannelID

m.RLock()
defer m.RUnlock()

// haveAlias returns true if the passed alias is already assigned to a
// channel in the baseToSet map.
haveAlias := func(maybeNextAlias lnwire.ShortChannelID) bool {
return fn.Any(func(aliasList []lnwire.ShortChannelID) bool {
return fn.Any(func(alias lnwire.ShortChannelID) bool {
return alias == maybeNextAlias
}, aliasList)
}, maps.Values(m.baseToSet))
}

err := kvdb.Update(m.backend, func(tx kvdb.RwTx) error {
bucket, err := tx.CreateTopLevelBucket(aliasAllocBucket)
if err != nil {
Expand All @@ -404,6 +533,29 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
// StartingAlias to it.
nextAlias = StartingAlias

// If the very first alias is already assigned, we'll
// keep incrementing until we find an unassigned alias.
// This is to avoid collision with custom added SCID
// aliases that fall into the same range as the ones we
// generate here monotonically. Those custom SCIDs are
// stored in a different bucket, but we can just check
// the in-memory map for simplicity.
for {
if !haveAlias(nextAlias) {
break
}

nextAlias = getNextScid(nextAlias)

// Abort if we've reached the end of the range.
if nextAlias.BlockHeight >=
AliasEndBlockHeight {

return fmt.Errorf("range for custom " +
"aliases exhausted")
}
}

var scratch [8]byte
byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
return bucket.Put(lastAliasKey, scratch[:])
Expand All @@ -418,6 +570,26 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {
)
nextAlias = getNextScid(lastScid)

// If the next alias is already assigned, we'll keep
// incrementing until we find an unassigned alias. This is to
// avoid collision with custom added SCID aliases that fall into
// the same range as the ones we generate here monotonically.
// Those custom SCIDs are stored in a different bucket, but we
// can just check the in-memory map for simplicity.
for {
if !haveAlias(nextAlias) {
break
}

nextAlias = getNextScid(nextAlias)

// Abort if we've reached the end of the range.
if nextAlias.BlockHeight >= AliasEndBlockHeight {
return fmt.Errorf("range for custom " +
"aliases exhausted")
}
}

var scratch [8]byte
byteOrder.PutUint64(scratch[:], nextAlias.ToUint64())
return bucket.Put(lastAliasKey, scratch[:])
Expand All @@ -433,11 +605,11 @@ func (m *Manager) RequestAlias() (lnwire.ShortChannelID, error) {

// ListAliases returns a carbon copy of baseToSet. This is used by the rpc
// layer.
func (m *Manager) ListAliases() map[lnwire.ShortChannelID][]lnwire.ShortChannelID {
func (m *Manager) ListAliases() ScidAliasMap {
m.RLock()
defer m.RUnlock()

baseCopy := make(map[lnwire.ShortChannelID][]lnwire.ShortChannelID)
baseCopy := make(ScidAliasMap)

for k, v := range m.baseToSet {
setCopy := make([]lnwire.ShortChannelID, len(v))
Expand Down Expand Up @@ -496,10 +668,10 @@ func getNextScid(last lnwire.ShortChannelID) lnwire.ShortChannelID {

// IsAlias returns true if the passed SCID is an alias. The function determines
// this by looking at the BlockHeight. If the BlockHeight is greater than
// startingBlockHeight and less than endBlockHeight, then it is an alias
// AliasStartBlockHeight and less than AliasEndBlockHeight, then it is an alias
// assigned by RequestAlias. These bounds only apply to aliases we generate.
// Our peers are free to use any range they choose.
func IsAlias(scid lnwire.ShortChannelID) bool {
return scid.BlockHeight >= uint32(startingBlockHeight) &&
scid.BlockHeight < uint32(endBlockHeight)
return scid.BlockHeight >= AliasStartBlockHeight &&
scid.BlockHeight < AliasEndBlockHeight
}
Loading
Loading