mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-02 17:52:28 +02:00
routing/route+tlv: add new TLV-EOB awareness to Hop+Route
In this commit, we extend the Hop struct to carry an arbitrary set of TLV values, and add a new field that allows us to distinguish between the modern and legacy TLV payload. We add a new `PackPayload` method that will be used to encode the combined required routing TLV fields along any set of TLV fields that were specified as part of path finding. Finally, the `ToSphinxPath` has been extended to be able to recognize if a hop needs the modern, or legacy payload.
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/tlv"
|
||||
)
|
||||
|
||||
// VertexSize is the size of the array to store a vertex.
|
||||
@@ -72,6 +75,61 @@ type Hop struct {
|
||||
// hop. This value is less than the value that the incoming HTLC
|
||||
// carries as a fee will be subtracted by the hop.
|
||||
AmtToForward lnwire.MilliSatoshi
|
||||
|
||||
// TLVRecords if non-nil are a set of additional TLV records that
|
||||
// should be included in the forwarding instructions for this node.
|
||||
TLVRecords []tlv.Record
|
||||
|
||||
// LegacyPayload if true, then this signals that this node doesn't
|
||||
// understand the new TLV payload, so we must instead use the legacy
|
||||
// payload.
|
||||
LegacyPayload bool
|
||||
}
|
||||
|
||||
// PackHopPayload writes to the passed io.Writer, the series of byes that can
|
||||
// be placed directly into the per-hop payload (EOB) for this hop. This will
|
||||
// include the required routing fields, as well as serializing any of the
|
||||
// passed optional TLVRecords. nextChanID is the unique channel ID that
|
||||
// references the _outgoing_ channel ID that follows this hop. This field
|
||||
// follows the same semantics as the NextAddress field in the onion: it should
|
||||
// be set to zero to indicate the terminal hop.
|
||||
func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
|
||||
// If this is a legacy payload, then we'll exit here as this method
|
||||
// shouldn't be called.
|
||||
if h.LegacyPayload == true {
|
||||
return fmt.Errorf("cannot pack hop payloads for legacy " +
|
||||
"payloads")
|
||||
}
|
||||
|
||||
// Otherwise, we'll need to make a new stream that includes our
|
||||
// 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),
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
tlvStream, err := tlv.NewStream(combinedRecords...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tlvStream.Encode(w)
|
||||
}
|
||||
|
||||
// Route represents a path through the channel graph which runs over one or
|
||||
@@ -156,7 +214,8 @@ func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
|
||||
|
||||
// ToSphinxPath converts a complete route into a sphinx PaymentPath that
|
||||
// contains the per-hop paylods used to encoding the HTLC routing data for each
|
||||
// hop in the route.
|
||||
// hop in the route. This method also accepts an optional EOB payload for the
|
||||
// final hop.
|
||||
func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
|
||||
var path sphinx.PaymentPath
|
||||
|
||||
@@ -171,17 +230,6 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path[i] = sphinx.OnionHop{
|
||||
NodePub: *pub,
|
||||
HopData: sphinx.HopData{
|
||||
// TODO(roasbeef): properly set realm, make
|
||||
// sphinx type an enum actually?
|
||||
Realm: [1]byte{0},
|
||||
ForwardAmount: uint64(hop.AmtToForward),
|
||||
OutgoingCltv: hop.OutgoingTimeLock,
|
||||
},
|
||||
}
|
||||
|
||||
// As a base case, the next hop is set to all zeroes in order
|
||||
// to indicate that the "last hop" as no further hops after it.
|
||||
nextHop := uint64(0)
|
||||
@@ -192,9 +240,50 @@ func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
|
||||
nextHop = r.Hops[i+1].ChannelID
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint64(
|
||||
path[i].HopData.NextAddress[:], nextHop,
|
||||
)
|
||||
var payload sphinx.HopPayload
|
||||
|
||||
// If this is the legacy payload, then we can just include the
|
||||
// hop data as normal.
|
||||
if hop.LegacyPayload {
|
||||
// Before we encode this value, we'll pack the next hop
|
||||
// into the NextAddress field of the hop info to ensure
|
||||
// we point to the right now.
|
||||
hopData := sphinx.HopData{
|
||||
ForwardAmount: uint64(hop.AmtToForward),
|
||||
OutgoingCltv: hop.OutgoingTimeLock,
|
||||
}
|
||||
binary.BigEndian.PutUint64(
|
||||
hopData.NextAddress[:], nextHop,
|
||||
)
|
||||
|
||||
payload, err = sphinx.NewHopPayload(&hopData, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// For non-legacy payloads, we'll need to pack the
|
||||
// routing information, along with any extra TLV
|
||||
// information into the new per-hop payload format.
|
||||
// We'll also pass in the chan ID of the hop this
|
||||
// channel should be forwarded to so we can construct a
|
||||
// valid payload.
|
||||
var b bytes.Buffer
|
||||
err := hop.PackHopPayload(&b, nextHop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(roasbeef): make better API for NewHopPayload?
|
||||
payload, err = sphinx.NewHopPayload(nil, b.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
path[i] = sphinx.OnionHop{
|
||||
NodePub: *pub,
|
||||
HopPayload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
return &path, nil
|
||||
|
Reference in New Issue
Block a user