Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LightningPayment first hop custom records #8656

Closed
19 changes: 18 additions & 1 deletion routing/payment_lifecycle.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package routing

import (
"bytes"
"errors"
"fmt"
"time"
Expand All @@ -13,8 +14,10 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/routing/shards"
"github.com/lightningnetwork/lnd/tlv"
)

// ErrPaymentLifecycleExiting is used when waiting for htlc attempt result, but
Expand All @@ -31,6 +34,7 @@ type paymentLifecycle struct {
shardTracker shards.ShardTracker
timeoutChan <-chan time.Time
currentHeight int32
firstHopTLVs record.CustomSet

// quit is closed to signal the sub goroutines of the payment lifecycle
// to stop.
Expand All @@ -53,7 +57,7 @@ type paymentLifecycle struct {
func newPaymentLifecycle(r *ChannelRouter, feeLimit lnwire.MilliSatoshi,
identifier lntypes.Hash, paySession PaymentSession,
shardTracker shards.ShardTracker, timeout time.Duration,
currentHeight int32) *paymentLifecycle {
currentHeight int32, firstHopTLVs record.CustomSet) *paymentLifecycle {

p := &paymentLifecycle{
router: r,
Expand All @@ -64,6 +68,7 @@ func newPaymentLifecycle(r *ChannelRouter, feeLimit lnwire.MilliSatoshi,
currentHeight: currentHeight,
quit: make(chan struct{}),
resultCollected: make(chan error, 1),
firstHopTLVs: firstHopTLVs,
}

// Mount the result collector.
Expand Down Expand Up @@ -673,6 +678,18 @@ func (p *paymentLifecycle) sendAttempt(
PaymentHash: *attempt.Hash,
}

buffer := new(bytes.Buffer)
if err := p.firstHopTLVs.Encode(buffer); err != nil {
return p.failAttempt(attempt.AttemptID, err)
}

recordsBytes := buffer.Bytes()
tlvRecord := tlv.NewPrimitiveRecord[lnwire.CustomRecordsBlobTlvType](
recordsBytes,
)

htlcAdd.CustomRecordsBlob = tlv.SomeRecordT(tlvRecord)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I chatted with @ffranr about this offline, but instead we only want to be setting ExtraData. All the other logic (further down in the state machine), will only look inside of ExtraData, as it isn't expecting that extra layer of wrapping.

We do want the upfront validation here tho to ensure that w/e we're passed in is actually a canonical TLV stream.


// Generate the raw encoded sphinx packet to be included along
// with the htlcAdd message that we send directly to the
// switch.
Expand Down
17 changes: 12 additions & 5 deletions routing/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ func (r *ChannelRouter) Start() error {
// be tried.
_, _, err := r.sendPayment(
0, payment.Info.PaymentIdentifier, 0,
paySession, shardTracker,
paySession, shardTracker, nil,
)
if err != nil {
log.Errorf("Resuming payment %v failed: %v.",
Expand Down Expand Up @@ -2306,6 +2306,11 @@ type LightningPayment struct {
// fail.
DestCustomRecords record.CustomSet

// FirstHopCustomRecords are the TLV records that are to be sent to the
// first hop of this payment. These records will be transmitted via the
// wire message and therefore do not affect the onion payload size.
FirstHopCustomRecords record.CustomSet

// MaxParts is the maximum number of partial payments that may be used
// to complete the full amount.
MaxParts uint32
Expand Down Expand Up @@ -2391,6 +2396,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte,
return r.sendPayment(
payment.FeeLimit, payment.Identifier(),
payment.PayAttemptTimeout, paySession, shardTracker,
payment.FirstHopCustomRecords,
)
}

Expand All @@ -2411,6 +2417,7 @@ func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment,
_, _, err := r.sendPayment(
payment.FeeLimit, payment.Identifier(),
payment.PayAttemptTimeout, ps, st,
payment.FirstHopCustomRecords,
)
if err != nil {
log.Errorf("Payment %x failed: %v",
Expand Down Expand Up @@ -2585,7 +2592,7 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// - no payment timeout.
// - no current block height.
p := newPaymentLifecycle(
r, 0, paymentIdentifier, nil, shardTracker, 0, 0,
r, 0, paymentIdentifier, nil, shardTracker, 0, 0, nil,
)

// We found a route to try, create a new HTLC attempt to try.
Expand Down Expand Up @@ -2681,8 +2688,8 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
// the ControlTower.
func (r *ChannelRouter) sendPayment(feeLimit lnwire.MilliSatoshi,
identifier lntypes.Hash, timeout time.Duration,
paySession PaymentSession,
shardTracker shards.ShardTracker) ([32]byte, *route.Route, error) {
paySession PaymentSession, shardTracker shards.ShardTracker,
firstHopTLVs record.CustomSet) ([32]byte, *route.Route, error) {

// We'll also fetch the current block height so we can properly
// calculate the required HTLC time locks within the route.
Expand All @@ -2695,7 +2702,7 @@ func (r *ChannelRouter) sendPayment(feeLimit lnwire.MilliSatoshi,
// can resume the payment from the current state.
p := newPaymentLifecycle(
r, feeLimit, identifier, paySession,
shardTracker, timeout, currentHeight,
shardTracker, timeout, currentHeight, firstHopTLVs,
)

return p.resumePayment()
Expand Down