mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-22 22:25:24 +01:00
multi: add blinded path TLVs to onion payload / hops
This commit adds the encrypted_data, blinding_point and total_amt_msat tlvs to the known set of even tlvs for the onion payload. These TLVs are added in two places (the onion payload and hop struct) because lnd uses the same set of TLV types for both structs (and they inherently represent the same thing). Note: in some places, unit tests intentionally mimic the style of older tests, so as to be more consistently readable.
This commit is contained in:
parent
539a275faa
commit
fee0e05708
7 changed files with 392 additions and 23 deletions
|
@ -9,6 +9,7 @@ import (
|
|||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/kvdb"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
|
@ -1143,10 +1144,30 @@ func serializeHop(w io.Writer, h *route.Hop) error {
|
|||
records = append(records, h.MPP.Record())
|
||||
}
|
||||
|
||||
// Add blinding point and encrypted data if present.
|
||||
if h.EncryptedData != nil {
|
||||
records = append(records, record.NewEncryptedDataRecord(
|
||||
&h.EncryptedData,
|
||||
))
|
||||
}
|
||||
|
||||
if h.BlindingPoint != nil {
|
||||
records = append(records, record.NewBlindingPointRecord(
|
||||
&h.BlindingPoint,
|
||||
))
|
||||
}
|
||||
|
||||
if h.Metadata != nil {
|
||||
records = append(records, record.NewMetadataRecord(&h.Metadata))
|
||||
}
|
||||
|
||||
if h.TotalAmtMsat != 0 {
|
||||
totalMsatInt := uint64(h.TotalAmtMsat)
|
||||
records = append(
|
||||
records, record.NewTotalAmtMsatBlinded(&totalMsatInt),
|
||||
)
|
||||
}
|
||||
|
||||
// Final sanity check to absolutely rule out custom records that are not
|
||||
// custom and write into the standard range.
|
||||
if err := h.CustomRecords.Validate(); err != nil {
|
||||
|
@ -1261,6 +1282,27 @@ func deserializeHop(r io.Reader) (*route.Hop, error) {
|
|||
h.MPP = mpp
|
||||
}
|
||||
|
||||
// If encrypted data or blinding key are present, remove them from
|
||||
// the TLV map and parse into proper types.
|
||||
encryptedDataType := uint64(record.EncryptedDataOnionType)
|
||||
if data, ok := tlvMap[encryptedDataType]; ok {
|
||||
delete(tlvMap, encryptedDataType)
|
||||
h.EncryptedData = data
|
||||
}
|
||||
|
||||
blindingType := uint64(record.BlindingPointOnionType)
|
||||
if blindingPoint, ok := tlvMap[blindingType]; ok {
|
||||
delete(tlvMap, blindingType)
|
||||
|
||||
h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid blinding point: %w",
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// If the metatdata type is present, remove it from the tlv map and
|
||||
// populate directly on the hop.
|
||||
metadataType := uint64(record.MetadataOnionType)
|
||||
if metadata, ok := tlvMap[metadataType]; ok {
|
||||
delete(tlvMap, metadataType)
|
||||
|
@ -1268,6 +1310,26 @@ func deserializeHop(r io.Reader) (*route.Hop, error) {
|
|||
h.Metadata = metadata
|
||||
}
|
||||
|
||||
totalAmtMsatType := uint64(record.TotalAmtMsatBlindedType)
|
||||
if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
|
||||
delete(tlvMap, totalAmtMsatType)
|
||||
|
||||
var (
|
||||
totalAmtMsatInt uint64
|
||||
buf [8]byte
|
||||
)
|
||||
if err := tlv.DTUint64(
|
||||
bytes.NewReader(totalAmtMsat),
|
||||
&totalAmtMsatInt,
|
||||
&buf,
|
||||
uint64(len(totalAmtMsat)),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
|
||||
}
|
||||
|
||||
h.CustomRecords = tlvMap
|
||||
|
||||
return h, nil
|
||||
|
|
|
@ -21,9 +21,10 @@ import (
|
|||
var (
|
||||
priv, _ = btcec.NewPrivateKey()
|
||||
pub = priv.PubKey()
|
||||
vertex = route.NewVertex(pub)
|
||||
|
||||
testHop1 = &route.Hop{
|
||||
PubKeyBytes: route.NewVertex(pub),
|
||||
PubKeyBytes: vertex,
|
||||
ChannelID: 12345,
|
||||
OutgoingTimeLock: 111,
|
||||
AmtToForward: 555,
|
||||
|
@ -36,7 +37,7 @@ var (
|
|||
}
|
||||
|
||||
testHop2 = &route.Hop{
|
||||
PubKeyBytes: route.NewVertex(pub),
|
||||
PubKeyBytes: vertex,
|
||||
ChannelID: 12345,
|
||||
OutgoingTimeLock: 111,
|
||||
AmtToForward: 555,
|
||||
|
@ -46,12 +47,39 @@ var (
|
|||
testRoute = route.Route{
|
||||
TotalTimeLock: 123,
|
||||
TotalAmount: 1234567,
|
||||
SourcePubKey: route.NewVertex(pub),
|
||||
SourcePubKey: vertex,
|
||||
Hops: []*route.Hop{
|
||||
testHop2,
|
||||
testHop1,
|
||||
},
|
||||
}
|
||||
|
||||
testBlindedRoute = route.Route{
|
||||
TotalTimeLock: 150,
|
||||
TotalAmount: 1000,
|
||||
SourcePubKey: vertex,
|
||||
Hops: []*route.Hop{
|
||||
{
|
||||
PubKeyBytes: vertex,
|
||||
ChannelID: 9876,
|
||||
OutgoingTimeLock: 120,
|
||||
AmtToForward: 900,
|
||||
EncryptedData: []byte{1, 3, 3},
|
||||
BlindingPoint: pub,
|
||||
},
|
||||
{
|
||||
PubKeyBytes: vertex,
|
||||
EncryptedData: []byte{3, 2, 1},
|
||||
},
|
||||
{
|
||||
PubKeyBytes: vertex,
|
||||
Metadata: []byte{4, 5, 6},
|
||||
AmtToForward: 500,
|
||||
OutgoingTimeLock: 100,
|
||||
TotalAmtMsat: 500,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) {
|
||||
|
@ -140,27 +168,24 @@ func assertRouteEqual(a, b *route.Route) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// TestRouteSerialization tests serialization of a regular and blinded route.
|
||||
func TestRouteSerialization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testSerializeRoute(t, testRoute)
|
||||
testSerializeRoute(t, testBlindedRoute)
|
||||
}
|
||||
|
||||
func testSerializeRoute(t *testing.T, route route.Route) {
|
||||
var b bytes.Buffer
|
||||
if err := SerializeRoute(&b, testRoute); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := SerializeRoute(&b, route)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := bytes.NewReader(b.Bytes())
|
||||
route2, err := DeserializeRoute(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// First we verify all the records match up porperly, as they aren't
|
||||
// able to be properly compared using reflect.DeepEqual.
|
||||
err = assertRouteEqual(&testRoute, &route2)
|
||||
if err != nil {
|
||||
t.Fatalf("routes not equal: \n%v vs \n%v",
|
||||
spew.Sdump(testRoute), spew.Sdump(route2))
|
||||
}
|
||||
reflect.DeepEqual(route, route2)
|
||||
}
|
||||
|
||||
// deletePayment removes a payment with paymentHash from the payments database.
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/record"
|
||||
|
@ -94,9 +95,20 @@ type Payload struct {
|
|||
// were included in the payload.
|
||||
customRecords record.CustomSet
|
||||
|
||||
// encryptedData is a blob of data encrypted by the receiver for use
|
||||
// in blinded routes.
|
||||
encryptedData []byte
|
||||
|
||||
// blindingPoint is an ephemeral pubkey for use in blinded routes.
|
||||
blindingPoint *btcec.PublicKey
|
||||
|
||||
// metadata is additional data that is sent along with the payment to
|
||||
// the payee.
|
||||
metadata []byte
|
||||
|
||||
// totalAmtMsat holds the info provided in total_amount_msat when
|
||||
// parsed from a TLV onion payload.
|
||||
totalAmtMsat lnwire.MilliSatoshi
|
||||
}
|
||||
|
||||
// NewLegacyPayload builds a Payload from the amount, cltv, and next hop
|
||||
|
@ -118,12 +130,15 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload {
|
|||
// should correspond to the bytes encapsulated in a TLV onion payload.
|
||||
func NewPayloadFromReader(r io.Reader) (*Payload, error) {
|
||||
var (
|
||||
cid uint64
|
||||
amt uint64
|
||||
cltv uint32
|
||||
mpp = &record.MPP{}
|
||||
amp = &record.AMP{}
|
||||
metadata []byte
|
||||
cid uint64
|
||||
amt uint64
|
||||
totalAmtMsat uint64
|
||||
cltv uint32
|
||||
mpp = &record.MPP{}
|
||||
amp = &record.AMP{}
|
||||
encryptedData []byte
|
||||
blindingPoint *btcec.PublicKey
|
||||
metadata []byte
|
||||
)
|
||||
|
||||
tlvStream, err := tlv.NewStream(
|
||||
|
@ -131,8 +146,11 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
|
|||
record.NewLockTimeRecord(&cltv),
|
||||
record.NewNextHopIDRecord(&cid),
|
||||
mpp.Record(),
|
||||
record.NewEncryptedDataRecord(&encryptedData),
|
||||
record.NewBlindingPointRecord(&blindingPoint),
|
||||
amp.Record(),
|
||||
record.NewMetadataRecord(&metadata),
|
||||
record.NewTotalAmtMsatBlinded(&totalAmtMsat),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -175,6 +193,12 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
|
|||
amp = nil
|
||||
}
|
||||
|
||||
// If no encrypted data was parsed, set the field on our resulting
|
||||
// payload to nil.
|
||||
if _, ok := parsedTypes[record.EncryptedDataOnionType]; !ok {
|
||||
encryptedData = nil
|
||||
}
|
||||
|
||||
// If no metadata field was parsed, set the metadata field on the
|
||||
// resulting payload to nil.
|
||||
if _, ok := parsedTypes[record.MetadataOnionType]; !ok {
|
||||
|
@ -193,7 +217,10 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
|
|||
MPP: mpp,
|
||||
AMP: amp,
|
||||
metadata: metadata,
|
||||
encryptedData: encryptedData,
|
||||
blindingPoint: blindingPoint,
|
||||
customRecords: customRecords,
|
||||
totalAmtMsat: lnwire.MilliSatoshi(totalAmtMsat),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -297,12 +324,29 @@ func (h *Payload) CustomRecords() record.CustomSet {
|
|||
return h.customRecords
|
||||
}
|
||||
|
||||
// EncryptedData returns the route blinding encrypted data parsed from the
|
||||
// onion payload.
|
||||
func (h *Payload) EncryptedData() []byte {
|
||||
return h.encryptedData
|
||||
}
|
||||
|
||||
// BlindingPoint returns the route blinding point parsed from the onion payload.
|
||||
func (h *Payload) BlindingPoint() *btcec.PublicKey {
|
||||
return h.blindingPoint
|
||||
}
|
||||
|
||||
// Metadata returns the additional data that is sent along with the
|
||||
// payment to the payee.
|
||||
func (h *Payload) Metadata() []byte {
|
||||
return h.metadata
|
||||
}
|
||||
|
||||
// TotalAmtMsat returns the total amount sent to the final hop, as set by the
|
||||
// payee.
|
||||
func (h *Payload) TotalAmtMsat() lnwire.MilliSatoshi {
|
||||
return h.totalAmtMsat
|
||||
}
|
||||
|
||||
// getMinRequiredViolation checks for unrecognized required (even) fields in the
|
||||
// standard range and returns the lowest required type. Always returning the
|
||||
// lowest required type allows a failure message to be deterministic.
|
||||
|
|
|
@ -2,15 +2,23 @@ package hop_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/record"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
//nolint:lll
|
||||
testPrivKeyBytes, _ = hex.DecodeString("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734")
|
||||
_, testPubKey = btcec.PrivKeyFromBytes(testPrivKeyBytes)
|
||||
)
|
||||
|
||||
const testUnknownRequiredType = 0x80
|
||||
|
||||
type decodePayloadTest struct {
|
||||
|
@ -20,7 +28,10 @@ type decodePayloadTest struct {
|
|||
expCustomRecords map[uint64][]byte
|
||||
shouldHaveMPP bool
|
||||
shouldHaveAMP bool
|
||||
shouldHaveEncData bool
|
||||
shouldHaveBlinding bool
|
||||
shouldHaveMetadata bool
|
||||
shouldHaveTotalAmt bool
|
||||
}
|
||||
|
||||
var decodePayloadTests = []decodePayloadTest{
|
||||
|
@ -217,6 +228,33 @@ var decodePayloadTests = []decodePayloadTest{
|
|||
FinalHop: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "intermediate hop with encrypted data",
|
||||
payload: []byte{
|
||||
// amount
|
||||
0x02, 0x00,
|
||||
// cltv
|
||||
0x04, 0x00,
|
||||
// encrypted data
|
||||
0x0a, 0x03, 0x03, 0x02, 0x01,
|
||||
},
|
||||
shouldHaveEncData: true,
|
||||
},
|
||||
{
|
||||
name: "intermediate hop with blinding point",
|
||||
payload: append([]byte{
|
||||
// amount
|
||||
0x02, 0x00,
|
||||
// cltv
|
||||
0x04, 0x00,
|
||||
// blinding point (type / length)
|
||||
0x0c, 0x21,
|
||||
},
|
||||
// blinding point (value)
|
||||
testPubKey.SerializeCompressed()...,
|
||||
),
|
||||
shouldHaveBlinding: true,
|
||||
},
|
||||
{
|
||||
name: "final hop with mpp",
|
||||
payload: []byte{
|
||||
|
@ -271,6 +309,18 @@ var decodePayloadTests = []decodePayloadTest{
|
|||
},
|
||||
shouldHaveMetadata: true,
|
||||
},
|
||||
{
|
||||
name: "final hop with total amount",
|
||||
payload: []byte{
|
||||
// amount
|
||||
0x02, 0x00,
|
||||
// cltv
|
||||
0x04, 0x00,
|
||||
// total amount
|
||||
0x12, 0x01, 0x01,
|
||||
},
|
||||
shouldHaveTotalAmt: true,
|
||||
},
|
||||
}
|
||||
|
||||
// TestDecodeHopPayloadRecordValidation asserts that parsing the payloads in the
|
||||
|
@ -306,6 +356,7 @@ func testDecodeHopPayloadValidation(t *testing.T, test decodePayloadTest) {
|
|||
0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
|
||||
0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
|
||||
}
|
||||
testEncData = []byte{3, 2, 1}
|
||||
testMetadata = []byte{1, 2, 3}
|
||||
testChildIndex = uint32(9)
|
||||
)
|
||||
|
@ -354,6 +405,29 @@ func testDecodeHopPayloadValidation(t *testing.T, test decodePayloadTest) {
|
|||
t.Fatalf("unexpected metadata")
|
||||
}
|
||||
|
||||
if test.shouldHaveEncData {
|
||||
require.NotNil(t, p.EncryptedData(),
|
||||
"payment should have encrypted data")
|
||||
|
||||
require.Equal(t, testEncData, p.EncryptedData())
|
||||
} else {
|
||||
require.Nil(t, p.EncryptedData())
|
||||
}
|
||||
|
||||
if test.shouldHaveBlinding {
|
||||
require.NotNil(t, p.BlindingPoint())
|
||||
|
||||
require.Equal(t, testPubKey, p.BlindingPoint())
|
||||
} else {
|
||||
require.Nil(t, p.BlindingPoint())
|
||||
}
|
||||
|
||||
if test.shouldHaveTotalAmt {
|
||||
require.NotZero(t, p.TotalAmtMsat())
|
||||
} else {
|
||||
require.Zero(t, p.TotalAmtMsat())
|
||||
}
|
||||
|
||||
// Convert expected nil map to empty map, because we always expect an
|
||||
// initiated map from the payload.
|
||||
expCustomRecords := make(record.CustomSet)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package record
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
)
|
||||
|
||||
|
@ -17,9 +18,21 @@ const (
|
|||
// of the next hop.
|
||||
NextHopOnionType tlv.Type = 6
|
||||
|
||||
// EncryptedDataOnionType is the type used to include encrypted data
|
||||
// provided by the receiver in the onion for use in blinded paths.
|
||||
EncryptedDataOnionType tlv.Type = 10
|
||||
|
||||
// BlindingPointOnionType is the type used to include receiver provided
|
||||
// ephemeral keys in the onion that are used in blinded paths.
|
||||
BlindingPointOnionType tlv.Type = 12
|
||||
|
||||
// MetadataOnionType is the type used in the onion for the payment
|
||||
// metadata.
|
||||
MetadataOnionType tlv.Type = 16
|
||||
|
||||
// TotalAmtMsatBlindedType is the type used in the onion for the total
|
||||
// amount field that is included in the final hop for blinded payments.
|
||||
TotalAmtMsatBlindedType tlv.Type = 18
|
||||
)
|
||||
|
||||
// NewAmtToFwdRecord creates a tlv.Record that encodes the amount_to_forward
|
||||
|
@ -50,6 +63,18 @@ func NewNextHopIDRecord(cid *uint64) tlv.Record {
|
|||
return tlv.MakePrimitiveRecord(NextHopOnionType, cid)
|
||||
}
|
||||
|
||||
// NewEncryptedDataRecord creates a tlv.Record that encodes the encrypted_data
|
||||
// (type 10) record for an onion payload.
|
||||
func NewEncryptedDataRecord(data *[]byte) tlv.Record {
|
||||
return tlv.MakePrimitiveRecord(EncryptedDataOnionType, data)
|
||||
}
|
||||
|
||||
// NewBlindingPointRecord creates a tlv.Record that encodes the blinding_point
|
||||
// (type 12) record for an onion payload.
|
||||
func NewBlindingPointRecord(point **btcec.PublicKey) tlv.Record {
|
||||
return tlv.MakePrimitiveRecord(BlindingPointOnionType, point)
|
||||
}
|
||||
|
||||
// NewMetadataRecord creates a tlv.Record that encodes the metadata (type 10)
|
||||
// for an onion payload.
|
||||
func NewMetadataRecord(metadata *[]byte) tlv.Record {
|
||||
|
@ -61,3 +86,14 @@ func NewMetadataRecord(metadata *[]byte) tlv.Record {
|
|||
tlv.EVarBytes, tlv.DVarBytes,
|
||||
)
|
||||
}
|
||||
|
||||
// NewTotalAmtMsatBlinded creates a tlv.Record that encodes the
|
||||
// total_amount_msat for the final an onion payload within a blinded route.
|
||||
func NewTotalAmtMsatBlinded(amt *uint64) tlv.Record {
|
||||
return tlv.MakeDynamicRecord(
|
||||
TotalAmtMsatBlindedType, amt, func() uint64 {
|
||||
return tlv.SizeTUint64(*amt)
|
||||
},
|
||||
tlv.ETUint64, tlv.DTUint64,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -131,6 +131,20 @@ type Hop struct {
|
|||
// Metadata is additional data that is sent along with the payment to
|
||||
// the payee.
|
||||
Metadata []byte
|
||||
|
||||
// EncryptedData is an encrypted data blob includes for hops that are
|
||||
// part of a blinded route.
|
||||
EncryptedData []byte
|
||||
|
||||
// BlindingPoint is an ephemeral public key used by introduction nodes
|
||||
// in blinded routes to unblind their portion of the route and pass on
|
||||
// the next ephemeral key to the next blinded node to do the same.
|
||||
BlindingPoint *btcec.PublicKey
|
||||
|
||||
// TotalAmtMsat is the total amount for a blinded payment, potentially
|
||||
// spread over more than one HTLC. This field should only be set for
|
||||
// the final hop in a blinded path.
|
||||
TotalAmtMsat lnwire.MilliSatoshi
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of the Hop.
|
||||
|
@ -147,6 +161,11 @@ func (h *Hop) Copy() *Hop {
|
|||
c.AMP = &a
|
||||
}
|
||||
|
||||
if h.BlindingPoint != nil {
|
||||
b := *h.BlindingPoint
|
||||
c.BlindingPoint = &b
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
|
@ -197,6 +216,19 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Add encrypted data and blinding point if present.
|
||||
if h.EncryptedData != nil {
|
||||
records = append(records, record.NewEncryptedDataRecord(
|
||||
&h.EncryptedData,
|
||||
))
|
||||
}
|
||||
|
||||
if h.BlindingPoint != nil {
|
||||
records = append(records, record.NewBlindingPointRecord(
|
||||
&h.BlindingPoint,
|
||||
))
|
||||
}
|
||||
|
||||
// If an AMP record is destined for this hop, ensure that we only ever
|
||||
// attach it if we also have an MPP record. We can infer that this is
|
||||
// already a final hop if MPP is non-nil otherwise we would have exited
|
||||
|
@ -216,6 +248,13 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
|
|||
)
|
||||
}
|
||||
|
||||
if h.TotalAmtMsat != 0 {
|
||||
totalAmtInt := uint64(h.TotalAmtMsat)
|
||||
records = append(records,
|
||||
record.NewTotalAmtMsatBlinded(&totalAmtInt),
|
||||
)
|
||||
}
|
||||
|
||||
// Append any custom types destined for this hop.
|
||||
tlvRecords := tlv.MapToRecords(h.CustomRecords)
|
||||
records = append(records, tlvRecords...)
|
||||
|
@ -270,11 +309,33 @@ func (h *Hop) PayloadSize(nextChanID uint64) uint64 {
|
|||
addRecord(record.AMPOnionType, h.AMP.PayloadSize())
|
||||
}
|
||||
|
||||
// Add encrypted data and blinding point if present.
|
||||
if h.EncryptedData != nil {
|
||||
addRecord(
|
||||
record.EncryptedDataOnionType,
|
||||
uint64(len(h.EncryptedData)),
|
||||
)
|
||||
}
|
||||
|
||||
if h.BlindingPoint != nil {
|
||||
addRecord(
|
||||
record.BlindingPointOnionType,
|
||||
btcec.PubKeyBytesLenCompressed,
|
||||
)
|
||||
}
|
||||
|
||||
// Add metadata if present.
|
||||
if h.Metadata != nil {
|
||||
addRecord(record.MetadataOnionType, uint64(len(h.Metadata)))
|
||||
}
|
||||
|
||||
if h.TotalAmtMsat != 0 {
|
||||
addRecord(
|
||||
record.TotalAmtMsatBlindedType,
|
||||
tlv.SizeTUint64(uint64(h.AmtToForward)),
|
||||
)
|
||||
}
|
||||
|
||||
// Add custom records.
|
||||
for k, v := range h.CustomRecords {
|
||||
addRecord(tlv.Type(k), uint64(len(v)))
|
||||
|
|
|
@ -162,6 +162,8 @@ func TestAMPHop(t *testing.T) {
|
|||
// TestPayloadSize tests the payload size calculation that is provided by Hop
|
||||
// structs.
|
||||
func TestPayloadSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
hops := []*Hop{
|
||||
{
|
||||
PubKeyBytes: testPubKeyBytes,
|
||||
|
@ -181,7 +183,9 @@ func TestPayloadSize(t *testing.T) {
|
|||
AmtToForward: 1200,
|
||||
OutgoingTimeLock: 700000,
|
||||
MPP: record.NewMPP(500, [32]byte{}),
|
||||
AMP: record.NewAMP([32]byte{}, [32]byte{}, 8),
|
||||
AMP: record.NewAMP(
|
||||
[32]byte{}, [32]byte{}, 8,
|
||||
),
|
||||
CustomRecords: map[uint64][]byte{
|
||||
100000: {1, 2, 3},
|
||||
1000000: {4, 5},
|
||||
|
@ -190,6 +194,69 @@ func TestPayloadSize(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
blindedHops := []*Hop{
|
||||
{
|
||||
// Unblinded hop to introduction node.
|
||||
PubKeyBytes: testPubKeyBytes,
|
||||
AmtToForward: 1000,
|
||||
OutgoingTimeLock: 600000,
|
||||
ChannelID: 3432483437438,
|
||||
LegacyPayload: true,
|
||||
},
|
||||
{
|
||||
// Payload for an introduction node in a blinded route
|
||||
// that has the blinding point provided in the onion
|
||||
// payload, and encrypted data pointing it to the next
|
||||
// node.
|
||||
PubKeyBytes: testPubKeyBytes,
|
||||
EncryptedData: []byte{12, 13},
|
||||
BlindingPoint: testPubKey,
|
||||
},
|
||||
{
|
||||
// Payload for a forwarding node in a blinded route
|
||||
// that has encrypted data provided in the onion
|
||||
// payload, but no blinding point (it's provided in
|
||||
// update_add_htlc).
|
||||
PubKeyBytes: testPubKeyBytes,
|
||||
EncryptedData: []byte{12, 13},
|
||||
},
|
||||
{
|
||||
// Final hop has encrypted data and other final hop
|
||||
// fields like metadata.
|
||||
PubKeyBytes: testPubKeyBytes,
|
||||
AmtToForward: 900,
|
||||
OutgoingTimeLock: 50000,
|
||||
Metadata: []byte{10, 11},
|
||||
EncryptedData: []byte{12, 13},
|
||||
TotalAmtMsat: lnwire.MilliSatoshi(900),
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
hops []*Hop
|
||||
}{
|
||||
{
|
||||
name: "clear route",
|
||||
hops: hops,
|
||||
},
|
||||
{
|
||||
name: "blinded route",
|
||||
hops: blindedHops,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
testPayloadSize(t, testCase.hops)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testPayloadSize(t *testing.T, hops []*Hop) {
|
||||
rt := Route{
|
||||
Hops: hops,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue