Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions channeldb/migration_01_to_11/payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -562,12 +562,7 @@ func deserializeHop(r io.Reader) (*route.Hop, error) {
tlvMap[tlvType] = rawRecordBytes
}

tlvRecords, err := tlv.MapToRecords(tlvMap)
if err != nil {
return nil, err
}

h.TLVRecords = tlvRecords
h.TLVRecords = tlv.MapToRecords(tlvMap)

return h, nil
}
Expand Down
7 changes: 1 addition & 6 deletions channeldb/payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -710,12 +710,7 @@ func deserializeHop(r io.Reader) (*route.Hop, error) {
h.MPP = mpp
}

tlvRecords, err := tlv.MapToRecords(tlvMap)
if err != nil {
return nil, err
}

h.TLVRecords = tlvRecords
h.TLVRecords = tlv.MapToRecords(tlvMap)

return h, nil
}
Expand Down
6 changes: 3 additions & 3 deletions htlcswitch/hop/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ const (
)

const (
// customTypeStart is the start of the custom tlv type range as defined
// CustomTypeStart is the start of the custom tlv type range as defined
// in BOLT 01.
customTypeStart = 65536
CustomTypeStart = 65536
)

// String returns a human-readable description of the violation as a verb.
Expand Down Expand Up @@ -249,7 +249,7 @@ func getMinRequiredViolation(set tlv.TypeSet) *tlv.Type {
//
// We always accept custom fields, because a higher level
// application may understand them.
if known || t%2 != 0 || t >= customTypeStart {
if known || t%2 != 0 || t >= CustomTypeStart {
continue
}

Expand Down
273 changes: 138 additions & 135 deletions lnrpc/routerrpc/router.pb.go

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions lnrpc/routerrpc/router.proto
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ message SendPaymentRequest {
/**
An optional field that can be used to pass an arbitrary set of TLV records
to a peer which understands the new records. This can be used to pass
application specific data during the payment attempt.
application specific data during the payment attempt. Record types are
required to be in the custom range >= 65536. When using REST, the values
must be encoded as base64.
*/
map<uint64, bytes> dest_tlv = 11;
map<uint64, bytes> dest_custom_records = 11;

/// If set, circular payments to self are permitted.
bool allow_self_payment = 15;
Expand Down
139 changes: 70 additions & 69 deletions lnrpc/routerrpc/router_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
Expand Down Expand Up @@ -200,7 +201,6 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
}
cltvLimit -= uint32(finalCLTVDelta)

var destTLV map[uint64][]byte
restrictions := &routing.RestrictParams{
FeeLimit: feeLimit,
ProbabilitySource: func(fromNode, toNode route.Vertex,
Expand All @@ -226,14 +226,14 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
fromNode, toNode, amt,
)
},
DestPayloadTLV: len(destTLV) != 0,
DestPayloadTLV: len(in.DestCustomRecords) != 0,
CltvLimit: cltvLimit,
}

// If we have any TLV records destined for the final hop, then we'll
// attempt to decode them now into a form that the router can more
// easily manipulate.
destTlvRecords, err := tlv.MapToRecords(destTLV)
destTlvRecords, err := UnmarshallCustomRecords(in.DestCustomRecords)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -347,6 +347,11 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error)
}
}

tlvMap, err := tlv.RecordsToMap(hop.TLVRecords)
if err != nil {
return nil, err
}

resp.Hops[i] = &lnrpc.Hop{
ChanId: hop.ChannelID,
ChanCapacity: int64(chanCapacity),
Expand All @@ -358,99 +363,97 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error)
PubKey: hex.EncodeToString(
hop.PubKeyBytes[:],
),
TlvPayload: !hop.LegacyPayload,
MppRecord: mpp,
CustomRecords: tlvMap,
TlvPayload: !hop.LegacyPayload,
MppRecord: mpp,
}
incomingAmt = hop.AmtToForward
}

return resp, nil
}

