Skip to content

Commit 1d61de2

Browse files
authored
Merge pull request #8159 from carlaKC/7298-1-forwardblindedroutes
[1/3]: Preparatory work for Forwarding Blinded Routes
2 parents 41cdb0f + 2130022 commit 1d61de2

26 files changed

+1262
-160
lines changed

channeldb/channel.go

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ const (
3535
// begins to be interpreted as an absolute block height, rather than a
3636
// relative one.
3737
AbsoluteThawHeightThreshold uint32 = 500000
38+
39+
// HTLCBlindingPointTLV is the tlv type used for storing blinding
40+
// points with HTLCs.
41+
HTLCBlindingPointTLV tlv.Type = 0
3842
)
3943

4044
var (
@@ -2316,7 +2320,56 @@ type HTLC struct {
23162320
// Note that this extra data is stored inline with the OnionBlob for
23172321
// legacy reasons, see serialization/deserialization functions for
23182322
// detail.
2319-
ExtraData []byte
2323+
ExtraData lnwire.ExtraOpaqueData
2324+
2325+
// BlindingPoint is an optional blinding point included with the HTLC.
2326+
//
2327+
// Note: this field is not a part of on-disk representation of the
2328+
// HTLC. It is stored in the ExtraData field, which is used to store
2329+
// a TLV stream of additional information associated with the HTLC.
2330+
BlindingPoint lnwire.BlindingPointRecord
2331+
}
2332+
2333+
// serializeExtraData encodes a TLV stream of extra data to be stored with a
2334+
// HTLC. It uses the update_add_htlc TLV types, because this is where extra
2335+
// data is passed with a HTLC. At present blinding points are the only extra
2336+
// data that we will store, and the function is a no-op if a nil blinding
2337+
// point is provided.
2338+
//
2339+
// This function MUST be called to persist all HTLC values when they are
2340+
// serialized.
2341+
func (h *HTLC) serializeExtraData() error {
2342+
var records []tlv.RecordProducer
2343+
h.BlindingPoint.WhenSome(func(b tlv.RecordT[lnwire.BlindingPointTlvType,
2344+
*btcec.PublicKey]) {
2345+
2346+
records = append(records, &b)
2347+
})
2348+
2349+
return h.ExtraData.PackRecords(records...)
2350+
}
2351+
2352+
// deserializeExtraData extracts TLVs from the extra data persisted for the
2353+
// htlc and populates values in the struct accordingly.
2354+
//
2355+
// This function MUST be called to populate the struct properly when HTLCs
2356+
// are deserialized.
2357+
func (h *HTLC) deserializeExtraData() error {
2358+
if len(h.ExtraData) == 0 {
2359+
return nil
2360+
}
2361+
2362+
blindingPoint := h.BlindingPoint.Zero()
2363+
tlvMap, err := h.ExtraData.ExtractRecords(&blindingPoint)
2364+
if err != nil {
2365+
return err
2366+
}
2367+
2368+
if val, ok := tlvMap[h.BlindingPoint.TlvType()]; ok && val == nil {
2369+
h.BlindingPoint = tlv.SomeRecordT(blindingPoint)
2370+
}
2371+
2372+
return nil
23202373
}
23212374

23222375
// SerializeHtlcs writes out the passed set of HTLC's into the passed writer
@@ -2340,6 +2393,12 @@ func SerializeHtlcs(b io.Writer, htlcs ...HTLC) error {
23402393
}
23412394

23422395
for _, htlc := range htlcs {
2396+
// Populate TLV stream for any additional fields contained
2397+
// in the TLV.
2398+
if err := htlc.serializeExtraData(); err != nil {
2399+
return err
2400+
}
2401+
23432402
// The onion blob and hltc data are stored as a single var
23442403
// bytes blob.
23452404
onionAndExtraData := make(
@@ -2425,6 +2484,12 @@ func DeserializeHtlcs(r io.Reader) ([]HTLC, error) {
24252484
onionAndExtraData[lnwire.OnionPacketSize:],
24262485
)
24272486
}
2487+
2488+
// Finally, deserialize any TLVs contained in that extra data
2489+
// if they are present.
2490+
if err := htlcs[i].deserializeExtraData(); err != nil {
2491+
return nil, err
2492+
}
24282493
}
24292494

24302495
return htlcs, nil
@@ -2440,6 +2505,7 @@ func (h *HTLC) Copy() HTLC {
24402505
}
24412506
copy(clone.Signature[:], h.Signature)
24422507
copy(clone.RHash[:], h.RHash[:])
2508+
copy(clone.ExtraData, h.ExtraData)
24432509

24442510
return clone
24452511
}

