lnd/htlcswitch/interfaces.go

422 lines
16 KiB
Go
Raw Normal View History

package htlcswitch
import (
"context"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
)
// InvoiceDatabase is an interface which represents the persistent subsystem
// which may search, lookup and settle invoices.
type InvoiceDatabase interface {
// LookupInvoice attempts to look up an invoice according to its 32
// byte payment hash.
LookupInvoice(context.Context, lntypes.Hash) (invoices.Invoice, error)
// NotifyExitHopHtlc attempts to mark an invoice as settled. If the
// invoice is a debug invoice, then this method is a noop as debug
// invoices are never fully settled. The return value describes how the
// htlc should be resolved. If the htlc cannot be resolved immediately,
// the resolution is sent on the passed in hodlChan later. The eob
// field passes the entire onion hop payload into the invoice registry
// for decoding purposes.
NotifyExitHopHtlc(payHash lntypes.Hash, paidAmount lnwire.MilliSatoshi,
expiry uint32, currentHeight int32,
circuitKey models.CircuitKey, hodlChan chan<- interface{},
payload invoices.Payload) (invoices.HtlcResolution, error)
// CancelInvoice attempts to cancel the invoice corresponding to the
// passed payment hash.
CancelInvoice(ctx context.Context, payHash lntypes.Hash) error
// SettleHodlInvoice settles a hold invoice.
SettleHodlInvoice(ctx context.Context, preimage lntypes.Preimage) error
// HodlUnsubscribeAll unsubscribes from all htlc resolutions.
HodlUnsubscribeAll(subscriber chan<- interface{})
}
// packetHandler is an interface used exclusively by the Switch to handle
// htlcPacket and pass them to the link implementation.
type packetHandler interface {
// handleSwitchPacket handles the switch packets. These packets might
// be forwarded to us from another channel link in case the htlc
// update came from another peer or if the update was created by user
// initially.
//
// NOTE: This function should block as little as possible.
handleSwitchPacket(*htlcPacket) error
}
// dustHandler is an interface used exclusively by the Switch to evaluate
// whether a link has too much dust exposure.
type dustHandler interface {
// getDustSum returns the dust sum on either the local or remote
// commitment.
getDustSum(remote bool) lnwire.MilliSatoshi
// getFeeRate returns the current channel feerate.
getFeeRate() chainfee.SatPerKWeight
// getDustClosure returns a closure that can evaluate whether a passed
// HTLC is dust.
getDustClosure() dustClosure
}
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.
2022-04-04 22:44:51 +02:00
// scidAliasHandler is an interface that the ChannelLink implements so it can
// properly handle option_scid_alias channels.
type scidAliasHandler interface {
// attachFailAliasUpdate allows the link to properly fail incoming
// HTLCs on option_scid_alias channels.
attachFailAliasUpdate(failClosure func(
sid lnwire.ShortChannelID,
incoming bool) *lnwire.ChannelUpdate)
// getAliases fetches the link's underlying aliases. This is used by
// the Switch to determine whether to forward an HTLC and where to
// forward an HTLC.
getAliases() []lnwire.ShortChannelID
// isZeroConf returns whether or not the underlying channel is a
// zero-conf channel.
isZeroConf() bool
// negotiatedAliasFeature returns whether the option-scid-alias feature
// bit was negotiated.
negotiatedAliasFeature() bool
// confirmedScid returns the confirmed SCID for a zero-conf channel.
confirmedScid() lnwire.ShortChannelID
// zeroConfConfirmed returns whether or not the zero-conf channel has
// confirmed.
zeroConfConfirmed() bool
}
// ChannelUpdateHandler is an interface that provides methods that allow
// sending lnwire.Message to the underlying link as well as querying state.
type ChannelUpdateHandler interface {
// HandleChannelUpdate handles the htlc requests as settle/add/fail
// which sent to us from remote peer we have a channel with.
//
// NOTE: This function MUST be non-blocking (or block as little as
// possible).
HandleChannelUpdate(lnwire.Message)
// ChanID returns the channel ID for the channel link. The channel ID
// is a more compact representation of a channel's full outpoint.
ChanID() lnwire.ChannelID
// Bandwidth returns the amount of milli-satoshis which current link
// might pass through channel link. The value returned from this method
// represents the up to date available flow through the channel. This
// takes into account any forwarded but un-cleared HTLC's, and any
// HTLC's which have been set to the over flow queue.
Bandwidth() lnwire.MilliSatoshi
// EligibleToForward returns a bool indicating if the channel is able
// to actively accept requests to forward HTLC's. A channel may be
// active, but not able to forward HTLC's if it hasn't yet finalized
// the pre-channel operation protocol with the remote peer. The switch
// will use this function in forwarding decisions accordingly.
EligibleToForward() bool
// MayAddOutgoingHtlc returns an error if we may not add an outgoing
// htlc to the channel, taking the amount of the htlc to add as a
// parameter.
MayAddOutgoingHtlc(lnwire.MilliSatoshi) error
// EnableAdds sets the ChannelUpdateHandler state to allow
// UpdateAddHtlc's in the specified direction. It returns true if the
// state was changed and false if the desired state was already set
// before the method was called.
EnableAdds(direction LinkDirection) bool
2024-01-31 11:09:02 +01:00
// DisableAdds sets the ChannelUpdateHandler state to allow
// UpdateAddHtlc's in the specified direction. It returns true if the
// state was changed and false if the desired state was already set
// before the method was called.
DisableAdds(direction LinkDirection) bool
// IsFlushing returns true when UpdateAddHtlc's are disabled in the
// direction of the argument.
IsFlushing(direction LinkDirection) bool
// OnFlushedOnce adds a hook that will be called the next time the
// channel state reaches zero htlcs. This hook will only ever be called
// once. If the channel state already has zero htlcs, then this will be
// called immediately.
OnFlushedOnce(func())
// OnCommitOnce adds a hook that will be called the next time a
// CommitSig message is sent in the argument's LinkDirection. This hook
// will only ever be called once. If no CommitSig is owed in the
// argument's LinkDirection, then we will call this hook immediately.
OnCommitOnce(LinkDirection, func())
}
// CommitHookID is a value that is used to uniquely identify hooks in the
// ChannelUpdateHandler's commitment update lifecycle. You should never need to
// construct one of these by hand, nor should you try.
type CommitHookID uint64
// FlushHookID is a value that is used to uniquely identify hooks in the
// ChannelUpdateHandler's flush lifecycle. You should never need to construct
// one of these by hand, nor should you try.
type FlushHookID uint64
// LinkDirection is used to query and change any link state on a per-direction
// basis.
type LinkDirection bool
const (
// Incoming is the direction from the remote peer to our node.
Incoming LinkDirection = false
// Outgoing is the direction from our node to the remote peer.
Outgoing LinkDirection = true
)
// ChannelLink is an interface which represents the subsystem for managing the
// incoming htlc requests, applying the changes to the channel, and also
// propagating/forwarding it to htlc switch.
//
2022-08-22 20:58:42 +02:00
// abstraction level
// ^
// |
// | - - - - - - - - - - - - Lightning - - - - - - - - - - - - -
// |
// | (Switch) (Switch) (Switch)
// | Alice <-- channel link --> Bob <-- channel link --> Carol
// |
// | - - - - - - - - - - - - - TCP - - - - - - - - - - - - - - -
// |
// | (Peer) (Peer) (Peer)
// | Alice <----- tcp conn --> Bob <---- tcp conn -----> Carol
// |
type ChannelLink interface {
// TODO(roasbeef): modify interface to embed mail boxes?
// Embed the packetHandler interface.
packetHandler
// Embed the ChannelUpdateHandler interface.
ChannelUpdateHandler
// Embed the dustHandler interface.
dustHandler
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.
2022-04-04 22:44:51 +02:00
// Embed the scidAliasHandler interface.
scidAliasHandler
// IsUnadvertised returns true if the underlying channel is
// unadvertised.
IsUnadvertised() bool
// ChannelPoint returns the channel outpoint for the channel link.
ChannelPoint() wire.OutPoint
// ShortChanID returns the short channel ID for the channel link. The
// short channel ID encodes the exact location in the main chain that
// the original funding output can be found.
ShortChanID() lnwire.ShortChannelID
// UpdateShortChanID updates the short channel ID for a link. This may
// be required in the event that a link is created before the short
// chan ID for it is known, or a re-org occurs, and the funding
// transaction changes location within the chain.
UpdateShortChanID() (lnwire.ShortChannelID, error)
// UpdateForwardingPolicy updates the forwarding policy for the target
// ChannelLink. Once updated, the link will use the new forwarding
// policy to govern if it an incoming HTLC should be forwarded or not.
UpdateForwardingPolicy(models.ForwardingPolicy)
// CheckHtlcForward should return a nil error if the passed HTLC details
// satisfy the current forwarding policy fo the target link. Otherwise,
// a LinkError with a valid protocol failure message should be returned
// in order to signal to the source of the HTLC, the policy consistency
// issue.
CheckHtlcForward(payHash [32]byte, incomingAmt lnwire.MilliSatoshi,
amtToForward lnwire.MilliSatoshi,
incomingTimeout, outgoingTimeout uint32,
inboundFee models.InboundFee,
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.
2022-04-04 22:44:51 +02:00
heightNow uint32, scid lnwire.ShortChannelID) *LinkError
htlcswitch: perform fee related checks at forwarding time In this commit, we fix a very old, lingering bug within the link. When accepting an HTLC we are meant to validate the fee against the constraints of the *outgoing* link. This is due to the fact that we're offering a payment transit service on our outgoing link. Before this commit, we would use the policies of the *incoming* link. This would at times lead to odd routing errors as we would go to route, get an error update and then route again, repeating the process. With this commit, we'll properly use the incoming link for timelock related constraints, and the outgoing link for fee related constraints. We do this by introducing a new HtlcSatisfiesPolicy method in the link. This method should return a non-nil error if the link can carry the HTLC as it satisfies its current forwarding policy. We'll use this method now at *forwarding* time to ensure that we only forward to links that actually accept the policy. This fixes a number of bugs that existed before that could result in a link accepting an HTLC that actually violated its policy. In the case that the policy is violated for *all* links, we take care to return the error returned by the *target* link so the caller can update their sending accordingly. In this commit, we also remove the prior linkControl channel in the channelLink. Instead, of sending a message to update the internal link policy, we'll use a mutex in place. This simplifies the code, and also adds some necessary refactoring in anticipation of the next follow up commit.
2018-04-04 04:51:40 +02:00
// CheckHtlcTransit should return a nil error if the passed HTLC details
// satisfy the current channel policy. Otherwise, a LinkError with a
// valid protocol failure message should be returned in order to signal
// the violation. This call is intended to be used for locally initiated
// payments for which there is no corresponding incoming htlc.
CheckHtlcTransit(payHash [32]byte, amt lnwire.MilliSatoshi,
timeout uint32, heightNow uint32) *LinkError
// Stats return the statistics of channel link. Number of updates,
// total sent/received milli-satoshis.
Stats() (uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi)
// Peer returns the serialized public key of remote peer with which we
// have the channel link opened.
PeerPubKey() [33]byte
// AttachMailBox delivers an active MailBox to the link. The MailBox may
// have buffered messages.
AttachMailBox(MailBox)
// Start/Stop are used to initiate the start/stop of the channel link
// functioning.
Start() error
Stop()
}
// ForwardingLog is an interface that represents a time series database which
// keep track of all successfully completed payment circuits. Every few
// seconds, the switch will collate and flush out all the successful payment
// circuits during the last interval.
type ForwardingLog interface {
// AddForwardingEvents is a method that should write out the set of
// forwarding events in a batch to persistent storage. Outside
// sub-systems can then query the contents of the log for analysis,
// visualizations, etc.
AddForwardingEvents([]channeldb.ForwardingEvent) error
}
// TowerClient is the primary interface used by the daemon to backup pre-signed
// justice transactions to watchtowers.
type TowerClient interface {
// RegisterChannel persistently initializes any channel-dependent
// parameters within the client. This should be called during link
// startup to ensure that the client is able to support the link during
// operation.
RegisterChannel(lnwire.ChannelID, channeldb.ChannelType) error
// BackupState initiates a request to back up a particular revoked
// state. If the method returns nil, the backup is guaranteed to be
// successful unless the justice transaction would create dust outputs
// when trying to abide by the negotiated policy.
BackupState(chanID *lnwire.ChannelID, stateNum uint64) error
}
// InterceptableHtlcForwarder is the interface to set the interceptor
// implementation that intercepts htlc forwards.
type InterceptableHtlcForwarder interface {
// SetInterceptor sets a ForwardInterceptor.
SetInterceptor(interceptor ForwardInterceptor)
// Resolve resolves an intercepted packet.
Resolve(res *FwdResolution) error
}
// ForwardInterceptor is a function that is invoked from the switch for every
// incoming htlc that is intended to be forwarded. It is passed with the
// InterceptedForward that contains the information about the packet and a way
// to resolve it manually later in case it is held.
// The return value indicates if this handler will take control of this forward
// and resolve it later or let the switch execute its default behavior.
type ForwardInterceptor func(InterceptedPacket) error
// InterceptedPacket contains the relevant information for the interceptor about
// an htlc.
type InterceptedPacket struct {
// IncomingCircuit contains the incoming channel and htlc id of the
// packet.
IncomingCircuit models.CircuitKey
// OutgoingChanID is the destination channel for this packet.
OutgoingChanID lnwire.ShortChannelID
// Hash is the payment hash of the htlc.
Hash lntypes.Hash
// OutgoingExpiry is the absolute block height at which the outgoing
// htlc expires.
OutgoingExpiry uint32
// OutgoingAmount is the amount to forward.
OutgoingAmount lnwire.MilliSatoshi
// IncomingExpiry is the absolute block height at which the incoming
// htlc expires.
IncomingExpiry uint32
// IncomingAmount is the amount of the accepted htlc.
IncomingAmount lnwire.MilliSatoshi
// CustomRecords are user-defined records in the custom type range that
// were included in the payload.
CustomRecords record.CustomSet
// OnionBlob is the onion packet for the next hop
OnionBlob [lnwire.OnionPacketSize]byte
// AutoFailHeight is the block height at which this intercept will be
// failed back automatically.
AutoFailHeight int32
}
// InterceptedForward is passed to the ForwardInterceptor for every forwarded
// htlc. It contains all the information about the packet which accordingly
// the interceptor decides if to hold or not.
// In addition this interface allows a later resolution by calling either
// Resume, Settle or Fail.
type InterceptedForward interface {
// Packet returns the intercepted packet.
Packet() InterceptedPacket
// Resume notifies the intention to resume an existing hold forward. This
// basically means the caller wants to resume with the default behavior for
// this htlc which usually means forward it.
Resume() error
// Settle notifies the intention to settle an existing hold
// forward with a given preimage.
Settle(lntypes.Preimage) error
// Fail notifies the intention to fail an existing hold forward with an
// encrypted failure reason.
Fail(reason []byte) error
// FailWithCode notifies the intention to fail an existing hold forward
// with the specified failure code.
FailWithCode(code lnwire.FailCode) error
}
// htlcNotifier is an interface which represents the input side of the
// HtlcNotifier which htlc events are piped through. This interface is intended
// to allow for mocking of the htlcNotifier in tests, so is unexported because
// it is not needed outside of the htlcSwitch package.
type htlcNotifier interface {
// NotifyForwardingEvent notifies the HtlcNotifier than a htlc has been
// forwarded.
NotifyForwardingEvent(key HtlcKey, info HtlcInfo,
eventType HtlcEventType)
// NotifyIncomingLinkFailEvent notifies that a htlc has failed on our
// incoming link. It takes an isReceive bool to differentiate between
// our node's receives and forwards.
NotifyLinkFailEvent(key HtlcKey, info HtlcInfo,
eventType HtlcEventType, linkErr *LinkError, incoming bool)
// NotifyForwardingFailEvent notifies the HtlcNotifier that a htlc we
// forwarded has failed down the line.
NotifyForwardingFailEvent(key HtlcKey, eventType HtlcEventType)
// NotifySettleEvent notifies the HtlcNotifier that a htlc that we
// committed to as part of a forward or a receive to our node has been
// settled.
NotifySettleEvent(key HtlcKey, preimage lntypes.Preimage,
eventType HtlcEventType)
// NotifyFinalHtlcEvent notifies the HtlcNotifier that the final outcome
// for an htlc has been determined.
NotifyFinalHtlcEvent(key models.CircuitKey,
info channeldb.FinalHtlcInfo)
}