From 00903ef9f51f0d10636e7381332dba48a6d7a1c9 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 1 Apr 2020 00:13:22 +0200 Subject: [PATCH] routing/payment_session: make RequestRoute take max amt, fee limit and active shards In preparation for doing pathfinding for routes sending a value less than the total payment amount, we let the payment session take the max amount to send and the fee limit as arguments to RequestRoute. --- routing/mock_test.go | 4 +++- routing/payment_lifecycle.go | 6 +++++- routing/payment_session.go | 19 +++++++++++++------ routing/payment_session_test.go | 6 +++++- routing/router.go | 25 ++++++++++++++++++------- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/routing/mock_test.go b/routing/mock_test.go index 64a1e4f6..c6a780bb 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -122,7 +122,9 @@ type mockPaymentSession struct { var _ PaymentSession = (*mockPaymentSession)(nil) -func (m *mockPaymentSession) RequestRoute(height uint32) (*route.Route, error) { +func (m *mockPaymentSession) RequestRoute(_, _ lnwire.MilliSatoshi, + _, height uint32) (*route.Route, error) { + if len(m.routes) == 0 { return nil, fmt.Errorf("no routes") } diff --git a/routing/payment_lifecycle.go b/routing/payment_lifecycle.go index a0cb566a..f153d4b6 100644 --- a/routing/payment_lifecycle.go +++ b/routing/payment_lifecycle.go @@ -31,6 +31,8 @@ func (e errNoRoute) Error() string { // needed to resume if from any point. type paymentLifecycle struct { router *ChannelRouter + totalAmount lnwire.MilliSatoshi + feeLimit lnwire.MilliSatoshi paymentHash lntypes.Hash paySession PaymentSession timeoutChan <-chan time.Time @@ -267,7 +269,9 @@ func (p *paymentLifecycle) createNewPaymentAttempt() (lnwire.ShortChannelID, } // Create a new payment attempt from the given payment session. - rt, err := p.paySession.RequestRoute(uint32(p.currentHeight)) + rt, err := p.paySession.RequestRoute( + p.totalAmount, p.feeLimit, 0, uint32(p.currentHeight), + ) if err != nil { log.Warnf("Failed to find route for payment %x: %v", p.paymentHash, err) diff --git a/routing/payment_session.go b/routing/payment_session.go index fda93536..a5c04266 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -23,8 +23,14 @@ var ( // information learned during the previous attempts. type PaymentSession interface { // RequestRoute returns the next route to attempt for routing the - // specified HTLC payment to the target node. - RequestRoute(height uint32) (*route.Route, error) + // 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. + RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, + activeShards, height uint32) (*route.Route, error) } // paymentSession is used during an HTLC routings session to prune the local @@ -59,7 +65,8 @@ type paymentSession struct { // // NOTE: This function is safe for concurrent access. // NOTE: Part of the PaymentSession interface. -func (p *paymentSession) RequestRoute(height uint32) (*route.Route, error) { +func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, + activeShards, height uint32) (*route.Route, error) { switch { @@ -94,7 +101,7 @@ func (p *paymentSession) RequestRoute(height uint32) (*route.Route, error) { restrictions := &RestrictParams{ ProbabilitySource: ss.MissionControl.GetProbability, - FeeLimit: p.payment.FeeLimit, + FeeLimit: feeLimit, OutgoingChannelID: p.payment.OutgoingChannelID, LastHop: p.payment.LastHop, CltvLimit: cltvLimit, @@ -124,7 +131,7 @@ func (p *paymentSession) RequestRoute(height uint32) (*route.Route, error) { }, restrictions, &ss.PathFindingConfig, ss.SelfNode.PubKeyBytes, p.payment.Target, - p.payment.Amount, finalHtlcExpiry, + maxAmt, finalHtlcExpiry, ) if err != nil { return nil, err @@ -136,7 +143,7 @@ func (p *paymentSession) RequestRoute(height uint32) (*route.Route, error) { route, err := newRoute( sourceVertex, path, height, finalHopParams{ - amt: p.payment.Amount, + amt: maxAmt, cltvDelta: finalCltvDelta, records: p.payment.DestCustomRecords, paymentAddr: p.payment.PaymentAddr, diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index 6d795b89..a9165804 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -50,6 +50,8 @@ func TestRequestRoute(t *testing.T) { payment := &LightningPayment{ CltvLimit: cltvLimit, FinalCLTVDelta: finalCltvDelta, + Amount: 1000, + FeeLimit: 1000, } session := &paymentSession{ @@ -63,7 +65,9 @@ func TestRequestRoute(t *testing.T) { pathFinder: findPath, } - route, err := session.RequestRoute(height) + route, err := session.RequestRoute( + payment.Amount, payment.FeeLimit, 0, height, + ) if err != nil { t.Fatal(err) } diff --git a/routing/router.go b/routing/router.go index 581671c1..18e1689b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -542,9 +542,11 @@ func (r *ChannelRouter) Start() error { // We pass in a zero timeout value, to indicate we // don't need it to timeout. It will stop immediately - // after the existing attempt has finished anyway. + // after the existing attempt has finished anyway. We + // also set a zero fee limit, as no more routes should + // be tried. _, _, err := r.sendPayment( - attempt, + attempt, payment.Info.Value, 0, payment.Info.PaymentHash, 0, paySession, ) if err != nil { @@ -1644,7 +1646,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, // Since this is the first time this payment is being made, we pass nil // for the existing attempt. return r.sendPayment( - nil, payment.PaymentHash, + nil, payment.Amount, payment.FeeLimit, payment.PaymentHash, payment.PayAttemptTimeout, paySession, ) } @@ -1667,8 +1669,9 @@ func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment) error { spewPayment(payment)) _, _, err := r.sendPayment( - nil, payment.PaymentHash, - payment.PayAttemptTimeout, paySession, + nil, payment.Amount, payment.FeeLimit, + payment.PaymentHash, payment.PayAttemptTimeout, + paySession, ) if err != nil { log.Errorf("Payment with hash %x failed: %v", @@ -1769,7 +1772,13 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, route *route.Route) ( // timeout. It is only a single attempt, so no more attempts will be // done anyway. Since this is the first time this payment is being // made, we pass nil for the existing attempt. - preimage, _, err := r.sendPayment(nil, hash, 0, paySession) + // We pass the route receiver amount as the total payment amount such + // that the payment loop will request a route for this amount. As fee + // limit we pass the route's total fees, since we already know this is + // the route that is going to be used. + preimage, _, err := r.sendPayment( + nil, amt, route.TotalFees(), hash, 0, paySession, + ) if err != nil { // SendToRoute should return a structured error. In case the // provided route fails, payment lifecycle will return a @@ -1807,7 +1816,7 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, route *route.Route) ( // the ControlTower. func (r *ChannelRouter) sendPayment( existingAttempt *channeldb.HTLCAttemptInfo, - paymentHash lntypes.Hash, + totalAmt, feeLimit lnwire.MilliSatoshi, paymentHash lntypes.Hash, timeout time.Duration, paySession PaymentSession) ([32]byte, *route.Route, error) { @@ -1822,6 +1831,8 @@ func (r *ChannelRouter) sendPayment( // can resume the payment from the current state. p := &paymentLifecycle{ router: r, + totalAmount: totalAmt, + feeLimit: feeLimit, paymentHash: paymentHash, paySession: paySession, currentHeight: currentHeight,