// UnmarshallHopByChannelLookup unmarshalls an rpc hop for which the pub key is
// not known. This function will query the channel graph with channel id to
// retrieve both endpoints and determine the hop pubkey using the previous hop
// pubkey. If the channel is unknown, an error is returned.
func (r *RouterBackend) UnmarshallHopByChannelLookup(hop *lnrpc.Hop,
prevPubKeyBytes [33]byte) (*route.Hop, error) {

// Discard edge policies, because they may be nil.
node1, node2, err := r.FetchChannelEndpoints(hop.ChanId)
if err != nil {
return nil, err
}
// UnmarshallCustomRecords unmarshall rpc custom records to tlv records.
func UnmarshallCustomRecords(rpcRecords map[uint64][]byte) ([]tlv.Record,
error) {

var pubKeyBytes [33]byte
switch {
case prevPubKeyBytes == node1:
pubKeyBytes = node2
case prevPubKeyBytes == node2:
pubKeyBytes = node1
default:
return nil, fmt.Errorf("channel edge does not match expected node")
if len(rpcRecords) == 0 {
return nil, nil
}

var tlvRecords []tlv.Record
tlvRecords := tlv.MapToRecords(rpcRecords)

mpp, err := UnmarshalMPP(hop.MppRecord)
if err != nil {
return nil, err
// tlvRecords is sorted, so we only need to check that the first
// element is within the custom range.
if uint64(tlvRecords[0].Type()) < hop.CustomTypeStart {
Comment thread
cfromknecht marked this conversation as resolved.
Outdated
return nil, fmt.Errorf("no custom records with types "+
"below %v allowed", hop.CustomTypeStart)
}

return &route.Hop{
OutgoingTimeLock: hop.Expiry,
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat),
PubKeyBytes: pubKeyBytes,
ChannelID: hop.ChanId,
TLVRecords: tlvRecords,
LegacyPayload: !hop.TlvPayload,
MPP: mpp,
}, nil
return tlvRecords, nil
}

// UnmarshallKnownPubkeyHop unmarshalls an rpc hop that contains the hop pubkey.
// The channel graph doesn't need to be queried because all information required
// for sending the payment is present.
func UnmarshallKnownPubkeyHop(hop *lnrpc.Hop) (*route.Hop, error) {
pubKey, err := hex.DecodeString(hop.PubKey)
// UnmarshallHopWithPubkey unmarshalls an rpc hop for which the pubkey has
// already been extracted.
func UnmarshallHopWithPubkey(rpcHop *lnrpc.Hop, pubkey route.Vertex) (*route.Hop,
error) {

tlvRecords, err := UnmarshallCustomRecords(rpcHop.CustomRecords)
if err != nil {
return nil, fmt.Errorf("cannot decode pubkey %s", hop.PubKey)
return nil, err
}

var pubKeyBytes [33]byte
copy(pubKeyBytes[:], pubKey)

var tlvRecords []tlv.Record

mpp, err := UnmarshalMPP(hop.MppRecord)
mpp, err := UnmarshalMPP(rpcHop.MppRecord)
if err != nil {
return nil, err
}

return &route.Hop{
OutgoingTimeLock: hop.Expiry,
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat),
PubKeyBytes: pubKeyBytes,
ChannelID: hop.ChanId,
OutgoingTimeLock: rpcHop.Expiry,
AmtToForward: lnwire.MilliSatoshi(rpcHop.AmtToForwardMsat),
PubKeyBytes: pubkey,
ChannelID: rpcHop.ChanId,
TLVRecords: tlvRecords,
LegacyPayload: !hop.TlvPayload,
LegacyPayload: !rpcHop.TlvPayload,
MPP: mpp,
}, nil
}

// UnmarshallHop unmarshalls an rpc hop that may or may not contain a node
// pubkey.
func (r *RouterBackend) UnmarshallHop(hop *lnrpc.Hop,
func (r *RouterBackend) UnmarshallHop(rpcHop *lnrpc.Hop,
prevNodePubKey [33]byte) (*route.Hop, error) {

if hop.PubKey == "" {
// If no pub key is given of the hop, the local channel
// graph needs to be queried to complete the information
// necessary for routing.
return r.UnmarshallHopByChannelLookup(hop, prevNodePubKey)
var pubKeyBytes [33]byte
if rpcHop.PubKey != "" {
// Unmarshall the provided hop pubkey.
pubKey, err := hex.DecodeString(rpcHop.PubKey)
if err != nil {
return nil, fmt.Errorf("cannot decode pubkey %s",
rpcHop.PubKey)
}
copy(pubKeyBytes[:], pubKey)
} else {
// If no pub key is given of the hop, the local channel graph
// needs to be queried to complete the information necessary for
// routing. Discard edge policies, because they may be nil.
node1, node2, err := r.FetchChannelEndpoints(rpcHop.ChanId)
if err != nil {
return nil, err
}

switch {
case prevNodePubKey == node1:
pubKeyBytes = node2
case prevNodePubKey == node2:
pubKeyBytes = node1
default:
return nil, fmt.Errorf("channel edge does not match " +
"expected node")
}
}

return UnmarshallKnownPubkeyHop(hop)
return UnmarshallHopWithPubkey(rpcHop, pubKeyBytes)
}

// UnmarshallRoute unmarshalls an rpc route. For hops that don't specify a
Expand Down Expand Up @@ -531,13 +534,11 @@ func (r *RouterBackend) extractIntentFromSendRequest(
return nil, errors.New("timeout_seconds must be specified")
}

var destTLV map[uint64][]byte
if len(destTLV) != 0 {
var err error
payIntent.FinalDestRecords, err = tlv.MapToRecords(destTLV)
if err != nil {
return nil, err
}
payIntent.FinalDestRecords, err = UnmarshallCustomRecords(
rpcPayReq.DestCustomRecords,
)
if err != nil {
return nil, err
}

payIntent.PayAttemptTimeout = time.Second *
Expand Down
Loading