Skip to content

Commit aa17543

Browse files
GeorgeTsagkguggero
authored andcommitted
routing: use first hop records on path finding
1 parent 4804cbf commit aa17543

File tree

4 files changed

+128
-15
lines changed

4 files changed

+128
-15
lines changed

routing/pathfind.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ type RestrictParams struct {
466466
// BlindedPaymentPathSet is necessary to determine the hop size of the
467467
// last/exit hop.
468468
BlindedPaymentPathSet *BlindedPaymentPathSet
469+
470+
// FirstHopCustomRecords includes any records that should be included in
471+
// the update_add_htlc message towards our peer.
472+
FirstHopCustomRecords lnwire.CustomRecords
469473
}
470474

471475
// PathFindingConfig defines global parameters that control the trade-off in
@@ -524,7 +528,16 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
524528
max = bandwidth
525529
}
526530

527-
total += bandwidth
531+
var overflow bool
532+
total, overflow = overflowSafeAdd(total, bandwidth)
533+
if overflow {
534+
// If the current total and the bandwidth would
535+
// overflow the maximum value, we set the total to the
536+
// maximum value. Which is more milli-satoshis than are
537+
// in existence anyway, so the actual value is
538+
// irrelevant.
539+
total = lnwire.MilliSatoshi(math.MaxUint64)
540+
}
528541

529542
return nil
530543
}
@@ -1446,3 +1459,15 @@ func lastHopPayloadSize(r *RestrictParams, finalHtlcExpiry int32,
14461459
// The final hop does not have a short chanID set.
14471460
return finalHop.PayloadSize(0)
14481461
}
1462+
1463+
// overflowSafeAdd adds two MilliSatoshi values and returns the result. If an
1464+
// overflow could occur, zero is returned instead and the boolean is set to
1465+
// true.
1466+
func overflowSafeAdd(x, y lnwire.MilliSatoshi) (lnwire.MilliSatoshi, bool) {
1467+
if y > math.MaxUint64-x {
1468+
// Overflow would occur, return 0 and set overflow flag.
1469+
return 0, true
1470+
}
1471+
1472+
return x + y, false
1473+
}

routing/payment_lifecycle.go

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ import (
1111
sphinx "github.com/lightningnetwork/lightning-onion"
1212
"github.com/lightningnetwork/lnd/channeldb"
1313
"github.com/lightningnetwork/lnd/channeldb/models"
14+
"github.com/lightningnetwork/lnd/fn"
1415
"github.com/lightningnetwork/lnd/htlcswitch"
1516
"github.com/lightningnetwork/lnd/lntypes"
1617
"github.com/lightningnetwork/lnd/lnwire"
1718
"github.com/lightningnetwork/lnd/routing/route"
1819
"github.com/lightningnetwork/lnd/routing/shards"
20+
"github.com/lightningnetwork/lnd/tlv"
1921
)
2022

2123
// ErrPaymentLifecycleExiting is used when waiting for htlc attempt result, but
@@ -275,6 +277,13 @@ lifecycle:
275277

276278
log.Tracef("Found route: %s", spew.Sdump(rt.Hops))
277279

