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
65 changes: 65 additions & 0 deletions htlcswitch/interceptable_switch.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package htlcswitch

import (
"bytes"
"crypto/sha256"
"fmt"
"sync"

"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/tlv"
)

var (
Expand Down Expand Up @@ -105,6 +109,10 @@ const (

// FwdActionFail fails the intercepted packet back to the sender.
FwdActionFail

// FwdActionResumeModified forwards the intercepted packet to the switch
// with modifications.
FwdActionResumeModified
)

// FwdResolution defines the action to be taken on an intercepted packet.
Expand All @@ -119,6 +127,14 @@ type FwdResolution struct {
// FwdActionSettle.
Preimage lntypes.Preimage

// OutgoingAmountMsat is the amount that is to be used for forwarding if
// Action is FwdActionResumeModified.
OutgoingAmountMsat fn.Option[lnwire.MilliSatoshi]

// CustomRecords is the custom records that are to be used for
// forwarding if Action is FwdActionResumeModified.
CustomRecords fn.Option[record.CustomSet]

// FailureMessage is the encrypted failure message that is to be passed
// back to the sender if action is FwdActionFail.
FailureMessage []byte
Expand Down Expand Up @@ -363,6 +379,8 @@ func (s *InterceptableSwitch) setInterceptor(interceptor ForwardInterceptor) {
})
}

// resolve processes a HTLC given the resolution type specified by the
// intercepting client.
func (s *InterceptableSwitch) resolve(res *FwdResolution) error {
intercepted, err := s.heldHtlcSet.pop(res.Key)
if err != nil {
Expand All @@ -373,6 +391,11 @@ func (s *InterceptableSwitch) resolve(res *FwdResolution) error {
case FwdActionResume:
return intercepted.Resume()

case FwdActionResumeModified:
return intercepted.ResumeModified(
res.OutgoingAmountMsat, res.CustomRecords,
)

case FwdActionSettle:
return intercepted.Settle(res.Preimage)

Expand Down Expand Up @@ -615,6 +638,48 @@ func (f *interceptedForward) Resume() error {
return f.htlcSwitch.ForwardPackets(nil, f.packet)
}

// ResumeModified resumes the default behavior with field modifications.
func (f *interceptedForward) ResumeModified(
outgoingAmountMsat fn.Option[lnwire.MilliSatoshi],
customRecords fn.Option[record.CustomSet]) error {

// Modify the wire message contained in the packet.
htlc, ok := f.packet.htlc.(*lnwire.UpdateAddHTLC)
if ok {
outgoingAmountMsat.WhenSome(func(amount lnwire.MilliSatoshi) {
htlc.Amount = amount
})

var customRecordsErr error
customRecords.WhenSome(func(records record.CustomSet) {
if len(records) == 0 {
return
}

// Encode the custom records to bytes.
var buf bytes.Buffer
if err := records.Encode(&buf); err != nil {
customRecordsErr = err
return
}
recordsBytes := buf.Bytes()

// Create a new custom records blob TLV record.
//nolint:lll
tlvRecord := tlv.NewPrimitiveRecord[lnwire.CustomRecordsBlobTlvType](recordsBytes)
htlc.CustomRecordsBlob = tlv.SomeRecordT(tlvRecord)
})
if customRecordsErr != nil {
return fmt.Errorf("failed to encode custom records: %w",
customRecordsErr)
}
}

// Forward to the switch. A link quit channel isn't needed, because we
// are on a different thread now.
return f.htlcSwitch.ForwardPackets(nil, f.packet)
}

// Fail notifies the intention to Fail an existing hold forward with an
// encrypted failure reason.
func (f *interceptedForward) Fail(reason []byte) error {
Expand Down
8 changes: 7 additions & 1 deletion htlcswitch/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
Expand Down Expand Up @@ -323,7 +324,7 @@ type InterceptableHtlcForwarder interface {
type ForwardInterceptor func(InterceptedPacket) error

// InterceptedPacket contains the relevant information for the interceptor about
// an htlc.
// a HTLC.
type InterceptedPacket struct {
// IncomingCircuit contains the incoming channel and htlc id of the
// packet.
Expand Down Expand Up @@ -375,6 +376,11 @@ type InterceptedForward interface {
// this htlc which usually means forward it.
Resume() error

// ResumeModified notifies the intention to resume an existing hold
// forward with modified fields.
ResumeModified(outgoingAmountMsat fn.Option[lnwire.MilliSatoshi],
customRecords fn.Option[record.CustomSet]) error

// Settle notifies the intention to settle an existing hold
// forward with a given preimage.
Settle(lntypes.Preimage) error
Expand Down
10 changes: 10 additions & 0 deletions intercepted_forward.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package lnd
import (
"errors"

"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
)

var (
Expand Down Expand Up @@ -51,6 +53,14 @@ func (f *interceptedForward) Resume() error {
return ErrCannotResume
}

// ResumeModified notifies the intention to resume an existing hold forward with
// a modified htlc.
func (f *interceptedForward) ResumeModified(_ fn.Option[lnwire.MilliSatoshi],
_ fn.Option[record.CustomSet]) error {

return ErrCannotResume
}

// Fail notifies the intention to fail an existing hold forward with an
// encrypted failure reason.
func (f *interceptedForward) Fail(_ []byte) error {
Expand Down
8 changes: 8 additions & 0 deletions itest/list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,14 @@ var allTestCases = []*lntest.TestCase{
Name: "forward interceptor",
TestFunc: testForwardInterceptorBasic,
},
{
Name: "forward interceptor modified htlc",
TestFunc: testForwardInterceptorModifiedHtlc,
},
{
Name: "forward interceptor first hop records",
TestFunc: testForwardInterceptorFirstHopRecords,
},
{
Name: "zero conf channel open",
TestFunc: testZeroConfChannelOpen,
Expand Down
Loading
Loading