Skip to content

Commit 4804cbf

Browse files
committed
channeldb+routing: persist first hop custom data for route
1 parent c391430 commit 4804cbf

File tree

3 files changed

+79
-5
lines changed

3 files changed

+79
-5
lines changed

channeldb/payments.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,25 @@ func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error {
10961096
return err
10971097
}
10981098

1099+
// Merge the fixed/known records together with the custom records to
1100+
// serialize them as a single blob. We can't do this in SerializeRoute
1101+
// because we're in the middle of the byte stream there. We can only do
1102+
// TLV serialization at the end of the stream, since EOF is allowed for
1103+
// a stream if no more data is expected.
1104+
producers := []tlv.RecordProducer{
1105+
&a.Route.FirstHopAmount,
1106+
}
1107+
tlvData, err := lnwire.MergeAndEncode(
1108+
producers, nil, a.Route.FirstHopWireCustomRecords,
1109+
)
1110+
if err != nil {
1111+
return err
1112+
}
1113+
1114+
if _, err := w.Write(tlvData); err != nil {
1115+
return err
1116+
}
1117+
10991118
return nil
11001119
}
11011120

@@ -1133,6 +1152,22 @@ func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) {
11331152

11341153
a.Hash = &hash
11351154

1155+
// Read any remaining data (if any) and parse it into the known records
1156+
// and custom records.
1157+
extraData, err := io.ReadAll(r)
1158+
if err != nil {
1159+
return nil, err
1160+
}
1161+
1162+
customRecords, _, _, err := lnwire.ParseAndExtractCustomRecords(
1163+
extraData, &a.Route.FirstHopAmount,
1164+
)
1165+
if err != nil {
1166+
return nil, err
1167+
}
1168+
1169+
a.Route.FirstHopWireCustomRecords = customRecords
1170+
11361171
return a, nil
11371172
}
11381173

@@ -1398,6 +1433,8 @@ func SerializeRoute(w io.Writer, r route.Route) error {
13981433
}
13991434
}
14001435

1436+
// Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
1437+
14011438
return nil
14021439
}
14031440

@@ -1431,5 +1468,7 @@ func DeserializeRoute(r io.Reader) (route.Route, error) {
14311468
}
14321469
rt.Hops = hops
14331470

1471+
// Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
1472+
14341473
return rt, nil
14351474
}

channeldb/payments_test.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/lightningnetwork/lnd/lnwire"
1717
"github.com/lightningnetwork/lnd/record"
1818
"github.com/lightningnetwork/lnd/routing/route"
19+
"github.com/lightningnetwork/lnd/tlv"
1920
"github.com/stretchr/testify/require"
2021
)
2122

@@ -149,20 +150,34 @@ func TestSentPaymentSerialization(t *testing.T) {
149150
require.NoError(t, err, "deserialize")
150151
require.Equal(t, c, newCreationInfo)
151152

153+
b.Reset()
152154
require.NoError(t, serializeHTLCAttemptInfo(&b, s), "serialize")
153155

154156
newWireInfo, err := deserializeHTLCAttemptInfo(&b)
155157
require.NoError(t, err, "deserialize")
156-
newWireInfo.AttemptID = s.AttemptID
157158

158-
// First we verify all the records match up properly, as they aren't
159-
// able to be properly compared using reflect.DeepEqual.
160-
err = assertRouteEqual(&s.Route, &newWireInfo.Route)
161-
require.NoError(t, err)
159+
// First we verify all the records match up properly.
160+
require.Equal(t, s.Route, newWireInfo.Route)
161+
162+
// We now add the new fields and custom records to the route and
163+
// serialize it again.
164+
b.Reset()
165+
s.Route.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
166+
tlv.NewBigSizeT(lnwire.MilliSatoshi(1234)),
167+
)
168+
s.Route.FirstHopWireCustomRecords = lnwire.CustomRecords{
169+
lnwire.MinCustomRecordsTlvType + 3: []byte{4, 5, 6},
170+
}
171+
require.NoError(t, serializeHTLCAttemptInfo(&b, s), "serialize")
172+
173+
newWireInfo, err = deserializeHTLCAttemptInfo(&b)
174+
require.NoError(t, err, "deserialize")
175+
require.Equal(t, s.Route, newWireInfo.Route)
162176

163177
// Clear routes to allow DeepEqual to compare the remaining fields.
164178
newWireInfo.Route = route.Route{}
165179
s.Route = route.Route{}
180+
newWireInfo.AttemptID = s.AttemptID
166181

167182
// Call session key method to set our cached session key so we can use
168183
// DeepEqual, and assert that our key equals the original key.

routing/route/route.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,26 @@ type Route struct {
488488
// Hops contains details concerning the specific forwarding details at
489489
// each hop.
490490
Hops []*Hop
491+
492+
// FirstHopAmount is the amount that should actually be sent to the
493+
// first hop in the route. This is only different from TotalAmount above
494+
// for custom channels where the on-chain amount doesn't necessarily
495+
// reflect all the value of an outgoing payment.
496+
FirstHopAmount tlv.RecordT[
497+
tlv.TlvType0, tlv.BigSizeT[lnwire.MilliSatoshi],
498+
]
499+
500+
// FirstHopWireCustomRecords is a set of custom records that should be
501+
// included in the wire message sent to the first hop. This is only set
502+
// on custom channels and is used to include additional information
503+
// about the actual value of the payment.
504+
//
505+
// NOTE: Since these records already represent TLV records, and we
506+
// enforce them to be in the custom range (e.g. >= 65536), we don't use
507+
// another parent record type here. Instead, when serializing the Route
508+
// we merge the TLV records together with the custom records and encode
509+
// everything as a single TLV stream.
510+
FirstHopWireCustomRecords lnwire.CustomRecords
491511
}
492512

493513
// Copy returns a deep copy of the Route.

0 commit comments

Comments
 (0)