mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-07-12 14:12:27 +02:00
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:
@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/go-errors/errors"
|
||||
@ -33,6 +34,10 @@ import (
|
||||
"github.com/lightningnetwork/lnd/ticker"
|
||||
)
|
||||
|
||||
func isAlias(scid lnwire.ShortChannelID) bool {
|
||||
return scid.BlockHeight >= 16_000_000 && scid.BlockHeight < 16_250_000
|
||||
}
|
||||
|
||||
type mockPreimageCache struct {
|
||||
sync.Mutex
|
||||
preimageMap map[lntypes.Hash]lntypes.Preimage
|
||||
@ -180,6 +185,12 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error)
|
||||
}
|
||||
}
|
||||
|
||||
signAliasUpdate := func(u *lnwire.ChannelUpdate) (*ecdsa.Signature,
|
||||
error) {
|
||||
|
||||
return testSig, nil
|
||||
}
|
||||
|
||||
cfg := Config{
|
||||
DB: db,
|
||||
FetchAllOpenChannels: db.ChannelStateDB().FetchAllOpenChannels,
|
||||
@ -188,21 +199,27 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error)
|
||||
FwdingLog: &mockForwardingLog{
|
||||
events: make(map[time.Time]channeldb.ForwardingEvent),
|
||||
},
|
||||
FetchLastChannelUpdate: func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) {
|
||||
return &lnwire.ChannelUpdate{}, nil
|
||||
FetchLastChannelUpdate: func(scid lnwire.ShortChannelID) (
|
||||
*lnwire.ChannelUpdate, error) {
|
||||
|
||||
return &lnwire.ChannelUpdate{
|
||||
ShortChannelID: scid,
|
||||
}, nil
|
||||
},
|
||||
Notifier: &mock.ChainNotifier{
|
||||
SpendChan: make(chan *chainntnfs.SpendDetail),
|
||||
EpochChan: make(chan *chainntnfs.BlockEpoch),
|
||||
ConfChan: make(chan *chainntnfs.TxConfirmation),
|
||||
},
|
||||
FwdEventTicker: ticker.NewForce(DefaultFwdEventInterval),
|
||||
LogEventTicker: ticker.NewForce(DefaultLogInterval),
|
||||
AckEventTicker: ticker.NewForce(DefaultAckInterval),
|
||||
HtlcNotifier: &mockHTLCNotifier{},
|
||||
Clock: clock.NewDefaultClock(),
|
||||
HTLCExpiry: time.Hour,
|
||||
DustThreshold: DefaultDustThreshold,
|
||||
FwdEventTicker: ticker.NewForce(DefaultFwdEventInterval),
|
||||
LogEventTicker: ticker.NewForce(DefaultLogInterval),
|
||||
AckEventTicker: ticker.NewForce(DefaultAckInterval),
|
||||
HtlcNotifier: &mockHTLCNotifier{},
|
||||
Clock: clock.NewDefaultClock(),
|
||||
HTLCExpiry: time.Hour,
|
||||
DustThreshold: DefaultDustThreshold,
|
||||
SignAliasUpdate: signAliasUpdate,
|
||||
IsAlias: isAlias,
|
||||
}
|
||||
|
||||
return New(cfg, startingHeight)
|
||||
@ -658,6 +675,11 @@ type mockChannelLink struct {
|
||||
|
||||
shortChanID lnwire.ShortChannelID
|
||||
|
||||
// Only used for zero-conf channels.
|
||||
realScid lnwire.ShortChannelID
|
||||
|
||||
aliases []lnwire.ShortChannelID
|
||||
|
||||
chanID lnwire.ChannelID
|
||||
|
||||
peer lnpeer.Peer
|
||||
@ -668,11 +690,22 @@ type mockChannelLink struct {
|
||||
|
||||
eligible bool
|
||||
|
||||
unadvertised bool
|
||||
|
||||
zeroConf bool
|
||||
|
||||
optionFeature bool
|
||||
|
||||
htlcID uint64
|
||||
|
||||
checkHtlcTransitResult *LinkError
|
||||
|
||||
checkHtlcForwardResult *LinkError
|
||||
|
||||
failAliasUpdate func(sid lnwire.ShortChannelID,
|
||||
incoming bool) *lnwire.ChannelUpdate
|
||||
|
||||
confirmedZC bool
|
||||
}
|
||||
|
||||
// completeCircuit is a helper method for adding the finalized payment circuit
|
||||
@ -712,16 +745,39 @@ func (f *mockChannelLink) deleteCircuit(pkt *htlcPacket) error {
|
||||
}
|
||||
|
||||
func newMockChannelLink(htlcSwitch *Switch, chanID lnwire.ChannelID,
|
||||
shortChanID lnwire.ShortChannelID, peer lnpeer.Peer, eligible bool,
|
||||
shortChanID, realScid lnwire.ShortChannelID, peer lnpeer.Peer,
|
||||
eligible, unadvertised, zeroConf, optionFeature bool,
|
||||
) *mockChannelLink {
|
||||
|
||||
return &mockChannelLink{
|
||||
htlcSwitch: htlcSwitch,
|
||||
chanID: chanID,
|
||||
shortChanID: shortChanID,
|
||||
peer: peer,
|
||||
eligible: eligible,
|
||||
aliases := make([]lnwire.ShortChannelID, 0)
|
||||
var realConfirmed bool
|
||||
|
||||
if zeroConf {
|
||||
aliases = append(aliases, shortChanID)
|
||||
}
|
||||
|
||||
if realScid != hop.Source {
|
||||
realConfirmed = true
|
||||
}
|
||||
|
||||
return &mockChannelLink{
|
||||
htlcSwitch: htlcSwitch,
|
||||
chanID: chanID,
|
||||
shortChanID: shortChanID,
|
||||
realScid: realScid,
|
||||
peer: peer,
|
||||
eligible: eligible,
|
||||
unadvertised: unadvertised,
|
||||
zeroConf: zeroConf,
|
||||
optionFeature: optionFeature,
|
||||
aliases: aliases,
|
||||
confirmedZC: realConfirmed,
|
||||
}
|
||||
}
|
||||
|
||||
// addAlias is not part of any interface method.
|
||||
func (f *mockChannelLink) addAlias(alias lnwire.ShortChannelID) {
|
||||
f.aliases = append(f.aliases, alias)
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) handleSwitchPacket(pkt *htlcPacket) error {
|
||||
@ -750,7 +806,8 @@ func (f *mockChannelLink) HandleChannelUpdate(lnwire.Message) {
|
||||
func (f *mockChannelLink) UpdateForwardingPolicy(_ ForwardingPolicy) {
|
||||
}
|
||||
func (f *mockChannelLink) CheckHtlcForward([32]byte, lnwire.MilliSatoshi,
|
||||
lnwire.MilliSatoshi, uint32, uint32, uint32) *LinkError {
|
||||
lnwire.MilliSatoshi, uint32, uint32, uint32,
|
||||
lnwire.ShortChannelID) *LinkError {
|
||||
|
||||
return f.checkHtlcForwardResult
|
||||
}
|
||||
@ -772,6 +829,32 @@ func (f *mockChannelLink) AttachMailBox(mailBox MailBox) {
|
||||
mailBox.SetDustClosure(f.getDustClosure())
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) attachFailAliasUpdate(closure func(
|
||||
sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate) {
|
||||
|
||||
f.failAliasUpdate = closure
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) getAliases() []lnwire.ShortChannelID {
|
||||
return f.aliases
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) isZeroConf() bool {
|
||||
return f.zeroConf
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) negotiatedAliasFeature() bool {
|
||||
return f.optionFeature
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) confirmedScid() lnwire.ShortChannelID {
|
||||
return f.realScid
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) zeroConfConfirmed() bool {
|
||||
return f.confirmedZC
|
||||
}
|
||||
|
||||
func (f *mockChannelLink) Start() error {
|
||||
f.mailBox.ResetMessages()
|
||||
f.mailBox.ResetPackets()
|
||||
@ -788,6 +871,7 @@ func (f *mockChannelLink) EligibleToForward() bool { return
|
||||
func (f *mockChannelLink) MayAddOutgoingHtlc(lnwire.MilliSatoshi) error { return nil }
|
||||
func (f *mockChannelLink) ShutdownIfChannelClean() error { return nil }
|
||||
func (f *mockChannelLink) setLiveShortChanID(sid lnwire.ShortChannelID) { f.shortChanID = sid }
|
||||
func (f *mockChannelLink) IsUnadvertised() bool { return f.unadvertised }
|
||||
func (f *mockChannelLink) UpdateShortChanID() (lnwire.ShortChannelID, error) {
|
||||
f.eligible = true
|
||||
return f.shortChanID, nil
|
||||
|
Reference in New Issue
Block a user