channeldb/channel_test.go

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/lightningnetwork/lnd/lntest/channels"
2424
"github.com/lightningnetwork/lnd/lnwire"
2525
"github.com/lightningnetwork/lnd/shachain"
26+
"github.com/lightningnetwork/lnd/tlv"
2627
"github.com/stretchr/testify/require"
2728
)
2829

@@ -651,8 +652,7 @@ func TestChannelStateTransition(t *testing.T) {
651652
{
652653
LogIndex: 2,
653654
UpdateMsg: &lnwire.UpdateAddHTLC{
654-
ChanID: lnwire.ChannelID{1, 2, 3},
655-
ExtraData: make([]byte, 0),
655+
ChanID: lnwire.ChannelID{1, 2, 3},
656656
},
657657
},
658658
}
@@ -710,25 +710,22 @@ func TestChannelStateTransition(t *testing.T) {
710710
wireSig,
711711
wireSig,
712712
},
713-
ExtraData: make([]byte, 0),
714713
},
715714
LogUpdates: []LogUpdate{
716715
{
717716
LogIndex: 1,
718717
UpdateMsg: &lnwire.UpdateAddHTLC{
719-
ID: 1,
720-
Amount: lnwire.NewMSatFromSatoshis(100),
721-
Expiry: 25,
722-
ExtraData: make([]byte, 0),
718+
ID: 1,
719+
Amount: lnwire.NewMSatFromSatoshis(100),
720+
Expiry: 25,
723721
},
724722
},
725723
{
726724
LogIndex: 2,
727725
UpdateMsg: &lnwire.UpdateAddHTLC{
728-
ID: 2,
729-
Amount: lnwire.NewMSatFromSatoshis(200),
730-
Expiry: 50,
731-
ExtraData: make([]byte, 0),
726+
ID: 2,
727+
Amount: lnwire.NewMSatFromSatoshis(200),
728+
Expiry: 50,
732729
},
733730
},
734731
},
@@ -1610,9 +1607,25 @@ func TestHTLCsExtraData(t *testing.T) {
16101607
OnionBlob: lnmock.MockOnion(),
16111608
}
16121609

1610+
// Add a blinding point to a htlc.
1611+
blindingPointHTLC := HTLC{
1612+
Signature: testSig.Serialize(),
1613+
Incoming: false,
1614+
Amt: 10,
1615+
RHash: key,
1616+
RefundTimeout: 1,
1617+
OnionBlob: lnmock.MockOnion(),
1618+
BlindingPoint: tlv.SomeRecordT(
1619+
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](
1620+
pubKey,
1621+
),
1622+
),
1623+
}
1624+
16131625
testCases := []struct {
1614-
name string
1615-
htlcs []HTLC
1626+
name string
1627+
htlcs []HTLC
1628+
blindingIdx int
16161629
}{
16171630
{
16181631
// Serialize multiple HLTCs with no extra data to
@@ -1624,30 +1637,12 @@ func TestHTLCsExtraData(t *testing.T) {
16241637
},
16251638
},
16261639
{
1640+
// Some HTLCs with extra data, some without.
16271641
name: "mixed extra data",
16281642
htlcs: []HTLC{
16291643
mockHtlc,
1630-
{
1631-
Signature: testSig.Serialize(),
1632-
Incoming: false,
1633-
Amt: 10,
1634-
RHash: key,
1635-
RefundTimeout: 1,
1636-
OnionBlob: lnmock.MockOnion(),
1637-
ExtraData: []byte{1, 2, 3},
1638-
},
1644+
blindingPointHTLC,
16391645
mockHtlc,
1640-
{
1641-
Signature: testSig.Serialize(),
1642-
Incoming: false,
1643-
Amt: 10,
1644-
RHash: key,
1645-
RefundTimeout: 1,
1646-
OnionBlob: lnmock.MockOnion(),
1647-
ExtraData: bytes.Repeat(
1648-
[]byte{9}, 999,
1649-
),
1650-
},
16511646
},
16521647
},
16531648
}
@@ -1665,7 +1660,15 @@ func TestHTLCsExtraData(t *testing.T) {
16651660
r := bytes.NewReader(b.Bytes())
16661661
htlcs, err := DeserializeHtlcs(r)
16671662
require.NoError(t, err)
1668-
require.Equal(t, testCase.htlcs, htlcs)
1663+
1664+
require.EqualValues(t, len(testCase.htlcs), len(htlcs))
1665+
for i, htlc := range htlcs {
1666+
// We use the extra data field when we
1667+
// serialize, so we set to nil to be able to
1668+
// assert on equal for the test.
1669+
htlc.ExtraData = nil
1670+
require.Equal(t, testCase.htlcs[i], htlc)
1671+
}
16691672
})
16701673
}
16711674
}

