server+htlcswitch: prevent privacy leaks, allow alias routing

This intent of this change is to prevent privacy leaks when routing
with aliases and also to allow routing when using an alias. The
aliases are our aliases.

Introduces are two maps:

* aliasToReal:
  This is an N->1 mapping for a channel. The keys are the set of
  aliases and the value is the confirmed, on-chain SCID.

* baseIndex:
  This is also an N->1 mapping for a channel. The keys are the set
  of aliases and the value is the "base" SCID (whatever is in the
  OpenChannel.ShortChannelID field). There is also a base->base
  mapping, so not all keys are aliases.

The above maps are populated when a link is added to the switch and
when the channel has confirmed on-chain. The maps are not removed
from if the link is removed, but this is fine since forwarding won't
occur.

* getLinkByMapping
  This function is introduced to adhere to the spec requirements that
  using the confirmed SCID of a private, scid-alias-feature-bit
  channel does not work. Lnd implements a stricter version of the spec
  and disallows this behavior if the feature-bit was negotiated, rather
  than just the channel type. The old, privacy-leak behavior is
  preserved.

The spec also requires that if we must fail back an HTLC, the
ChannelUpdate must use the SCID of whatever was in the onion, to avoid
a privacy leak. This is also done by passing in the relevant SCID to
the mailbox and link. Lnd will also cancel back on the "incoming" side
if the InterceptableSwitch was used or if the link failed to decrypt
the onion. In this case, we are cautious and replace the SCID if an
alias exists.
This commit is contained in:
eugene committed 2022-07-07 17:10:28 -04:00
1 parent 59fea192b7
commit 13c15e8038
12 files changed
+2406 -298

No files matched your search

