From 495f80e0bcbe9b193d34b7be35efae6aaa56a411 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 01:40:47 -0700 Subject: [PATCH 1/5] record/hop: add primitive hop TLV record constructors --- record/hop.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 record/hop.go diff --git a/record/hop.go b/record/hop.go new file mode 100644 index 000000000..74240083b --- /dev/null +++ b/record/hop.go @@ -0,0 +1,33 @@ +package record + +import ( + "github.com/lightningnetwork/lnd/tlv" +) + +// NewAmtToFwdRecord creates a tlv.Record that encodes the amount_to_forward +// (type 2) for an onion payload. +func NewAmtToFwdRecord(amt *uint64) tlv.Record { + return tlv.MakeDynamicRecord( + tlv.AmtOnionType, amt, func() uint64 { + return tlv.SizeTUint64(*amt) + }, + tlv.ETUint64, tlv.DTUint64, + ) +} + +// NewLockTimeRecord creates a tlv.Record that encodes the outgoing_cltv_value +// (type 4) for an onion payload. +func NewLockTimeRecord(lockTime *uint32) tlv.Record { + return tlv.MakeDynamicRecord( + tlv.LockTimeOnionType, lockTime, func() uint64 { + return tlv.SizeTUint32(*lockTime) + }, + tlv.ETUint32, tlv.DTUint32, + ) +} + +// NewNextHopIDRecord creates a tlv.Record that encodes the short_channel_id +// (type 6) for an onion payload. +func NewNextHopIDRecord(cid *uint64) tlv.Record { + return tlv.MakePrimitiveRecord(tlv.NextHopOnionType, cid) +} From dc0f429271cc686eda24365db3f916db377127ad Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 01:41:12 -0700 Subject: [PATCH 2/5] htlcswitch+routing: use TLV hop constructors --- htlcswitch/iterator.go | 13 ++++--------- htlcswitch/iterator_test.go | 17 ++++------------- routing/route/route.go | 17 ++++------------- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index 6e3ae07d8..f201129ea 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/tlv" ) @@ -175,15 +176,9 @@ func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) { var cid uint64 tlvStream, err := tlv.NewStream( - tlv.MakeDynamicRecord( - tlv.AmtOnionType, &amt, nil, - tlv.ETUint64, tlv.DTUint64, - ), - tlv.MakeDynamicRecord( - tlv.LockTimeOnionType, &cltv, nil, - tlv.ETUint32, tlv.DTUint32, - ), - tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &cid), + record.NewAmtToFwdRecord(&amt), + record.NewLockTimeRecord(&cltv), + record.NewNextHopIDRecord(&cid), ) if err != nil { return ForwardingInfo{}, err diff --git a/htlcswitch/iterator_test.go b/htlcswitch/iterator_test.go index 01c28ed9a..ae6940e5c 100644 --- a/htlcswitch/iterator_test.go +++ b/htlcswitch/iterator_test.go @@ -8,6 +8,7 @@ import ( "github.com/davecgh/go-spew/spew" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/tlv" ) @@ -38,19 +39,9 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { // as we would normally in the routing network. var b bytes.Buffer tlvRecords := []tlv.Record{ - tlv.MakeDynamicRecord( - tlv.AmtOnionType, &hopData.ForwardAmount, func() uint64 { - return tlv.SizeTUint64(hopData.ForwardAmount) - }, - tlv.ETUint64, tlv.DTUint64, - ), - tlv.MakeDynamicRecord( - tlv.LockTimeOnionType, &hopData.OutgoingCltv, func() uint64 { - return tlv.SizeTUint32(hopData.OutgoingCltv) - }, - tlv.ETUint32, tlv.DTUint32, - ), - tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &nextAddrInt), + record.NewAmtToFwdRecord(&hopData.ForwardAmount), + record.NewLockTimeRecord(&hopData.OutgoingCltv), + record.NewNextHopIDRecord(&nextAddrInt), } tlvStream, err := tlv.NewStream(tlvRecords...) if err != nil { diff --git a/routing/route/route.go b/routing/route/route.go index 70712d04e..13eddfeb8 100644 --- a/routing/route/route.go +++ b/routing/route/route.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/btcec" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/tlv" ) @@ -105,19 +106,9 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error { // required routing fields, as well as these optional values. amt := uint64(h.AmtToForward) combinedRecords := append(h.TLVRecords, - tlv.MakeDynamicRecord( - tlv.AmtOnionType, &amt, func() uint64 { - return tlv.SizeTUint64(amt) - }, - tlv.ETUint64, tlv.DTUint64, - ), - tlv.MakeDynamicRecord( - tlv.LockTimeOnionType, &h.OutgoingTimeLock, func() uint64 { - return tlv.SizeTUint32(h.OutgoingTimeLock) - }, - tlv.ETUint32, tlv.DTUint32, - ), - tlv.MakePrimitiveRecord(tlv.NextHopOnionType, &nextChanID), + record.NewAmtToFwdRecord(&amt), + record.NewLockTimeRecord(&h.OutgoingTimeLock), + record.NewNextHopIDRecord(&nextChanID), ) // To ensure we produce a canonical stream, we'll sort the records From 1b2c9a02b5c98000e9c8e13f09459744b4f078d9 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Fri, 30 Aug 2019 01:41:34 -0700 Subject: [PATCH 3/5] record+tlv/onion_types: move type constants to route/hop --- record/hop.go | 20 +++++++++++++++++--- tlv/onion_types.go | 15 --------------- 2 files changed, 17 insertions(+), 18 deletions(-) delete mode 100644 tlv/onion_types.go diff --git a/record/hop.go b/record/hop.go index 74240083b..3f515e6b0 100644 --- a/record/hop.go +++ b/record/hop.go @@ -4,11 +4,25 @@ import ( "github.com/lightningnetwork/lnd/tlv" ) +const ( + // AmtOnionType is the type used in the onion to refrence the amount to + // send to the next hop. + AmtOnionType tlv.Type = 2 + + // LockTimeTLV is the type used in the onion to refenernce the CLTV + // value that should be used for the next hop's HTLC. + LockTimeOnionType tlv.Type = 4 + + // NextHopOnionType is the type used in the onion to reference the ID + // of the next hop. + NextHopOnionType tlv.Type = 6 +) + // NewAmtToFwdRecord creates a tlv.Record that encodes the amount_to_forward // (type 2) for an onion payload. func NewAmtToFwdRecord(amt *uint64) tlv.Record { return tlv.MakeDynamicRecord( - tlv.AmtOnionType, amt, func() uint64 { + AmtOnionType, amt, func() uint64 { return tlv.SizeTUint64(*amt) }, tlv.ETUint64, tlv.DTUint64, @@ -19,7 +33,7 @@ func NewAmtToFwdRecord(amt *uint64) tlv.Record { // (type 4) for an onion payload. func NewLockTimeRecord(lockTime *uint32) tlv.Record { return tlv.MakeDynamicRecord( - tlv.LockTimeOnionType, lockTime, func() uint64 { + LockTimeOnionType, lockTime, func() uint64 { return tlv.SizeTUint32(*lockTime) }, tlv.ETUint32, tlv.DTUint32, @@ -29,5 +43,5 @@ func NewLockTimeRecord(lockTime *uint32) tlv.Record { // NewNextHopIDRecord creates a tlv.Record that encodes the short_channel_id // (type 6) for an onion payload. func NewNextHopIDRecord(cid *uint64) tlv.Record { - return tlv.MakePrimitiveRecord(tlv.NextHopOnionType, cid) + return tlv.MakePrimitiveRecord(NextHopOnionType, cid) } diff --git a/tlv/onion_types.go b/tlv/onion_types.go deleted file mode 100644 index 65d5b42c9..000000000 --- a/tlv/onion_types.go +++ /dev/null @@ -1,15 +0,0 @@ -package tlv - -const ( - // AmtOnionType is the type used in the onion to refrence the amount to - // send to the next hop. - AmtOnionType Type = 2 - - // LockTimeTLV is the type used in the onion to refenernce the CLTV - // value that should be used for the next hop's HTLC. - LockTimeOnionType Type = 4 - - // NextHopOnionType is the type used in the onion to reference the ID - // of the next hop. - NextHopOnionType Type = 6 -) From 278e10a2fd4d0ba6e59d27b5a27c61bc09a2df87 Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Wed, 4 Sep 2019 08:39:22 -0700 Subject: [PATCH 4/5] routing/route/route: omit next_hop_id for final hop BOLT04 says to omit this field for final hops, but must be present on all other hops. --- routing/route/route.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/routing/route/route.go b/routing/route/route.go index 13eddfeb8..e9e159f65 100644 --- a/routing/route/route.go +++ b/routing/route/route.go @@ -108,9 +108,18 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error { combinedRecords := append(h.TLVRecords, record.NewAmtToFwdRecord(&amt), record.NewLockTimeRecord(&h.OutgoingTimeLock), - record.NewNextHopIDRecord(&nextChanID), ) + // BOLT 04 says the next_hop_id should be omitted for the final hop, + // but present for all others. + // + // TODO(conner): test using hop.Exit once available + if nextChanID != 0 { + combinedRecords = append(combinedRecords, + record.NewNextHopIDRecord(&nextChanID), + ) + } + // To ensure we produce a canonical stream, we'll sort the records // before encoding them as a stream in the hop payload. tlv.SortRecords(combinedRecords) From 947cc50c136b3a0a98a66ec2987dcf0014d7ae8a Mon Sep 17 00:00:00 2001 From: Conner Fromknecht Date: Wed, 4 Sep 2019 14:11:31 -0700 Subject: [PATCH 5/5] routing/route/route: prevent modification of hop.TLVRecords Currently the underlying array backing the hop's TLVRecords is modified when combining custom records with the primitive forwarding info. This commit uses a fresh slice to prevent modifications from mutating the hop itself. --- routing/route/route.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/routing/route/route.go b/routing/route/route.go index e9e159f65..ef9024682 100644 --- a/routing/route/route.go +++ b/routing/route/route.go @@ -104,8 +104,11 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error { // Otherwise, we'll need to make a new stream that includes our // required routing fields, as well as these optional values. + var records []tlv.Record + + // Every hop must have an amount to forward and CLTV expiry. amt := uint64(h.AmtToForward) - combinedRecords := append(h.TLVRecords, + records = append(records, record.NewAmtToFwdRecord(&amt), record.NewLockTimeRecord(&h.OutgoingTimeLock), ) @@ -115,16 +118,19 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error { // // TODO(conner): test using hop.Exit once available if nextChanID != 0 { - combinedRecords = append(combinedRecords, + records = append(records, record.NewNextHopIDRecord(&nextChanID), ) } + // Append any custom types destined for this hop. + records = append(records, h.TLVRecords...) + // To ensure we produce a canonical stream, we'll sort the records // before encoding them as a stream in the hop payload. - tlv.SortRecords(combinedRecords) + tlv.SortRecords(records) - tlvStream, err := tlv.NewStream(combinedRecords...) + tlvStream, err := tlv.NewStream(records...) if err != nil { return err }