280+
// Allow the traffic shaper to add custom records to the
281+
// outgoing HTLC and also adjust the amount if needed.
282+
err = p.amendFirstHopData(rt)
283+
if err != nil {
284+
return exitWithErr(err)
285+
}
286+
278287
// We found a route to try, create a new HTLC attempt to try.
279288
attempt, err := p.registerAttempt(rt, ps.RemainingAmt)
280289
if err != nil {
@@ -668,8 +677,10 @@ func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route,
668677
func (p *paymentLifecycle) sendAttempt(
669678
attempt *channeldb.HTLCAttempt) (*attemptResult, error) {
670679

671-
log.Debugf("Sending HTLC attempt(id=%v, amt=%v) for payment %v",
672-
attempt.AttemptID, attempt.Route.TotalAmount, p.identifier)
680+
log.Debugf("Sending HTLC attempt(id=%v, total_amt=%v, first_hop_amt=%d"+
681+
") for payment %v", attempt.AttemptID,
682+
attempt.Route.TotalAmount, attempt.Route.FirstHopAmount.Val,
683+
p.identifier)
673684

674685
rt := attempt.Route
675686

@@ -680,10 +691,10 @@ func (p *paymentLifecycle) sendAttempt(
680691
// this packet will be used to route the payment through the network,
681692
// starting with the first-hop.
682693
htlcAdd := &lnwire.UpdateAddHTLC{
683-
Amount: rt.TotalAmount,
694+
Amount: rt.FirstHopAmount.Val.Int(),
684695
Expiry: rt.TotalTimeLock,
685696
PaymentHash: *attempt.Hash,
686-
CustomRecords: p.firstHopCustomRecords,
697+
CustomRecords: rt.FirstHopWireCustomRecords,
687698
}
688699

689700
// Generate the raw encoded sphinx packet to be included along
@@ -722,6 +733,75 @@ func (p *paymentLifecycle) sendAttempt(
722733
}, nil
723734
}
724735

736+
// amendFirstHopData is a function that calls the traffic shaper to allow it to
737+
// add custom records to the outgoing HTLC and also adjust the amount if
738+
// needed.
739+
func (p *paymentLifecycle) amendFirstHopData(rt *route.Route) error {
740+
// The first hop amount on the route is the full route amount if not
741+
// overwritten by the traffic shaper. So we set the initial value now
742+
// and potentially overwrite it later.
743+
rt.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
744+
tlv.NewBigSizeT(rt.TotalAmount),
745+
)
746+
747+
// By default, we set the first hop custom records to the initial
748+
// value requested by the RPC. The traffic shaper may overwrite this
749+
// value.
750+
rt.FirstHopWireCustomRecords = p.firstHopCustomRecords
751+
752+
// extraDataRequest is a helper struct to pass the custom records and
753+
// amount back from the traffic shaper.
754+
type extraDataRequest struct {
755+
customRecords fn.Option[lnwire.CustomRecords]
756+
757+
amount fn.Option[lnwire.MilliSatoshi]
758+
}
759+
760+
// If a hook exists that may affect our outgoing message, we call it now
761+
// and apply its side effects to the UpdateAddHTLC message.
762+
result, err := fn.MapOptionZ(
763+
p.router.cfg.TrafficShaper,
764+
func(ts TlvTrafficShaper) fn.Result[extraDataRequest] {
765+
newAmt, newRecords, err := ts.ProduceHtlcExtraData(
766+
rt.TotalAmount, p.firstHopCustomRecords,
767+
)
768+
if err != nil {
769+
return fn.Err[extraDataRequest](err)
770+
}
771+
772+
// Make sure we only received valid records.
773+
if err := newRecords.Validate(); err != nil {
774+
return fn.Err[extraDataRequest](err)
775+
}
776+
777+
log.Debugf("TLV traffic shaper returned custom "+
778+
"records %v and amount %d msat for HTLC",
779+
spew.Sdump(newRecords), newAmt)
780+
781+
return fn.Ok(extraDataRequest{
782+
customRecords: fn.Some(newRecords),
783+
amount: fn.Some(newAmt),
784+
})
785+
},
786+
).Unpack()
787+
if err != nil {
788+
return fmt.Errorf("traffic shaper failed to produce extra "+
789+
"data: %w", err)
790+
}
791+
792+
// Apply the side effects to the UpdateAddHTLC message.
793+
result.customRecords.WhenSome(func(records lnwire.CustomRecords) {
794+
rt.FirstHopWireCustomRecords = records
795+
})
796+
result.amount.WhenSome(func(amount lnwire.MilliSatoshi) {
797+
rt.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
798+
tlv.NewBigSizeT(amount),
799+
)
800+
})
801+
802+
return nil
803+
}
804+
725805
// failAttemptAndPayment fails both the payment and its attempt via the
726806
// router's control tower, which marks the payment as failed in db.
727807
func (p *paymentLifecycle) failPaymentAndAttempt(

routing/payment_session.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,17 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
268268
// to our destination, respecting the recommendations from
269269
// MissionControl.
270270
restrictions := &RestrictParams{
271-
ProbabilitySource: p.missionControl.GetProbability,
272-
FeeLimit: feeLimit,
273-
OutgoingChannelIDs: p.payment.OutgoingChannelIDs,
274-
LastHop: p.payment.LastHop,
275-
CltvLimit: cltvLimit,
276-
DestCustomRecords: p.payment.DestCustomRecords,
277-
DestFeatures: p.payment.DestFeatures,
278-
PaymentAddr: p.payment.PaymentAddr,
279-
Amp: p.payment.amp,
280-
Metadata: p.payment.Metadata,
271+
ProbabilitySource: p.missionControl.GetProbability,
272+
FeeLimit: feeLimit,
273+
OutgoingChannelIDs: p.payment.OutgoingChannelIDs,
274+
LastHop: p.payment.LastHop,
275+
CltvLimit: cltvLimit,
276+
DestCustomRecords: p.payment.DestCustomRecords,
277+
DestFeatures: p.payment.DestFeatures,
278+
PaymentAddr: p.payment.PaymentAddr,
279+
Amp: p.payment.amp,
280+
Metadata: p.payment.Metadata,
281+
FirstHopCustomRecords: firstHopCustomRecords,
281282
}
282283

283284
finalHtlcExpiry := int32(height) + int32(finalCltvDelta)

routing/router.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,13 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
11881188
firstHopCustomRecords,
11891189
)
11901190

1191+
// Allow the traffic shaper to add custom records to the outgoing HTLC
1192+
// and also adjust the amount if needed.
1193+
err = p.amendFirstHopData(rt)
1194+
if err != nil {
1195+
return nil, err
1196+
}
1197+
11911198
// We found a route to try, create a new HTLC attempt to try.
11921199
//
11931200
// NOTE: we use zero `remainingAmt` here to simulate the same effect of

0 commit comments

Comments
 (0)