2019-01-24 20:50:53 +01:00
|
|
|
package routing
|
|
|
|
|
|
|
|
import (
|
2020-04-16 15:22:44 +02:00
|
|
|
"fmt"
|
|
|
|
|
2022-02-23 14:48:00 +01:00
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
2020-04-16 15:22:44 +02:00
|
|
|
"github.com/btcsuite/btclog"
|
2021-04-15 17:58:02 +02:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
2020-04-16 15:22:44 +02:00
|
|
|
"github.com/lightningnetwork/lnd/build"
|
2019-01-24 20:50:53 +01:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
2023-11-08 10:18:45 +01:00
|
|
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
2019-01-24 20:50:53 +01:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2019-04-05 17:36:11 +02:00
|
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
2019-01-24 20:50:53 +01:00
|
|
|
)
|
|
|
|
|
2019-07-24 02:59:31 +02:00
|
|
|
// BlockPadding is used to increment the finalCltvDelta value for the last hop
|
|
|
|
// to prevent an HTLC being failed if some blocks are mined while it's in-flight.
|
|
|
|
const BlockPadding uint16 = 3
|
|
|
|
|
2021-05-12 20:44:56 +02:00
|
|
|
// ValidateCLTVLimit is a helper function that validates that the cltv limit is
|
|
|
|
// greater than the final cltv delta parameter, optionally including the
|
|
|
|
// BlockPadding in this calculation.
|
|
|
|
func ValidateCLTVLimit(limit uint32, delta uint16, includePad bool) error {
|
|
|
|
if includePad {
|
|
|
|
delta += BlockPadding
|
|
|
|
}
|
|
|
|
|
|
|
|
if limit <= uint32(delta) {
|
|
|
|
return fmt.Errorf("cltv limit %v should be greater than %v",
|
|
|
|
limit, delta)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-01 00:13:27 +02:00
|
|
|
// noRouteError encodes a non-critical error encountered during path finding.
|
|
|
|
type noRouteError uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
// errNoTlvPayload is returned when the destination hop does not support
|
|
|
|
// a tlv payload.
|
|
|
|
errNoTlvPayload noRouteError = iota
|
|
|
|
|
|
|
|
// errNoPaymentAddr is returned when the destination hop does not
|
|
|
|
// support payment addresses.
|
|
|
|
errNoPaymentAddr
|
|
|
|
|
|
|
|
// errNoPathFound is returned when a path to the target destination does
|
|
|
|
// not exist in the graph.
|
|
|
|
errNoPathFound
|
|
|
|
|
2020-04-16 10:45:00 +02:00
|
|
|
// errInsufficientLocalBalance is returned when none of the local
|
|
|
|
// channels have enough balance for the payment.
|
|
|
|
errInsufficientBalance
|
|
|
|
|
2020-04-01 00:13:25 +02:00
|
|
|
// errEmptyPaySession is returned when the empty payment session is
|
|
|
|
// queried for a route.
|
2020-04-01 00:13:27 +02:00
|
|
|
errEmptyPaySession
|
2020-05-27 12:14:52 +02:00
|
|
|
|
|
|
|
// errUnknownRequiredFeature is returned when the destination node
|
|
|
|
// requires an unknown feature.
|
|
|
|
errUnknownRequiredFeature
|
|
|
|
|
|
|
|
// errMissingDependentFeature is returned when the destination node
|
|
|
|
// misses a feature that a feature that we require depends on.
|
|
|
|
errMissingDependentFeature
|
2019-11-21 12:05:43 +01:00
|
|
|
)
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
var (
|
|
|
|
// DefaultShardMinAmt is the default amount beyond which we won't try to
|
|
|
|
// further split the payment if no route is found. It is the minimum
|
|
|
|
// amount that we use as the shard size when splitting.
|
|
|
|
DefaultShardMinAmt = lnwire.NewMSatFromSatoshis(10000)
|
|
|
|
)
|
|
|
|
|
2022-02-07 13:58:28 +01:00
|
|
|
// Error returns the string representation of the noRouteError.
|
2020-04-01 00:13:27 +02:00
|
|
|
func (e noRouteError) Error() string {
|
|
|
|
switch e {
|
|
|
|
case errNoTlvPayload:
|
|
|
|
return "destination hop doesn't understand new TLV payloads"
|
|
|
|
|
|
|
|
case errNoPaymentAddr:
|
|
|
|
return "destination hop doesn't understand payment addresses"
|
|
|
|
|
|
|
|
case errNoPathFound:
|
|
|
|
return "unable to find a path to destination"
|
|
|
|
|
|
|
|
case errEmptyPaySession:
|
|
|
|
return "empty payment session"
|
|
|
|
|
2020-04-16 10:45:00 +02:00
|
|
|
case errInsufficientBalance:
|
|
|
|
return "insufficient local balance"
|
|
|
|
|
2020-05-27 12:14:52 +02:00
|
|
|
case errUnknownRequiredFeature:
|
|
|
|
return "unknown required feature"
|
|
|
|
|
|
|
|
case errMissingDependentFeature:
|
|
|
|
return "missing dependent feature"
|
|
|
|
|
2020-04-01 00:13:27 +02:00
|
|
|
default:
|
|
|
|
return "unknown no-route error"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// FailureReason converts a path finding error into a payment-level failure.
|
|
|
|
func (e noRouteError) FailureReason() channeldb.FailureReason {
|
|
|
|
switch e {
|
|
|
|
case
|
|
|
|
errNoTlvPayload,
|
|
|
|
errNoPaymentAddr,
|
|
|
|
errNoPathFound,
|
2020-05-27 12:14:52 +02:00
|
|
|
errEmptyPaySession,
|
|
|
|
errUnknownRequiredFeature,
|
|
|
|
errMissingDependentFeature:
|
2020-04-01 00:13:27 +02:00
|
|
|
|
|
|
|
return channeldb.FailureReasonNoRoute
|
|
|
|
|
2020-04-16 10:45:00 +02:00
|
|
|
case errInsufficientBalance:
|
|
|
|
return channeldb.FailureReasonInsufficientBalance
|
|
|
|
|
2020-04-01 00:13:27 +02:00
|
|
|
default:
|
|
|
|
return channeldb.FailureReasonError
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-23 20:05:30 +02:00
|
|
|
// PaymentSession is used during SendPayment attempts to provide routes to
|
|
|
|
// attempt. It also defines methods to give the PaymentSession additional
|
|
|
|
// information learned during the previous attempts.
|
|
|
|
type PaymentSession interface {
|
|
|
|
// RequestRoute returns the next route to attempt for routing the
|
2020-04-01 00:13:22 +02:00
|
|
|
// specified HTLC payment to the target node. The returned route should
|
|
|
|
// carry at most maxAmt to the target node, and pay at most feeLimit in
|
|
|
|
// fees. It can carry less if the payment is MPP. The activeShards
|
|
|
|
// argument should be set to instruct the payment session about the
|
|
|
|
// number of in flight HTLCS for the payment, such that it can choose
|
|
|
|
// splitting strategy accordingly.
|
2020-04-01 00:13:27 +02:00
|
|
|
//
|
|
|
|
// A noRouteError is returned if a non-critical error is encountered
|
|
|
|
// during path finding.
|
2020-04-01 00:13:22 +02:00
|
|
|
RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|
|
|
activeShards, height uint32) (*route.Route, error)
|
2021-04-15 18:00:17 +02:00
|
|
|
|
|
|
|
// UpdateAdditionalEdge takes an additional channel edge policy
|
|
|
|
// (private channels) and applies the update from the message. Returns
|
|
|
|
// a boolean to indicate whether the update has been applied without
|
|
|
|
// error.
|
|
|
|
UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, pubKey *btcec.PublicKey,
|
2023-11-08 10:18:45 +01:00
|
|
|
policy *models.CachedEdgePolicy) bool
|
2021-04-15 18:00:17 +02:00
|
|
|
|
|
|
|
// GetAdditionalEdgePolicy uses the public key and channel ID to query
|
|
|
|
// the ephemeral channel edge policy for additional edges. Returns a nil
|
|
|
|
// if nothing found.
|
|
|
|
GetAdditionalEdgePolicy(pubKey *btcec.PublicKey,
|
2023-11-08 10:18:45 +01:00
|
|
|
channelID uint64) *models.CachedEdgePolicy
|
2019-05-23 20:05:30 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 20:50:53 +01:00
|
|
|
// paymentSession is used during an HTLC routings session to prune the local
|
|
|
|
// chain view in response to failures, and also report those failures back to
|
2019-05-23 20:05:30 +02:00
|
|
|
// MissionControl. The snapshot copied for this session will only ever grow,
|
2019-01-24 20:50:53 +01:00
|
|
|
// and will now be pruned after a decay like the main view within mission
|
|
|
|
// control. We do this as we want to avoid the case where we continually try a
|
|
|
|
// bad edge or route multiple times in a session. This can lead to an infinite
|
|
|
|
// loop if payment attempts take long enough. An additional set of edges can
|
|
|
|
// also be provided to assist in reaching the payment's destination.
|
|
|
|
type paymentSession struct {
|
2023-11-20 17:54:37 +01:00
|
|
|
additionalEdges map[route.Vertex][]AdditionalEdge
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2021-10-21 13:55:22 +02:00
|
|
|
getBandwidthHints func(routingGraph) (bandwidthHints, error)
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2020-04-01 00:13:22 +02:00
|
|
|
payment *LightningPayment
|
|
|
|
|
2020-04-01 00:13:25 +02:00
|
|
|
empty bool
|
2019-02-13 11:35:55 +01:00
|
|
|
|
|
|
|
pathFinder pathFinder
|
2020-03-17 11:32:07 +01:00
|
|
|
|
2021-10-21 13:55:22 +02:00
|
|
|
getRoutingGraph func() (routingGraph, func(), error)
|
2020-03-17 11:32:07 +01:00
|
|
|
|
|
|
|
// pathFindingConfig defines global parameters that control the
|
2023-09-20 17:37:32 +02:00
|
|
|
// trade-off in path finding between fees and probability.
|
2020-03-17 11:32:07 +01:00
|
|
|
pathFindingConfig PathFindingConfig
|
|
|
|
|
|
|
|
missionControl MissionController
|
2020-01-28 16:07:34 +01:00
|
|
|
|
|
|
|
// minShardAmt is the amount beyond which we won't try to further split
|
|
|
|
// the payment if no route is found. If the maximum number of htlcs
|
|
|
|
// specified in the payment is one, under no circumstances splitting
|
|
|
|
// will happen and this value remains unused.
|
|
|
|
minShardAmt lnwire.MilliSatoshi
|
2020-04-16 15:22:44 +02:00
|
|
|
|
|
|
|
// log is a payment session-specific logger.
|
|
|
|
log btclog.Logger
|
2019-01-24 20:50:53 +01:00
|
|
|
}
|
|
|
|
|
2020-04-16 15:20:23 +02:00
|
|
|
// newPaymentSession instantiates a new payment session.
|
|
|
|
func newPaymentSession(p *LightningPayment,
|
2021-10-21 13:55:22 +02:00
|
|
|
getBandwidthHints func(routingGraph) (bandwidthHints, error),
|
|
|
|
getRoutingGraph func() (routingGraph, func(), error),
|
2020-04-16 15:20:23 +02:00
|
|
|
missionControl MissionController, pathFindingConfig PathFindingConfig) (
|
|
|
|
*paymentSession, error) {
|
|
|
|
|
|
|
|
edges, err := RouteHintsToEdges(p.RouteHints, p.Target)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-03-31 12:23:08 +02:00
|
|
|
logPrefix := fmt.Sprintf("PaymentSession(%x):", p.Identifier())
|
2020-04-16 15:22:44 +02:00
|
|
|
|
2020-04-16 15:20:23 +02:00
|
|
|
return &paymentSession{
|
|
|
|
additionalEdges: edges,
|
|
|
|
getBandwidthHints: getBandwidthHints,
|
|
|
|
payment: p,
|
|
|
|
pathFinder: findPath,
|
2021-10-21 13:55:22 +02:00
|
|
|
getRoutingGraph: getRoutingGraph,
|
2020-04-16 15:20:23 +02:00
|
|
|
pathFindingConfig: pathFindingConfig,
|
|
|
|
missionControl: missionControl,
|
|
|
|
minShardAmt: DefaultShardMinAmt,
|
2020-04-16 15:22:44 +02:00
|
|
|
log: build.NewPrefixLog(logPrefix, log),
|
2020-04-16 15:20:23 +02:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-01-24 20:50:53 +01:00
|
|
|
// RequestRoute returns a route which is likely to be capable for successfully
|
|
|
|
// routing the specified HTLC payment to the target node. Initially the first
|
|
|
|
// set of paths returned from this method may encounter routing failure along
|
|
|
|
// the way, however as more payments are sent, mission control will start to
|
|
|
|
// build an up to date view of the network itself. With each payment a new area
|
|
|
|
// will be explored, which feeds into the recommendations made for routing.
|
|
|
|
//
|
|
|
|
// NOTE: This function is safe for concurrent access.
|
2019-05-23 20:05:30 +02:00
|
|
|
// NOTE: Part of the PaymentSession interface.
|
2020-04-01 00:13:22 +02:00
|
|
|
func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|
|
|
activeShards, height uint32) (*route.Route, error) {
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2020-04-01 00:13:25 +02:00
|
|
|
if p.empty {
|
|
|
|
return nil, errEmptyPaySession
|
2019-01-24 20:50:53 +01:00
|
|
|
}
|
|
|
|
|
2019-07-24 02:59:31 +02:00
|
|
|
// Add BlockPadding to the finalCltvDelta so that the receiving node
|
|
|
|
// does not reject the HTLC if some blocks are mined while it's in-flight.
|
2020-04-01 00:13:22 +02:00
|
|
|
finalCltvDelta := p.payment.FinalCLTVDelta
|
2019-07-24 02:59:31 +02:00
|
|
|
finalCltvDelta += BlockPadding
|
|
|
|
|
2019-10-11 21:46:10 +02:00
|
|
|
// We need to subtract the final delta before passing it into path
|
|
|
|
// finding. The optimal path is independent of the final cltv delta and
|
|
|
|
// the path finding algorithm is unaware of this value.
|
2020-04-01 00:13:22 +02:00
|
|
|
cltvLimit := p.payment.CltvLimit - uint32(finalCltvDelta)
|
2019-02-13 11:53:32 +01:00
|
|
|
|
2019-01-24 20:50:53 +01:00
|
|
|
// TODO(roasbeef): sync logic amongst dist sys
|
|
|
|
|
|
|
|
// Taking into account this prune view, we'll attempt to locate a path
|
|
|
|
// to our destination, respecting the recommendations from
|
2019-05-23 20:05:30 +02:00
|
|
|
// MissionControl.
|
2019-06-18 18:30:56 +02:00
|
|
|
restrictions := &RestrictParams{
|
2020-05-07 11:48:39 +02:00
|
|
|
ProbabilitySource: p.missionControl.GetProbability,
|
|
|
|
FeeLimit: feeLimit,
|
|
|
|
OutgoingChannelIDs: p.payment.OutgoingChannelIDs,
|
|
|
|
LastHop: p.payment.LastHop,
|
|
|
|
CltvLimit: cltvLimit,
|
|
|
|
DestCustomRecords: p.payment.DestCustomRecords,
|
|
|
|
DestFeatures: p.payment.DestFeatures,
|
|
|
|
PaymentAddr: p.payment.PaymentAddr,
|
2024-02-03 13:09:11 +01:00
|
|
|
Amp: p.payment.amp,
|
2021-10-04 09:33:12 +02:00
|
|
|
Metadata: p.payment.Metadata,
|
2019-06-18 18:30:56 +02:00
|
|
|
}
|
|
|
|
|
2019-12-17 11:55:03 +01:00
|
|
|
finalHtlcExpiry := int32(height) + int32(finalCltvDelta)
|
|
|
|
|
2021-02-12 03:05:13 +01:00
|
|
|
// Before we enter the loop below, we'll make sure to respect the max
|
|
|
|
// payment shard size (if it's set), which is effectively our
|
|
|
|
// client-side MTU that we'll attempt to respect at all times.
|
|
|
|
maxShardActive := p.payment.MaxShardAmt != nil
|
|
|
|
if maxShardActive && maxAmt > *p.payment.MaxShardAmt {
|
|
|
|
p.log.Debug("Clamping payment attempt from %v to %v due to "+
|
|
|
|
"max shard size of %v", maxAmt,
|
|
|
|
*p.payment.MaxShardAmt, maxAmt)
|
|
|
|
|
|
|
|
maxAmt = *p.payment.MaxShardAmt
|
|
|
|
}
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
for {
|
2021-10-21 13:55:22 +02:00
|
|
|
// Get a routing graph.
|
|
|
|
routingGraph, cleanup, err := p.getRoutingGraph()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
// We'll also obtain a set of bandwidthHints from the lower
|
|
|
|
// layer for each of our outbound channels. This will allow the
|
|
|
|
// path finding to skip any links that aren't active or just
|
|
|
|
// don't have enough bandwidth to carry the payment. New
|
|
|
|
// bandwidth hints are queried for every new path finding
|
|
|
|
// attempt, because concurrent payments may change balances.
|
2021-10-21 13:55:22 +02:00
|
|
|
bandwidthHints, err := p.getBandwidthHints(routingGraph)
|
2020-01-28 16:07:34 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-04-16 15:22:44 +02:00
|
|
|
p.log.Debugf("pathfinding for amt=%v", maxAmt)
|
2020-01-28 16:07:34 +01:00
|
|
|
|
2021-10-21 13:55:22 +02:00
|
|
|
sourceVertex := routingGraph.sourceNode()
|
2020-01-28 16:07:34 +01:00
|
|
|
|
|
|
|
// Find a route for the current amount.
|
2022-11-17 13:59:21 +01:00
|
|
|
path, _, err := p.pathFinder(
|
2020-01-28 16:07:34 +01:00
|
|
|
&graphParams{
|
|
|
|
additionalEdges: p.additionalEdges,
|
|
|
|
bandwidthHints: bandwidthHints,
|
2021-10-21 13:55:22 +02:00
|
|
|
graph: routingGraph,
|
2020-01-28 16:07:34 +01:00
|
|
|
},
|
|
|
|
restrictions, &p.pathFindingConfig,
|
|
|
|
sourceVertex, p.payment.Target,
|
2021-11-23 12:06:33 +01:00
|
|
|
maxAmt, p.payment.TimePref, finalHtlcExpiry,
|
2020-01-28 16:07:34 +01:00
|
|
|
)
|
|
|
|
|
2021-10-21 13:55:22 +02:00
|
|
|
// Close routing graph.
|
|
|
|
cleanup()
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
switch {
|
|
|
|
case err == errNoPathFound:
|
2020-04-16 15:08:07 +02:00
|
|
|
// Don't split if this is a legacy payment without mpp
|
|
|
|
// record.
|
|
|
|
if p.payment.PaymentAddr == nil {
|
|
|
|
p.log.Debugf("not splitting because payment " +
|
|
|
|
"address is unspecified")
|
|
|
|
|
|
|
|
return nil, errNoPathFound
|
|
|
|
}
|
|
|
|
|
2021-03-16 12:02:12 +01:00
|
|
|
if p.payment.DestFeatures == nil {
|
|
|
|
p.log.Debug("Not splitting because " +
|
|
|
|
"destination DestFeatures is nil")
|
|
|
|
return nil, errNoPathFound
|
|
|
|
}
|
|
|
|
|
2021-05-06 18:18:22 +02:00
|
|
|
destFeatures := p.payment.DestFeatures
|
|
|
|
if !destFeatures.HasFeature(lnwire.MPPOptional) &&
|
|
|
|
!destFeatures.HasFeature(lnwire.AMPOptional) {
|
|
|
|
|
2021-02-12 03:05:13 +01:00
|
|
|
p.log.Debug("not splitting because " +
|
2021-05-06 18:18:22 +02:00
|
|
|
"destination doesn't declare MPP or AMP")
|
2021-01-15 18:56:32 +01:00
|
|
|
|
|
|
|
return nil, errNoPathFound
|
|
|
|
}
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
// No splitting if this is the last shard.
|
2020-04-22 09:19:11 +02:00
|
|
|
isLastShard := activeShards+1 >= p.payment.MaxParts
|
2020-01-28 16:07:34 +01:00
|
|
|
if isLastShard {
|
2020-04-16 15:22:44 +02:00
|
|
|
p.log.Debugf("not splitting because shard "+
|
|
|
|
"limit %v has been reached",
|
2020-04-22 09:19:11 +02:00
|
|
|
p.payment.MaxParts)
|
2020-04-16 15:22:44 +02:00
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
return nil, errNoPathFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is where the magic happens. If we can't find a
|
|
|
|
// route, try it for half the amount.
|
|
|
|
maxAmt /= 2
|
|
|
|
|
|
|
|
// Put a lower bound on the minimum shard size.
|
|
|
|
if maxAmt < p.minShardAmt {
|
2020-04-16 15:22:44 +02:00
|
|
|
p.log.Debugf("not splitting because minimum "+
|
|
|
|
"shard amount %v has been reached",
|
|
|
|
p.minShardAmt)
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
return nil, errNoPathFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go pathfinding.
|
|
|
|
continue
|
|
|
|
|
2020-04-16 10:45:00 +02:00
|
|
|
// If there isn't enough local bandwidth, there is no point in
|
|
|
|
// splitting. It won't be possible to create a complete set in
|
|
|
|
// any case, but the sent out partial payments would be held by
|
|
|
|
// the receiver until the mpp timeout.
|
|
|
|
case err == errInsufficientBalance:
|
|
|
|
p.log.Debug("not splitting because local balance " +
|
|
|
|
"is insufficient")
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
2020-01-28 16:07:34 +01:00
|
|
|
case err != nil:
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the next candidate path found, we'll attempt to turn
|
|
|
|
// this into a route by applying the time-lock and fee
|
|
|
|
// requirements.
|
|
|
|
route, err := newRoute(
|
|
|
|
sourceVertex, path, height,
|
|
|
|
finalHopParams{
|
|
|
|
amt: maxAmt,
|
|
|
|
totalAmt: p.payment.Amount,
|
|
|
|
cltvDelta: finalCltvDelta,
|
|
|
|
records: p.payment.DestCustomRecords,
|
|
|
|
paymentAddr: p.payment.PaymentAddr,
|
2021-10-04 09:33:12 +02:00
|
|
|
metadata: p.payment.Metadata,
|
2022-12-20 20:03:58 +01:00
|
|
|
}, nil,
|
2020-01-28 16:07:34 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return route, err
|
2020-03-17 11:32:07 +01:00
|
|
|
}
|
2019-01-24 20:50:53 +01:00
|
|
|
}
|
2021-04-15 17:58:02 +02:00
|
|
|
|
|
|
|
// UpdateAdditionalEdge updates the channel edge policy for a private edge. It
|
|
|
|
// validates the message signature and checks it's up to date, then applies the
|
|
|
|
// updates to the supplied policy. It returns a boolean to indicate whether
|
|
|
|
// there's an error when applying the updates.
|
|
|
|
func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
|
2023-11-08 10:18:45 +01:00
|
|
|
pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool {
|
2021-04-15 17:58:02 +02:00
|
|
|
|
|
|
|
// Validate the message signature.
|
|
|
|
if err := VerifyChannelUpdateSignature(msg, pubKey); err != nil {
|
|
|
|
log.Errorf(
|
|
|
|
"Unable to validate channel update signature: %v", err,
|
|
|
|
)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update channel policy for the additional edge.
|
|
|
|
policy.TimeLockDelta = msg.TimeLockDelta
|
|
|
|
policy.FeeBaseMSat = lnwire.MilliSatoshi(msg.BaseFee)
|
|
|
|
policy.FeeProportionalMillionths = lnwire.MilliSatoshi(msg.FeeRate)
|
|
|
|
|
|
|
|
log.Debugf("New private channel update applied: %v",
|
|
|
|
newLogClosure(func() string { return spew.Sdump(msg) }))
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAdditionalEdgePolicy uses the public key and channel ID to query the
|
|
|
|
// ephemeral channel edge policy for additional edges. Returns a nil if nothing
|
|
|
|
// found.
|
|
|
|
func (p *paymentSession) GetAdditionalEdgePolicy(pubKey *btcec.PublicKey,
|
2023-11-08 10:18:45 +01:00
|
|
|
channelID uint64) *models.CachedEdgePolicy {
|
2021-04-15 17:58:02 +02:00
|
|
|
|
|
|
|
target := route.NewVertex(pubKey)
|
|
|
|
|
|
|
|
edges, ok := p.additionalEdges[target]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, edge := range edges {
|
2023-11-20 17:54:37 +01:00
|
|
|
policy := edge.EdgePolicy()
|
|
|
|
if policy.ChannelID != channelID {
|
2021-04-15 17:58:02 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-11-20 17:54:37 +01:00
|
|
|
return policy
|
2021-04-15 17:58:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|