+489 -53
View File
@@ -9,6 +9,7 @@ import (
"sync/atomic"
"time"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
@@ -200,6 +201,16 @@ type Config struct {
// DustThreshold is the threshold in milli-satoshis after which we'll
// fail incoming or outgoing dust payments for a particular channel.
DustThreshold lnwire.MilliSatoshi
// SignAliasUpdate is used when sending FailureMessages backwards for
// option_scid_alias channels. This avoids a potential privacy leak by
// replacing the public, confirmed SCID with the alias in the
// ChannelUpdate.
SignAliasUpdate func(u *lnwire.ChannelUpdate) (*ecdsa.Signature,
error)
// IsAlias returns whether or not a given SCID is an alias.
IsAlias func(scid lnwire.ShortChannelID) bool
}
// Switch is the central messaging bus for all incoming/outgoing HTLCs.
@@ -247,8 +258,7 @@ type Switch struct {
indexMtx sync.RWMutex
// pendingLinkIndex holds links that have not had their final, live
// short_chan_id assigned. These links can be transitioned into the
// primary linkIndex by using UpdateShortChanID to load their live id.
// short_chan_id assigned.
pendingLinkIndex map[lnwire.ChannelID]ChannelLink
// links is a map of channel id and channel link which manages
@@ -311,6 +321,21 @@ type Switch struct {
// contractcourt. This is used so the Switch can properly forward them,
// even on restarts.
resMsgStore *resolutionStore
// aliasToReal is a map used for option-scid-alias feature-bit links.
// The alias SCID is the key and the real, confirmed SCID is the value.
// If the channel is unconfirmed, there will not be a mapping for it.
// Since channels can have multiple aliases, this map is essentially a
// N->1 mapping for a channel. This MUST be accessed with the indexMtx.
aliasToReal map[lnwire.ShortChannelID]lnwire.ShortChannelID
// baseIndex is a map used for option-scid-alias feature-bit links.
// The value is the SCID of the link's ShortChannelID. This value may
// be an alias for zero-conf channels or a confirmed SCID for
// non-zero-conf channels with the option-scid-alias feature-bit. The
// key includes the value itself and also any other aliases. This MUST
// be accessed with the indexMtx.
baseIndex map[lnwire.ShortChannelID]lnwire.ShortChannelID
}
// New creates the new instance of htlc switch.
@@ -345,11 +370,14 @@ func New(cfg Config, currentHeight uint32) (*Switch, error) {
quit: make(chan struct{}),
}
s.aliasToReal = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
s.baseIndex = make(map[lnwire.ShortChannelID]lnwire.ShortChannelID)
s.mailOrchestrator = newMailOrchestrator(&mailOrchConfig{
fetchUpdate: s.cfg.FetchLastChannelUpdate,
forwardPackets: s.ForwardPackets,
clock: s.cfg.Clock,
expiry: s.cfg.HTLCExpiry,
forwardPackets: s.ForwardPackets,
clock: s.cfg.Clock,
expiry: s.cfg.HTLCExpiry,
failMailboxUpdate: s.failMailboxUpdate,
})
return s, nil
@@ -725,14 +753,28 @@ func (s *Switch) ForwardPackets(linkQuit chan struct{},
// failures.
if len(failedPackets) > 0 {
var failure lnwire.FailureMessage
update, err := s.cfg.FetchLastChannelUpdate(
failedPackets[0].incomingChanID,
)
if err != nil {
failure = &lnwire.FailTemporaryNodeFailure{}
incomingID := failedPackets[0].incomingChanID
// If the incoming channel is an option_scid_alias channel,
// then we'll need to replace the SCID in the ChannelUpdate.
update := s.failAliasUpdate(incomingID, true)
if update == nil {
// Fallback to the original non-option behavior.
update, err := s.cfg.FetchLastChannelUpdate(
incomingID,
)
if err != nil {
failure = &lnwire.FailTemporaryNodeFailure{}
} else {
failure = lnwire.NewTemporaryChannelFailure(
update,
)
}
} else {
// This is an option_scid_alias channel.
failure = lnwire.NewTemporaryChannelFailure(update)
}
linkError := NewDetailedLinkError(
failure, OutgoingFailureIncompleteForward,
)
@@ -804,10 +846,29 @@ func (s *Switch) getLocalLink(pkt *htlcPacket, htlc *lnwire.UpdateAddHTLC) (
// Try to find links by node destination.
s.indexMtx.RLock()
link, err := s.getLinkByShortID(pkt.outgoingChanID)
s.indexMtx.RUnlock()
defer s.indexMtx.RUnlock()
if err != nil {
log.Errorf("Link %v not found", pkt.outgoingChanID)
return nil, NewLinkError(&lnwire.FailUnknownNextPeer{})
// If the link was not found for the outgoingChanID, an outside
// subsystem may be using the confirmed SCID of a zero-conf
// channel. In this case, we'll consult the Switch maps to see
// if an alias exists and use the alias to lookup the link.
// This extra step is a consequence of not updating the Switch
// forwardingIndex when a zero-conf channel is confirmed. We
// don't need to change the outgoingChanID since the link will
// do that upon receiving the packet.
baseScid, ok := s.baseIndex[pkt.outgoingChanID]
if !ok {
log.Errorf("Link %v not found", pkt.outgoingChanID)
return nil, NewLinkError(&lnwire.FailUnknownNextPeer{})
}
// The base SCID was found, so we'll use that to fetch the
// link.
link, err = s.getLinkByShortID(baseScid)
if err != nil {
log.Errorf("Link %v not found", baseScid)
return nil, NewLinkError(&lnwire.FailUnknownNextPeer{})
}
}
if !link.EligibleToForward() {
@@ -1043,8 +1104,11 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
// same incoming and outgoing channel. If our node does not
// allow forwards of this nature, we fail the htlc early. This
// check is in place to disallow inefficiently routed htlcs from
// locking up our balance.
linkErr := checkCircularForward(
// locking up our balance. With channels where the
// option-scid-alias feature was negotiated, we also have to be
// sure that the IDs aren't the same since one or both could be
// an alias.
linkErr := s.checkCircularForward(
packet.incomingChanID, packet.outgoingChanID,
s.cfg.AllowCircularRoute, htlc.PaymentHash,
)
@@ -1053,7 +1117,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
}
s.indexMtx.RLock()
targetLink, err := s.getLinkByShortID(packet.outgoingChanID)
targetLink, err := s.getLinkByMapping(packet)
if err != nil {
s.indexMtx.RUnlock()
@@ -1101,6 +1165,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
htlc.PaymentHash, packet.incomingAmount,
packet.amount, packet.incomingTimeout,
packet.outgoingTimeout, currentHeight,
packet.originalOutgoingChanID,
)
}
@@ -1306,12 +1371,51 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
// checkCircularForward checks whether a forward is circular (arrives and
// departs on the same link) and returns a link error if the switch is
// configured to disallow this behaviour.
func checkCircularForward(incoming, outgoing lnwire.ShortChannelID,
func (s *Switch) checkCircularForward(incoming, outgoing lnwire.ShortChannelID,
allowCircular bool, paymentHash lntypes.Hash) *LinkError {
// If the route is not circular we do not need to perform any further
// checks.
if incoming != outgoing {
// If they are equal, we can skip the alias mapping checks.
if incoming == outgoing {
// The switch may be configured to allow circular routes, so
// just log and return nil.
if allowCircular {
log.Debugf("allowing circular route over link: %v "+
"(payment hash: %x)", incoming, paymentHash)
return nil
}
// Otherwise, we'll return a temporary channel failure.
return NewDetailedLinkError(
lnwire.NewTemporaryChannelFailure(nil),
OutgoingFailureCircularRoute,
)
}
// We'll fetch the "base" SCID from the baseIndex for the incoming and
// outgoing SCIDs. If either one does not have a base SCID, then the
// two channels are not equal since one will be a channel that does not
// need a mapping and SCID equality was checked above. If the "base"
// SCIDs are equal, then this is a circular route. Otherwise, it isn't.
s.indexMtx.RLock()
incomingBaseScid, ok := s.baseIndex[incoming]
if !ok {
// This channel does not use baseIndex, bail out.
s.indexMtx.RUnlock()
return nil
}
outgoingBaseScid, ok := s.baseIndex[outgoing]
if !ok {
// This channel does not use baseIndex, bail out.
s.indexMtx.RUnlock()
return nil
}
s.indexMtx.RUnlock()
// Check base SCID equality.
if incomingBaseScid != outgoingBaseScid {
// The base SCIDs are not equal so these are not the same
// channel.
return nil
}
@@ -2170,6 +2274,9 @@ func (s *Switch) AddLink(link ChannelLink) error {
mailbox := s.mailOrchestrator.GetOrCreateMailBox(chanID, shortChanID)
link.AttachMailBox(mailbox)
// Attach the Switch's failAliasUpdate function to the link.
link.attachFailAliasUpdate(s.failAliasUpdate)
if err := link.Start(); err != nil {
s.removeLink(chanID)
return err
@@ -2196,12 +2303,14 @@ func (s *Switch) AddLink(link ChannelLink) error {
// addLiveLink adds a link to all associated forwarding index, this makes it a
// candidate for forwarding HTLCs.
func (s *Switch) addLiveLink(link ChannelLink) {
linkScid := link.ShortChanID()
// We'll add the link to the linkIndex which lets us quickly
// look up a channel when we need to close or register it, and
// the forwarding index which'll be used when forwarding HTLC's
// in the multi-hop setting.
s.linkIndex[link.ChanID()] = link
s.forwardingIndex[link.ShortChanID()] = link
s.forwardingIndex[linkScid] = link
// Next we'll add the link to the interface index so we can
// quickly look up all the channels for a particular node.
@@ -2210,6 +2319,42 @@ func (s *Switch) addLiveLink(link ChannelLink) {
s.interfaceIndex[peerPub] = make(map[lnwire.ChannelID]ChannelLink)
}
s.interfaceIndex[peerPub][link.ChanID()] = link
aliases := link.getAliases()
if link.isZeroConf() {
if link.zeroConfConfirmed() {
// Since the zero-conf channel has confirmed, we can
// populate the aliasToReal mapping.
confirmedScid := link.confirmedScid()
for _, alias := range aliases {
s.aliasToReal[alias] = confirmedScid
}
// Add the confirmed SCID as a key in the baseIndex.
s.baseIndex[confirmedScid] = linkScid
}
// Now we populate the baseIndex which will be used to fetch
// the link given any of the channel's alias SCIDs or the real
// SCID. The link's SCID is an alias, so we don't need to
// special-case it like the option-scid-alias feature-bit case
// further down.
for _, alias := range aliases {
s.baseIndex[alias] = linkScid
}
} else if link.negotiatedAliasFeature() {
// The link's SCID is the confirmed SCID for non-zero-conf
// option-scid-alias feature bit channels.
for _, alias := range aliases {
s.aliasToReal[alias] = linkScid
s.baseIndex[alias] = linkScid
}
// Since the link's SCID is confirmed, it was not included in
// the baseIndex above as a key. Add it now.
s.baseIndex[linkScid] = linkScid
}
}
// GetLink is used to initiate the handling of the get link command. The
@@ -2245,7 +2390,21 @@ func (s *Switch) GetLinkByShortID(chanID lnwire.ShortChannelID) (ChannelLink,
s.indexMtx.RLock()
defer s.indexMtx.RUnlock()
return s.getLinkByShortID(chanID)
link, err := s.getLinkByShortID(chanID)
if err != nil {
// If we failed to find the link under the passed-in SCID, we
// consult the Switch's baseIndex map to see if the confirmed
// SCID was used for a zero-conf channel.
aliasID, ok := s.baseIndex[chanID]
if !ok {
return nil, err
}
// An alias was found, use it to lookup if a link exists.
return s.getLinkByShortID(aliasID)
}
return link, nil
}
// getLinkByShortID attempts to return the link which possesses the target
@@ -2261,6 +2420,93 @@ func (s *Switch) getLinkByShortID(chanID lnwire.ShortChannelID) (ChannelLink, er
return link, nil
}
// getLinkByMapping attempts to fetch the link via the htlcPacket's
// outgoingChanID, possibly using a mapping. If it finds the link via mapping,
// the outgoingChanID will be changed so that an error can be properly
// attributed when looping over linkErrs in handlePacketForward.
//
// * If the outgoingChanID is an alias, we'll fetch the link regardless if it's
// public or not.
//
// * If the outgoingChanID is a confirmed SCID, we'll need to do more checks.
// - If there is no entry found in baseIndex, fetch the link. This channel
// did not have the option-scid-alias feature negotiated (which includes
// zero-conf and option-scid-alias channel-types).
// - If there is an entry found, fetch the link from forwardingIndex and
// fail if this is a private link.
//
// NOTE: This MUST be called with the indexMtx read lock held.
func (s *Switch) getLinkByMapping(pkt *htlcPacket) (ChannelLink, error) {
// Determine if this ShortChannelID is an alias or a confirmed SCID.
chanID := pkt.outgoingChanID
aliasID := s.cfg.IsAlias(chanID)
// Set the originalOutgoingChanID so the proper channel_update can be
// sent back if the option-scid-alias feature bit was negotiated.
pkt.originalOutgoingChanID = chanID
if aliasID {
// Since outgoingChanID is an alias, we'll fetch the link via
// baseIndex.
baseScid, ok := s.baseIndex[chanID]
if !ok {
// No mapping exists, bail.
return nil, ErrChannelLinkNotFound
}
// A mapping exists, so use baseScid to find the link in the
// forwardingIndex.
link, ok := s.forwardingIndex[baseScid]
if !ok {
// Link not found, bail.
return nil, ErrChannelLinkNotFound
}
// Change the packet's outgoingChanID field so that errors are
// properly attributed.
pkt.outgoingChanID = baseScid
// Return the link without checking if it's private or not.
return link, nil
}
// The outgoingChanID is a confirmed SCID. Attempt to fetch the base
// SCID from baseIndex.
baseScid, ok := s.baseIndex[chanID]
if !ok {
// outgoingChanID is not a key in base index meaning this
// channel did not have the option-scid-alias feature bit
// negotiated. We'll fetch the link and return it.
link, ok := s.forwardingIndex[chanID]
if !ok {
// The link wasn't found, bail out.
return nil, ErrChannelLinkNotFound
}
return link, nil
}
// Fetch the link whose internal SCID is baseScid.
link, ok := s.forwardingIndex[baseScid]
if !ok {
// Link wasn't found, bail out.
return nil, ErrChannelLinkNotFound
}
// If the link is unadvertised, we fail since the real SCID was used to
// forward over it and this is a channel where the option-scid-alias
// feature bit was negotiated.
if link.IsUnadvertised() {
return nil, ErrChannelLinkNotFound
}
// The link is public so the confirmed SCID can be used to forward over
// it. We'll also replace pkt's outgoingChanID field so errors can
// properly be attributed in the calling function.
pkt.outgoingChanID = baseScid
return link, nil
}
// HasActiveLink returns true if the given channel ID has a link in the link
// index AND the link is eligible to forward.
func (s *Switch) HasActiveLink(chanID lnwire.ChannelID) bool {
@@ -2357,50 +2603,38 @@ func (s *Switch) removeLink(chanID lnwire.ChannelID) ChannelLink {
return link
}
// UpdateShortChanID updates the short chan ID for an existing channel. This is
// required in the case of a re-org and re-confirmation or a channel, or in the
// case that a link was added to the switch before its short chan ID was known.
// UpdateShortChanID locates the link with the passed-in chanID and updates the
// underlying channel state. This is only used in zero-conf channels to allow
// the confirmed SCID to be updated.
func (s *Switch) UpdateShortChanID(chanID lnwire.ChannelID) error {
s.indexMtx.Lock()
defer s.indexMtx.Unlock()
// Locate the target link in the pending link index. If no such link
// exists, then we will ignore the request.
link, ok := s.pendingLinkIndex[chanID]
// Locate the target link in the link index. If no such link exists,
// then we will ignore the request.
link, ok := s.linkIndex[chanID]
if !ok {
return fmt.Errorf("link %v not found", chanID)
}
oldShortChanID := link.ShortChanID()
// Try to update the link's short channel ID, returning early if this
// update failed.
shortChanID, err := link.UpdateShortChanID()
// Try to update the link's underlying channel state, returning early
// if this update failed.
_, err := link.UpdateShortChanID()
if err != nil {
return err
}
// Reject any blank short channel ids.
if shortChanID == hop.Source {
return fmt.Errorf("refusing trivial short_chan_id for chan_id=%v"+
"live link", chanID)
// Since the zero-conf channel is confirmed, we should populate the
// aliasToReal map and update the baseIndex.
aliases := link.getAliases()
confirmedScid := link.confirmedScid()
for _, alias := range aliases {
s.aliasToReal[alias] = confirmedScid
}
log.Infof("Updated short_chan_id for ChannelLink(%v): old=%v, new=%v",
chanID, oldShortChanID, shortChanID)
// Since the link was in the pending state before, we will remove it
// from the pending link index and add it to the live link index so that
// it can be available in forwarding.
delete(s.pendingLinkIndex, chanID)
s.addLiveLink(link)
// Finally, alert the mail orchestrator to the change of short channel
// ID, and deliver any unclaimed packets to the link.
mailbox := s.mailOrchestrator.GetOrCreateMailBox(chanID, shortChanID)
s.mailOrchestrator.BindLiveShortChanID(
mailbox, chanID, shortChanID,
)
s.baseIndex[confirmedScid] = link.ShortChanID()
return nil
}
@@ -2569,3 +2803,205 @@ func (s *Switch) evaluateDustThreshold(link ChannelLink,
// If we reached this point, this HTLC is fine to forward.
return false
}
// failMailboxUpdate is passed to the mailbox orchestrator which in turn passes
// it to individual mailboxes. It allows the mailboxes to construct a
// FailureMessage when failing back HTLC's due to expiry and may include an
// alias in the ShortChannelID field. The outgoingScid is the SCID originally
// used in the onion. The mailboxScid is the SCID that the mailbox and link
// use. The mailboxScid is only used in the non-alias case, so it is always
// the confirmed SCID.
func (s *Switch) failMailboxUpdate(outgoingScid,
mailboxScid lnwire.ShortChannelID) lnwire.FailureMessage {
// Try to use the failAliasUpdate function in case this is a channel
// that uses aliases. If it returns nil, we'll fallback to the original
// pre-alias behavior.
update := s.failAliasUpdate(outgoingScid, false)
if update == nil {
// Execute the fallback behavior.
var err error
update, err = s.cfg.FetchLastChannelUpdate(mailboxScid)
if err != nil {
return &lnwire.FailTemporaryNodeFailure{}
}
}
return lnwire.NewTemporaryChannelFailure(update)
}
// failAliasUpdate prepares a ChannelUpdate for a failed incoming or outgoing
// HTLC on a channel where the option-scid-alias feature bit was negotiated. If
// the associated channel is not one of these, this function will return nil
// and the caller is expected to handle this properly. In this case, a return
// to the original non-alias behavior is expected.
func (s *Switch) failAliasUpdate(scid lnwire.ShortChannelID,
incoming bool) *lnwire.ChannelUpdate {
// This function does not defer the unlocking because of the database
// lookups for ChannelUpdate.
s.indexMtx.RLock()
if s.cfg.IsAlias(scid) {
// The alias SCID was used. In the incoming case this means
// the channel is zero-conf as the link sets the scid. In the
// outgoing case, the sender set the scid to use and may be
// either the alias or the confirmed one, if it exists.
realScid, ok := s.aliasToReal[scid]
if !ok {
// The real, confirmed SCID does not exist yet. Find
// the "base" SCID that the link uses via the
// baseIndex. If we can't find it, return nil. This
// means the channel is zero-conf.
baseScid, ok := s.baseIndex[scid]
s.indexMtx.RUnlock()
if !ok {
return nil
}
update, err := s.cfg.FetchLastChannelUpdate(baseScid)
if err != nil {
return nil
}
// Replace the baseScid with the passed-in alias.
update.ShortChannelID = scid
sig, err := s.cfg.SignAliasUpdate(update)
if err != nil {
return nil
}
update.Signature, err = lnwire.NewSigFromSignature(sig)
if err != nil {
return nil
}
return update
}
s.indexMtx.RUnlock()
// Fetch the SCID via the confirmed SCID and replace it with
// the alias.
update, err := s.cfg.FetchLastChannelUpdate(realScid)
if err != nil {
return nil
}
// In the incoming case, we want to ensure that we don't leak
// the UTXO in case the channel is private. In the outgoing
// case, since the alias was used, we do the same thing.
update.ShortChannelID = scid
sig, err := s.cfg.SignAliasUpdate(update)
if err != nil {
return nil
}
update.Signature, err = lnwire.NewSigFromSignature(sig)
if err != nil {
return nil
}
return update
}
// If the confirmed SCID is not in baseIndex, this is not an
// option-scid-alias or zero-conf channel.
baseScid, ok := s.baseIndex[scid]
if !ok {
s.indexMtx.RUnlock()
return nil
}
// Fetch the link so we can get an alias to use in the ShortChannelID
// of the ChannelUpdate.
link, ok := s.forwardingIndex[baseScid]
s.indexMtx.RUnlock()
if !ok {
// This should never happen, but if it does for some reason,
// fallback to the old behavior.
return nil
}
aliases := link.getAliases()
if len(aliases) == 0 {
// This should never happen, but if it does, fallback.
return nil
}
// Fetch the ChannelUpdate via the real, confirmed SCID.
update, err := s.cfg.FetchLastChannelUpdate(scid)
if err != nil {
return nil
}
// The incoming case will replace the ShortChannelID in the retrieved
// ChannelUpdate with the alias to ensure no privacy leak occurs. This
// would happen if a private non-zero-conf option-scid-alias
// feature-bit channel leaked its UTXO here rather than supplying an
// alias. In the outgoing case, the confirmed SCID was actually used
// for forwarding in the onion, so no replacement is necessary as the
// sender knows the scid.
if incoming {
// We will replace and sign the update with the first alias.
// Since this happens on the incoming side, it's not actually
// possible to know what the sender used in the onion.
update.ShortChannelID = aliases[0]
sig, err := s.cfg.SignAliasUpdate(update)
if err != nil {
return nil
}
update.Signature, err = lnwire.NewSigFromSignature(sig)
if err != nil {
return nil
}
}
return update
}
// AddAliasForLink instructs the Switch to update its in-memory maps to reflect
// that a link has a new alias.
func (s *Switch) AddAliasForLink(chanID lnwire.ChannelID,
alias lnwire.ShortChannelID) error {
// Fetch the link so that we can update the underlying channel's set of
// aliases.
s.indexMtx.RLock()
link, err := s.getLink(chanID)
s.indexMtx.RUnlock()
if err != nil {
return err
}
// If the link is a channel where the option-scid-alias feature bit was
// not negotiated, we'll return an error.
if !link.negotiatedAliasFeature() {
return fmt.Errorf("attempted to update non-alias channel")
}
linkScid := link.ShortChanID()
// We'll update the maps so the Switch includes this alias in its
// forwarding decisions.
if link.isZeroConf() {
if link.zeroConfConfirmed() {
// If the channel has confirmed on-chain, we'll
// add this alias to the aliasToReal map.
confirmedScid := link.confirmedScid()
s.aliasToReal[alias] = confirmedScid
}
// Add this alias to the baseIndex mapping.
s.baseIndex[alias] = linkScid
} else if link.negotiatedAliasFeature() {
// The channel is confirmed, so we'll populate the aliasToReal
// and baseIndex maps.
s.aliasToReal[alias] = linkScid
s.baseIndex[alias] = linkScid
}
return nil
}