cmd/lncli/cmd_payments.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,9 +1250,9 @@ func parseBlindedPaymentParameters(ctx *cli.Context) (
12501250
BaseFeeMsat: ctx.Uint64(
12511251
blindedBaseFlag.Name,
12521252
),
1253-
ProportionalFeeMsat: ctx.Uint64(
1253+
ProportionalFeeRate: uint32(ctx.Uint64(
12541254
blindedPPMFlag.Name,
1255-
),
1255+
)),
12561256
TotalCltvDelta: uint32(ctx.Uint64(
12571257
blindedCLTVFlag.Name,
12581258
)),

contractcourt/htlc_incoming_contest_resolver.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99

10+
"github.com/btcsuite/btcd/btcec/v2"
1011
"github.com/btcsuite/btcd/btcutil"
1112
"github.com/btcsuite/btcd/txscript"
1213
"github.com/lightningnetwork/lnd/channeldb"
@@ -17,6 +18,7 @@ import (
1718
"github.com/lightningnetwork/lnd/lnwallet"
1819
"github.com/lightningnetwork/lnd/lnwire"
1920
"github.com/lightningnetwork/lnd/queue"
21+
"github.com/lightningnetwork/lnd/tlv"
2022
)
2123

2224
// htlcIncomingContestResolver is a ContractResolver that's able to resolve an
@@ -520,9 +522,18 @@ func (h *htlcIncomingContestResolver) Supplement(htlc channeldb.HTLC) {
520522
func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload,
521523
[]byte, error) {
522524

525+
var blindingPoint *btcec.PublicKey
526+
h.htlc.BlindingPoint.WhenSome(
527+
func(b tlv.RecordT[lnwire.BlindingPointTlvType,
528+
*btcec.PublicKey]) {
529+
530+
blindingPoint = b.Val
531+
},
532+
)
533+
523534
onionReader := bytes.NewReader(h.htlc.OnionBlob[:])
524535
iterator, err := h.OnionProcessor.ReconstructHopIterator(
525-
onionReader, h.htlc.RHash[:],
536+
onionReader, h.htlc.RHash[:], blindingPoint,
526537
)
527538
if err != nil {
528539
return nil, nil, err

contractcourt/htlc_incoming_contest_resolver_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io/ioutil"
77
"testing"
88

9+
"github.com/btcsuite/btcd/btcec/v2"
910
sphinx "github.com/lightningnetwork/lightning-onion"
1011
"github.com/lightningnetwork/lnd/chainntnfs"
1112
"github.com/lightningnetwork/lnd/channeldb"
@@ -288,8 +289,8 @@ type mockOnionProcessor struct {
288289
offeredOnionBlob []byte
289290
}
290291

291-
func (o *mockOnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte) (
292-
hop.Iterator, error) {
292+
func (o *mockOnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte,
293+
blindingPoint *btcec.PublicKey) (hop.Iterator, error) {
293294

294295
data, err := ioutil.ReadAll(r)
295296
if err != nil {

contractcourt/interfaces.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"io"
66

7+
"github.com/btcsuite/btcd/btcec/v2"
78
"github.com/btcsuite/btcd/wire"
89
"github.com/lightningnetwork/lnd/channeldb"
910
"github.com/lightningnetwork/lnd/channeldb/models"
@@ -40,7 +41,8 @@ type Registry interface {
4041
type OnionProcessor interface {
4142
// ReconstructHopIterator attempts to decode a valid sphinx packet from
4243
// the passed io.Reader instance.
43-
ReconstructHopIterator(r io.Reader, rHash []byte) (hop.Iterator, error)
44+
ReconstructHopIterator(r io.Reader, rHash []byte,
45+
blindingKey *btcec.PublicKey) (hop.Iterator, error)
4446
}
4547

4648
// UtxoSweeper defines the sweep functions that contract court requires.

docs/release-notes/release-notes-0.18.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@
172172
* When computing a minimum fee for transaction construction, `lnd` [now takes our
173173
bitcoin peers' feefilter values into account](https://github.com/lightningnetwork/lnd/pull/8418).
174174

175+
* [Preparatory work](https://github.com/lightningnetwork/lnd/pull/8159) for
176+
forwarding of blinded routes was added.
177+
175178
## RPC Additions
176179

177180
* [Deprecated](https://github.com/lightningnetwork/lnd/pull/7175)

0 commit comments

Comments
 (0)