Merge pull request #4199 from joostjager/no-legacy-split

routing: do not split payment if destination does not support mpp
This commit is contained in:
Joost Jager 2020-04-17 15:54:43 +02:00 committed by GitHub
commit 9c4f9416d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 54 deletions

@ -140,18 +140,20 @@ func (c *integratedRoutingContext) testPayment(maxShards uint32) ([]htlcAttempt,
MaxShards: maxShards, MaxShards: maxShards,
} }
session := &paymentSession{ session, err := newPaymentSession(
getBandwidthHints: getBandwidthHints, &payment, getBandwidthHints,
payment: &payment, func() (routingGraph, func(), error) {
pathFinder: findPath,
getRoutingGraph: func() (routingGraph, func(), error) {
return c.graph, func() {}, nil return c.graph, func() {}, nil
}, },
pathFindingConfig: c.pathFindingCfg, mc, c.pathFindingCfg,
missionControl: mc, )
minShardAmt: lnwire.NewMSatFromSatoshis(5000), if err != nil {
c.t.Fatal(err)
} }
// Override default minimum shard amount.
session.minShardAmt = lnwire.NewMSatFromSatoshis(5000)
// Now the payment control loop starts. It will keep trying routes until // Now the payment control loop starts. It will keep trying routes until
// the payment succeeds. // the payment succeeds.
var ( var (

@ -1,6 +1,10 @@
package routing package routing
import ( import (
"fmt"
"github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
@ -124,6 +128,36 @@ type paymentSession struct {
// specified in the payment is one, under no circumstances splitting // specified in the payment is one, under no circumstances splitting
// will happen and this value remains unused. // will happen and this value remains unused.
minShardAmt lnwire.MilliSatoshi minShardAmt lnwire.MilliSatoshi
// log is a payment session-specific logger.
log btclog.Logger
}
// newPaymentSession instantiates a new payment session.
func newPaymentSession(p *LightningPayment,
getBandwidthHints func() (map[uint64]lnwire.MilliSatoshi, error),
getRoutingGraph func() (routingGraph, func(), error),
missionControl MissionController, pathFindingConfig PathFindingConfig) (
*paymentSession, error) {
edges, err := RouteHintsToEdges(p.RouteHints, p.Target)
if err != nil {
return nil, err
}
logPrefix := fmt.Sprintf("PaymentSession(%x):", p.PaymentHash)
return &paymentSession{
additionalEdges: edges,
getBandwidthHints: getBandwidthHints,
payment: p,
pathFinder: findPath,
getRoutingGraph: getRoutingGraph,
pathFindingConfig: pathFindingConfig,
missionControl: missionControl,
minShardAmt: DefaultShardMinAmt,
log: build.NewPrefixLog(logPrefix, log),
}, nil
} }
// RequestRoute returns a route which is likely to be capable for successfully // RequestRoute returns a route which is likely to be capable for successfully
@ -182,8 +216,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
return nil, err return nil, err
} }
log.Debugf("PaymentSession for %x: trying pathfinding with %v", p.log.Debugf("pathfinding for amt=%v", maxAmt)
p.payment.PaymentHash, maxAmt)
// Get a routing graph. // Get a routing graph.
routingGraph, cleanup, err := p.getRoutingGraph() routingGraph, cleanup, err := p.getRoutingGraph()
@ -210,9 +243,22 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
switch { switch {
case err == errNoPathFound: case err == errNoPathFound:
// 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
}
// No splitting if this is the last shard. // No splitting if this is the last shard.
isLastShard := activeShards+1 >= p.payment.MaxShards isLastShard := activeShards+1 >= p.payment.MaxShards
if isLastShard { if isLastShard {
p.log.Debugf("not splitting because shard "+
"limit %v has been reached",
p.payment.MaxShards)
return nil, errNoPathFound return nil, errNoPathFound
} }
@ -222,6 +268,10 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
// Put a lower bound on the minimum shard size. // Put a lower bound on the minimum shard size.
if maxAmt < p.minShardAmt { if maxAmt < p.minShardAmt {
p.log.Debugf("not splitting because minimum "+
"shard amount %v has been reached",
p.minShardAmt)
return nil, errNoPathFound return nil, errNoPathFound
} }

@ -62,11 +62,6 @@ func (m *SessionSource) getRoutingGraph() (routingGraph, func(), error) {
func (m *SessionSource) NewPaymentSession(p *LightningPayment) ( func (m *SessionSource) NewPaymentSession(p *LightningPayment) (
PaymentSession, error) { PaymentSession, error) {
edges, err := RouteHintsToEdges(p.RouteHints, p.Target)
if err != nil {
return nil, err
}
sourceNode, err := m.Graph.SourceNode() sourceNode, err := m.Graph.SourceNode()
if err != nil { if err != nil {
return nil, err return nil, err
@ -78,16 +73,15 @@ func (m *SessionSource) NewPaymentSession(p *LightningPayment) (
return generateBandwidthHints(sourceNode, m.QueryBandwidth) return generateBandwidthHints(sourceNode, m.QueryBandwidth)
} }
return &paymentSession{ session, err := newPaymentSession(
additionalEdges: edges, p, getBandwidthHints, m.getRoutingGraph,
getBandwidthHints: getBandwidthHints, m.MissionControl, m.PathFindingConfig,
payment: p, )
pathFinder: findPath, if err != nil {
getRoutingGraph: m.getRoutingGraph, return nil, err
pathFindingConfig: m.PathFindingConfig, }
missionControl: m.MissionControl,
minShardAmt: DefaultShardMinAmt, return session, nil
}, nil
} }
// NewPaymentSessionEmpty creates a new paymentSession instance that is empty, // NewPaymentSessionEmpty creates a new paymentSession instance that is empty,

@ -13,9 +13,36 @@ func TestRequestRoute(t *testing.T) {
height = 10 height = 10
) )
findPath := func( cltvLimit := uint32(30)
g *graphParams, finalCltvDelta := uint16(8)
r *RestrictParams, cfg *PathFindingConfig,
payment := &LightningPayment{
CltvLimit: cltvLimit,
FinalCLTVDelta: finalCltvDelta,
Amount: 1000,
FeeLimit: 1000,
}
session, err := newPaymentSession(
payment,
func() (map[uint64]lnwire.MilliSatoshi,
error) {
return nil, nil
},
func() (routingGraph, func(), error) {
return &sessionGraph{}, func() {}, nil
},
&MissionControl{cfg: &MissionControlConfig{}},
PathFindingConfig{},
)
if err != nil {
t.Fatal(err)
}
// Override pathfinder with a mock.
session.pathFinder = func(
g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
source, target route.Vertex, amt lnwire.MilliSatoshi, source, target route.Vertex, amt lnwire.MilliSatoshi,
finalHtlcExpiry int32) ([]*channeldb.ChannelEdgePolicy, error) { finalHtlcExpiry int32) ([]*channeldb.ChannelEdgePolicy, error) {
@ -38,32 +65,6 @@ func TestRequestRoute(t *testing.T) {
return path, nil return path, nil
} }
cltvLimit := uint32(30)
finalCltvDelta := uint16(8)
payment := &LightningPayment{
CltvLimit: cltvLimit,
FinalCLTVDelta: finalCltvDelta,
Amount: 1000,
FeeLimit: 1000,
}
session := &paymentSession{
getBandwidthHints: func() (map[uint64]lnwire.MilliSatoshi,
error) {
return nil, nil
},
payment: payment,
pathFinder: findPath,
missionControl: &MissionControl{
cfg: &MissionControlConfig{},
},
getRoutingGraph: func() (routingGraph, func(), error) {
return &sessionGraph{}, func() {}, nil
},
}
route, err := session.RequestRoute( route, err := session.RequestRoute(
payment.Amount, payment.FeeLimit, 0, height, payment.Amount, payment.FeeLimit, 0, height,
) )