Merge pull request #3819 from joostjager/fix-custom-record-payment
multi: fix custom record payment
This commit is contained in:
commit
75b94dec2b
@ -7,9 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -212,7 +212,7 @@ func TestInvoiceCancelSingleHtlc(t *testing.T) {
|
|||||||
key := CircuitKey{ChanID: lnwire.NewShortChanIDFromInt(1), HtlcID: 4}
|
key := CircuitKey{ChanID: lnwire.NewShortChanIDFromInt(1), HtlcID: 4}
|
||||||
htlc := HtlcAcceptDesc{
|
htlc := HtlcAcceptDesc{
|
||||||
Amt: 500,
|
Amt: 500,
|
||||||
CustomRecords: make(hop.CustomRecordSet),
|
CustomRecords: make(record.CustomSet),
|
||||||
}
|
}
|
||||||
invoice, err := db.UpdateInvoice(paymentHash,
|
invoice, err := db.UpdateInvoice(paymentHash,
|
||||||
func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
func(invoice *Invoice) (*InvoiceUpdateDesc, error) {
|
||||||
@ -439,7 +439,7 @@ func TestDuplicateSettleInvoice(t *testing.T) {
|
|||||||
AcceptTime: time.Unix(1, 0),
|
AcceptTime: time.Unix(1, 0),
|
||||||
ResolveTime: time.Unix(1, 0),
|
ResolveTime: time.Unix(1, 0),
|
||||||
State: HtlcStateSettled,
|
State: HtlcStateSettled,
|
||||||
CustomRecords: make(hop.CustomRecordSet),
|
CustomRecords: make(record.CustomSet),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,7 +751,7 @@ func getUpdateInvoice(amt lnwire.MilliSatoshi) InvoiceUpdateCallback {
|
|||||||
return nil, ErrInvoiceAlreadySettled
|
return nil, ErrInvoiceAlreadySettled
|
||||||
}
|
}
|
||||||
|
|
||||||
noRecords := make(hop.CustomRecordSet)
|
noRecords := make(record.CustomSet)
|
||||||
|
|
||||||
update := &InvoiceUpdateDesc{
|
update := &InvoiceUpdateDesc{
|
||||||
State: &InvoiceStateUpdateDesc{
|
State: &InvoiceStateUpdateDesc{
|
||||||
@ -795,7 +795,7 @@ func TestCustomRecords(t *testing.T) {
|
|||||||
// Accept an htlc with custom records on this invoice.
|
// Accept an htlc with custom records on this invoice.
|
||||||
key := CircuitKey{ChanID: lnwire.NewShortChanIDFromInt(1), HtlcID: 4}
|
key := CircuitKey{ChanID: lnwire.NewShortChanIDFromInt(1), HtlcID: 4}
|
||||||
|
|
||||||
records := hop.CustomRecordSet{
|
records := record.CustomSet{
|
||||||
100000: []byte{},
|
100000: []byte{},
|
||||||
100001: []byte{1, 2},
|
100001: []byte{1, 2},
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -317,7 +318,7 @@ type InvoiceHTLC struct {
|
|||||||
|
|
||||||
// CustomRecords contains the custom key/value pairs that accompanied
|
// CustomRecords contains the custom key/value pairs that accompanied
|
||||||
// the htlc.
|
// the htlc.
|
||||||
CustomRecords hop.CustomRecordSet
|
CustomRecords record.CustomSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// HtlcAcceptDesc describes the details of a newly accepted htlc.
|
// HtlcAcceptDesc describes the details of a newly accepted htlc.
|
||||||
@ -337,7 +338,7 @@ type HtlcAcceptDesc struct {
|
|||||||
|
|
||||||
// CustomRecords contains the custom key/value pairs that accompanied
|
// CustomRecords contains the custom key/value pairs that accompanied
|
||||||
// the htlc.
|
// the htlc.
|
||||||
CustomRecords hop.CustomRecordSet
|
CustomRecords record.CustomSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvoiceUpdateDesc describes the changes that should be applied to the
|
// InvoiceUpdateDesc describes the changes that should be applied to the
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -274,7 +273,7 @@ func serializePaymentAttemptInfoMigration9(w io.Writer, a *PaymentAttemptInfo) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeHopMigration9(w io.Writer, h *route.Hop) error {
|
func serializeHopMigration9(w io.Writer, h *Hop) error {
|
||||||
if err := WriteElements(w,
|
if err := WriteElements(w,
|
||||||
h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
|
h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
|
||||||
h.AmtToForward,
|
h.AmtToForward,
|
||||||
@ -285,7 +284,7 @@ func serializeHopMigration9(w io.Writer, h *route.Hop) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeRouteMigration9(w io.Writer, r route.Route) error {
|
func serializeRouteMigration9(w io.Writer, r Route) error {
|
||||||
if err := WriteElements(w,
|
if err := WriteElements(w,
|
||||||
r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
|
r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -318,8 +317,8 @@ func deserializePaymentAttemptInfoMigration9(r io.Reader) (*PaymentAttemptInfo,
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserializeRouteMigration9(r io.Reader) (route.Route, error) {
|
func deserializeRouteMigration9(r io.Reader) (Route, error) {
|
||||||
rt := route.Route{}
|
rt := Route{}
|
||||||
if err := ReadElements(r,
|
if err := ReadElements(r,
|
||||||
&rt.TotalTimeLock, &rt.TotalAmount,
|
&rt.TotalTimeLock, &rt.TotalAmount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -337,7 +336,7 @@ func deserializeRouteMigration9(r io.Reader) (route.Route, error) {
|
|||||||
return rt, err
|
return rt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var hops []*route.Hop
|
var hops []*Hop
|
||||||
for i := uint32(0); i < numHops; i++ {
|
for i := uint32(0); i < numHops; i++ {
|
||||||
hop, err := deserializeHopMigration9(r)
|
hop, err := deserializeHopMigration9(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -350,8 +349,8 @@ func deserializeRouteMigration9(r io.Reader) (route.Route, error) {
|
|||||||
return rt, nil
|
return rt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserializeHopMigration9(r io.Reader) (*route.Hop, error) {
|
func deserializeHopMigration9(r io.Reader) (*Hop, error) {
|
||||||
h := &route.Hop{}
|
h := &Hop{}
|
||||||
|
|
||||||
var pub []byte
|
var pub []byte
|
||||||
if err := ReadElements(r, &pub); err != nil {
|
if err := ReadElements(r, &pub); err != nil {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MigrateRouteSerialization migrates the way we serialize routes across the
|
// MigrateRouteSerialization migrates the way we serialize routes across the
|
||||||
@ -154,8 +153,8 @@ func serializePaymentAttemptInfoLegacy(w io.Writer, a *PaymentAttemptInfo) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserializeHopLegacy(r io.Reader) (*route.Hop, error) {
|
func deserializeHopLegacy(r io.Reader) (*Hop, error) {
|
||||||
h := &route.Hop{}
|
h := &Hop{}
|
||||||
|
|
||||||
var pub []byte
|
var pub []byte
|
||||||
if err := ReadElements(r, &pub); err != nil {
|
if err := ReadElements(r, &pub); err != nil {
|
||||||
@ -172,7 +171,7 @@ func deserializeHopLegacy(r io.Reader) (*route.Hop, error) {
|
|||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeHopLegacy(w io.Writer, h *route.Hop) error {
|
func serializeHopLegacy(w io.Writer, h *Hop) error {
|
||||||
if err := WriteElements(w,
|
if err := WriteElements(w,
|
||||||
h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
|
h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
|
||||||
h.AmtToForward,
|
h.AmtToForward,
|
||||||
@ -183,8 +182,8 @@ func serializeHopLegacy(w io.Writer, h *route.Hop) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deserializeRouteLegacy(r io.Reader) (route.Route, error) {
|
func deserializeRouteLegacy(r io.Reader) (Route, error) {
|
||||||
rt := route.Route{}
|
rt := Route{}
|
||||||
if err := ReadElements(r,
|
if err := ReadElements(r,
|
||||||
&rt.TotalTimeLock, &rt.TotalAmount,
|
&rt.TotalTimeLock, &rt.TotalAmount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -202,7 +201,7 @@ func deserializeRouteLegacy(r io.Reader) (route.Route, error) {
|
|||||||
return rt, err
|
return rt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var hops []*route.Hop
|
var hops []*Hop
|
||||||
for i := uint32(0); i < numHops; i++ {
|
for i := uint32(0); i < numHops; i++ {
|
||||||
hop, err := deserializeHopLegacy(r)
|
hop, err := deserializeHopLegacy(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -215,7 +214,7 @@ func deserializeRouteLegacy(r io.Reader) (route.Route, error) {
|
|||||||
return rt, nil
|
return rt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeRouteLegacy(w io.Writer, r route.Route) error {
|
func serializeRouteLegacy(w io.Writer, r Route) error {
|
||||||
if err := WriteElements(w,
|
if err := WriteElements(w,
|
||||||
r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
|
r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
|
||||||
); err != nil {
|
); err != nil {
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MigrateNodeAndEdgeUpdateIndex is a migration function that will update the
|
// MigrateNodeAndEdgeUpdateIndex is a migration function that will update the
|
||||||
@ -817,14 +816,14 @@ func MigrateOutgoingPayments(tx *bbolt.Tx) error {
|
|||||||
|
|
||||||
// Do the same for the PaymentAttemptInfo.
|
// Do the same for the PaymentAttemptInfo.
|
||||||
totalAmt := payment.Terms.Value + payment.Fee
|
totalAmt := payment.Terms.Value + payment.Fee
|
||||||
rt := route.Route{
|
rt := Route{
|
||||||
TotalTimeLock: payment.TimeLockLength,
|
TotalTimeLock: payment.TimeLockLength,
|
||||||
TotalAmount: totalAmt,
|
TotalAmount: totalAmt,
|
||||||
SourcePubKey: sourcePubKey,
|
SourcePubKey: sourcePubKey,
|
||||||
Hops: []*route.Hop{},
|
Hops: []*Hop{},
|
||||||
}
|
}
|
||||||
for _, hop := range payment.Path {
|
for _, hop := range payment.Path {
|
||||||
rt.Hops = append(rt.Hops, &route.Hop{
|
rt.Hops = append(rt.Hops, &Hop{
|
||||||
PubKeyBytes: hop,
|
PubKeyBytes: hop,
|
||||||
AmtToForward: totalAmt,
|
AmtToForward: totalAmt,
|
||||||
})
|
})
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestPaymentStatusesMigration checks that already completed payments will have
|
// TestPaymentStatusesMigration checks that already completed payments will have
|
||||||
@ -714,25 +713,25 @@ func makeRandPaymentCreationInfo() (*PaymentCreationInfo, error) {
|
|||||||
func TestPaymentRouteSerialization(t *testing.T) {
|
func TestPaymentRouteSerialization(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
legacyHop1 := &route.Hop{
|
legacyHop1 := &Hop{
|
||||||
PubKeyBytes: route.NewVertex(pub),
|
PubKeyBytes: NewVertex(pub),
|
||||||
ChannelID: 12345,
|
ChannelID: 12345,
|
||||||
OutgoingTimeLock: 111,
|
OutgoingTimeLock: 111,
|
||||||
LegacyPayload: true,
|
LegacyPayload: true,
|
||||||
AmtToForward: 555,
|
AmtToForward: 555,
|
||||||
}
|
}
|
||||||
legacyHop2 := &route.Hop{
|
legacyHop2 := &Hop{
|
||||||
PubKeyBytes: route.NewVertex(pub),
|
PubKeyBytes: NewVertex(pub),
|
||||||
ChannelID: 12345,
|
ChannelID: 12345,
|
||||||
OutgoingTimeLock: 111,
|
OutgoingTimeLock: 111,
|
||||||
LegacyPayload: true,
|
LegacyPayload: true,
|
||||||
AmtToForward: 555,
|
AmtToForward: 555,
|
||||||
}
|
}
|
||||||
legacyRoute := route.Route{
|
legacyRoute := Route{
|
||||||
TotalTimeLock: 123,
|
TotalTimeLock: 123,
|
||||||
TotalAmount: 1234567,
|
TotalAmount: 1234567,
|
||||||
SourcePubKey: route.NewVertex(pub),
|
SourcePubKey: NewVertex(pub),
|
||||||
Hops: []*route.Hop{legacyHop1, legacyHop2},
|
Hops: []*Hop{legacyHop1, legacyHop2},
|
||||||
}
|
}
|
||||||
|
|
||||||
const numPayments = 4
|
const numPayments = 4
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
"github.com/lightningnetwork/lnd/tlv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -213,7 +212,7 @@ type PaymentAttemptInfo struct {
|
|||||||
SessionKey *btcec.PrivateKey
|
SessionKey *btcec.PrivateKey
|
||||||
|
|
||||||
// Route is the route attempted to send the HTLC.
|
// Route is the route attempted to send the HTLC.
|
||||||
Route route.Route
|
Route Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// Payment is a wrapper around a payment's PaymentCreationInfo,
|
// Payment is a wrapper around a payment's PaymentCreationInfo,
|
||||||
@ -464,7 +463,7 @@ func deserializePaymentAttemptInfo(r io.Reader) (*PaymentAttemptInfo, error) {
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func serializeHop(w io.Writer, h *route.Hop) error {
|
func serializeHop(w io.Writer, h *Hop) error {
|
||||||
if err := WriteElements(w,
|
if err := WriteElements(w,
|
||||||
h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
|
h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
|
||||||
h.AmtToForward,
|
h.AmtToForward,
|
||||||
@ -513,8 +512,8 @@ func serializeHop(w io.Writer, h *route.Hop) error {
|
|||||||
// to read/write a TLV stream larger than this.
|
// to read/write a TLV stream larger than this.
|
||||||
const maxOnionPayloadSize = 1300
|
const maxOnionPayloadSize = 1300
|
||||||
|
|
||||||
func deserializeHop(r io.Reader) (*route.Hop, error) {
|
func deserializeHop(r io.Reader) (*Hop, error) {
|
||||||
h := &route.Hop{}
|
h := &Hop{}
|
||||||
|
|
||||||
var pub []byte
|
var pub []byte
|
||||||
if err := ReadElements(r, &pub); err != nil {
|
if err := ReadElements(r, &pub); err != nil {
|
||||||
@ -568,7 +567,7 @@ func deserializeHop(r io.Reader) (*route.Hop, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SerializeRoute serializes a route.
|
// SerializeRoute serializes a route.
|
||||||
func SerializeRoute(w io.Writer, r route.Route) error {
|
func SerializeRoute(w io.Writer, r Route) error {
|
||||||
if err := WriteElements(w,
|
if err := WriteElements(w,
|
||||||
r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
|
r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -589,8 +588,8 @@ func SerializeRoute(w io.Writer, r route.Route) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeserializeRoute deserializes a route.
|
// DeserializeRoute deserializes a route.
|
||||||
func DeserializeRoute(r io.Reader) (route.Route, error) {
|
func DeserializeRoute(r io.Reader) (Route, error) {
|
||||||
rt := route.Route{}
|
rt := Route{}
|
||||||
if err := ReadElements(r,
|
if err := ReadElements(r,
|
||||||
&rt.TotalTimeLock, &rt.TotalAmount,
|
&rt.TotalTimeLock, &rt.TotalAmount,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@ -608,7 +607,7 @@ func DeserializeRoute(r io.Reader) (route.Route, error) {
|
|||||||
return rt, err
|
return rt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var hops []*route.Hop
|
var hops []*Hop
|
||||||
for i := uint32(0); i < numHops; i++ {
|
for i := uint32(0); i < numHops; i++ {
|
||||||
hop, err := deserializeHop(r)
|
hop, err := deserializeHop(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
330
channeldb/migration_01_to_11/route.go
Normal file
330
channeldb/migration_01_to_11/route.go
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
package migration_01_to_11
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VertexSize is the size of the array to store a vertex.
|
||||||
|
const VertexSize = 33
|
||||||
|
|
||||||
|
// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
|
||||||
|
// sphinx packet, but provides an empty set of hops for each route.
|
||||||
|
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")
|
||||||
|
|
||||||
|
// Vertex is a simple alias for the serialization of a compressed Bitcoin
|
||||||
|
// public key.
|
||||||
|
type Vertex [VertexSize]byte
|
||||||
|
|
||||||
|
// NewVertex returns a new Vertex given a public key.
|
||||||
|
func NewVertex(pub *btcec.PublicKey) Vertex {
|
||||||
|
var v Vertex
|
||||||
|
copy(v[:], pub.SerializeCompressed())
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVertexFromBytes returns a new Vertex based on a serialized pubkey in a
|
||||||
|
// byte slice.
|
||||||
|
func NewVertexFromBytes(b []byte) (Vertex, error) {
|
||||||
|
vertexLen := len(b)
|
||||||
|
if vertexLen != VertexSize {
|
||||||
|
return Vertex{}, fmt.Errorf("invalid vertex length of %v, "+
|
||||||
|
"want %v", vertexLen, VertexSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
var v Vertex
|
||||||
|
copy(v[:], b)
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewVertexFromStr returns a new Vertex given its hex-encoded string format.
|
||||||
|
func NewVertexFromStr(v string) (Vertex, error) {
|
||||||
|
// Return error if hex string is of incorrect length.
|
||||||
|
if len(v) != VertexSize*2 {
|
||||||
|
return Vertex{}, fmt.Errorf("invalid vertex string length of "+
|
||||||
|
"%v, want %v", len(v), VertexSize*2)
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex, err := hex.DecodeString(v)
|
||||||
|
if err != nil {
|
||||||
|
return Vertex{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewVertexFromBytes(vertex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable version of the Vertex which is the
|
||||||
|
// hex-encoding of the serialized compressed public key.
|
||||||
|
func (v Vertex) String() string {
|
||||||
|
return fmt.Sprintf("%x", v[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hop represents an intermediate or final node of the route. This naming
|
||||||
|
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
|
||||||
|
// The struct houses the channel along which this hop can be reached and
|
||||||
|
// the values necessary to create the HTLC that needs to be sent to the
|
||||||
|
// next hop. It is also used to encode the per-hop payload included within
|
||||||
|
// the Sphinx packet.
|
||||||
|
type Hop struct {
|
||||||
|
// PubKeyBytes is the raw bytes of the public key of the target node.
|
||||||
|
PubKeyBytes Vertex
|
||||||
|
|
||||||
|
// ChannelID is the unique channel ID for the channel. The first 3
|
||||||
|
// bytes are the block height, the next 3 the index within the block,
|
||||||
|
// and the last 2 bytes are the output index for the channel.
|
||||||
|
ChannelID uint64
|
||||||
|
|
||||||
|
// OutgoingTimeLock is the timelock value that should be used when
|
||||||
|
// crafting the _outgoing_ HTLC from this hop.
|
||||||
|
OutgoingTimeLock uint32
|
||||||
|
|
||||||
|
// AmtToForward is the amount that this hop will forward to the next
|
||||||
|
// 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.
|
||||||
|
var records []tlv.Record
|
||||||
|
|
||||||
|
// Every hop must have an amount to forward and CLTV expiry.
|
||||||
|
amt := uint64(h.AmtToForward)
|
||||||
|
records = append(records,
|
||||||
|
record.NewAmtToFwdRecord(&amt),
|
||||||
|
record.NewLockTimeRecord(&h.OutgoingTimeLock),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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(records)
|
||||||
|
|
||||||
|
tlvStream, err := tlv.NewStream(records...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlvStream.Encode(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route represents a path through the channel graph which runs over one or
|
||||||
|
// more channels in succession. This struct carries all the information
|
||||||
|
// required to craft the Sphinx onion packet, and send the payment along the
|
||||||
|
// first hop in the path. A route is only selected as valid if all the channels
|
||||||
|
// have sufficient capacity to carry the initial payment amount after fees are
|
||||||
|
// accounted for.
|
||||||
|
type Route struct {
|
||||||
|
// TotalTimeLock is the cumulative (final) time lock across the entire
|
||||||
|
// route. This is the CLTV value that should be extended to the first
|
||||||
|
// hop in the route. All other hops will decrement the time-lock as
|
||||||
|
// advertised, leaving enough time for all hops to wait for or present
|
||||||
|
// the payment preimage to complete the payment.
|
||||||
|
TotalTimeLock uint32
|
||||||
|
|
||||||
|
// TotalAmount is the total amount of funds required to complete a
|
||||||
|
// payment over this route. This value includes the cumulative fees at
|
||||||
|
// each hop. As a result, the HTLC extended to the first-hop in the
|
||||||
|
// route will need to have at least this many satoshis, otherwise the
|
||||||
|
// route will fail at an intermediate node due to an insufficient
|
||||||
|
// amount of fees.
|
||||||
|
TotalAmount lnwire.MilliSatoshi
|
||||||
|
|
||||||
|
// SourcePubKey is the pubkey of the node where this route originates
|
||||||
|
// from.
|
||||||
|
SourcePubKey Vertex
|
||||||
|
|
||||||
|
// Hops contains details concerning the specific forwarding details at
|
||||||
|
// each hop.
|
||||||
|
Hops []*Hop
|
||||||
|
}
|
||||||
|
|
||||||
|
// HopFee returns the fee charged by the route hop indicated by hopIndex.
|
||||||
|
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
|
||||||
|
var incomingAmt lnwire.MilliSatoshi
|
||||||
|
if hopIndex == 0 {
|
||||||
|
incomingAmt = r.TotalAmount
|
||||||
|
} else {
|
||||||
|
incomingAmt = r.Hops[hopIndex-1].AmtToForward
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fee is calculated as difference between incoming and outgoing amount.
|
||||||
|
return incomingAmt - r.Hops[hopIndex].AmtToForward
|
||||||
|
}
|
||||||
|
|
||||||
|
// TotalFees is the sum of the fees paid at each hop within the final route. In
|
||||||
|
// the case of a one-hop payment, this value will be zero as we don't need to
|
||||||
|
// pay a fee to ourself.
|
||||||
|
func (r *Route) TotalFees() lnwire.MilliSatoshi {
|
||||||
|
if len(r.Hops) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.TotalAmount - r.Hops[len(r.Hops)-1].AmtToForward
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouteFromHops creates a new Route structure from the minimally required
|
||||||
|
// information to perform the payment. It infers fee amounts and populates the
|
||||||
|
// node, chan and prev/next hop maps.
|
||||||
|
func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
|
||||||
|
sourceVertex Vertex, hops []*Hop) (*Route, error) {
|
||||||
|
|
||||||
|
if len(hops) == 0 {
|
||||||
|
return nil, ErrNoRouteHopsProvided
|
||||||
|
}
|
||||||
|
|
||||||
|
// First, we'll create a route struct and populate it with the fields
|
||||||
|
// for which the values are provided as arguments of this function.
|
||||||
|
// TotalFees is determined based on the difference between the amount
|
||||||
|
// that is send from the source and the final amount that is received
|
||||||
|
// by the destination.
|
||||||
|
route := &Route{
|
||||||
|
SourcePubKey: sourceVertex,
|
||||||
|
Hops: hops,
|
||||||
|
TotalTimeLock: timeLock,
|
||||||
|
TotalAmount: amtToSend,
|
||||||
|
}
|
||||||
|
|
||||||
|
return route, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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. This method also accepts an optional EOB payload for the
|
||||||
|
// final hop.
|
||||||
|
func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
|
||||||
|
var path sphinx.PaymentPath
|
||||||
|
|
||||||
|
// For each hop encoded within the route, we'll convert the hop struct
|
||||||
|
// to an OnionHop with matching per-hop payload within the path as used
|
||||||
|
// by the sphinx package.
|
||||||
|
for i, hop := range r.Hops {
|
||||||
|
pub, err := btcec.ParsePubKey(
|
||||||
|
hop.PubKeyBytes[:], btcec.S256(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// If we aren't on the last hop, then we set the "next address"
|
||||||
|
// field to be the channel that directly follows it.
|
||||||
|
if i != len(r.Hops)-1 {
|
||||||
|
nextHop = r.Hops[i+1].ChannelID
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable representation of the route.
|
||||||
|
func (r *Route) String() string {
|
||||||
|
var b strings.Builder
|
||||||
|
|
||||||
|
for i, hop := range r.Hops {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteString(",")
|
||||||
|
}
|
||||||
|
b.WriteString(strconv.FormatUint(hop.ChannelID, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("amt=%v, fees=%v, tl=%v, chans=%v",
|
||||||
|
r.TotalAmount-r.TotalFees(), r.TotalFees(), r.TotalTimeLock,
|
||||||
|
b.String(),
|
||||||
|
)
|
||||||
|
}
|
@ -606,7 +606,9 @@ func serializeHop(w io.Writer, h *route.Hop) error {
|
|||||||
if h.MPP != nil {
|
if h.MPP != nil {
|
||||||
records = append(records, h.MPP.Record())
|
records = append(records, h.MPP.Record())
|
||||||
}
|
}
|
||||||
records = append(records, h.TLVRecords...)
|
|
||||||
|
tlvRecords := tlv.MapToRecords(h.CustomRecords)
|
||||||
|
records = append(records, tlvRecords...)
|
||||||
|
|
||||||
// Otherwise, we'll transform our slice of records into a map of the
|
// Otherwise, we'll transform our slice of records into a map of the
|
||||||
// raw bytes, then serialize them in-line with a length (number of
|
// raw bytes, then serialize them in-line with a length (number of
|
||||||
@ -710,7 +712,7 @@ func deserializeHop(r io.Reader) (*route.Hop, error) {
|
|||||||
h.MPP = mpp
|
h.MPP = mpp
|
||||||
}
|
}
|
||||||
|
|
||||||
h.TLVRecords = tlv.MapToRecords(tlvMap)
|
h.CustomRecords = tlvMap
|
||||||
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package channeldb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -28,9 +27,9 @@ var (
|
|||||||
ChannelID: 12345,
|
ChannelID: 12345,
|
||||||
OutgoingTimeLock: 111,
|
OutgoingTimeLock: 111,
|
||||||
AmtToForward: 555,
|
AmtToForward: 555,
|
||||||
TLVRecords: []tlv.Record{
|
CustomRecords: record.CustomSet{
|
||||||
tlv.MakeStaticRecord(1, nil, 3, tlvEncoder, nil),
|
1: []byte{},
|
||||||
tlv.MakeStaticRecord(2, nil, 3, tlvEncoder, nil),
|
2: []byte{},
|
||||||
},
|
},
|
||||||
MPP: record.NewMPP(32, [32]byte{0x42}),
|
MPP: record.NewMPP(32, [32]byte{0x42}),
|
||||||
}
|
}
|
||||||
@ -144,25 +143,7 @@ func TestSentPaymentSerialization(t *testing.T) {
|
|||||||
// assertRouteEquals compares to routes for equality and returns an error if
|
// assertRouteEquals compares to routes for equality and returns an error if
|
||||||
// they are not equal.
|
// they are not equal.
|
||||||
func assertRouteEqual(a, b *route.Route) error {
|
func assertRouteEqual(a, b *route.Route) error {
|
||||||
err := assertRouteHopRecordsEqual(a, b)
|
if !reflect.DeepEqual(a, b) {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLV records have already been compared and need to be cleared to
|
|
||||||
// properly compare the remaining fields using DeepEqual.
|
|
||||||
copyRouteNoHops := func(r *route.Route) *route.Route {
|
|
||||||
copy := *r
|
|
||||||
copy.Hops = make([]*route.Hop, len(r.Hops))
|
|
||||||
for i, hop := range r.Hops {
|
|
||||||
hopCopy := *hop
|
|
||||||
hopCopy.TLVRecords = nil
|
|
||||||
copy.Hops[i] = &hopCopy
|
|
||||||
}
|
|
||||||
return ©
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(copyRouteNoHops(a), copyRouteNoHops(b)) {
|
|
||||||
return fmt.Errorf("PaymentAttemptInfos don't match: %v vs %v",
|
return fmt.Errorf("PaymentAttemptInfos don't match: %v vs %v",
|
||||||
spew.Sdump(a), spew.Sdump(b))
|
spew.Sdump(a), spew.Sdump(b))
|
||||||
}
|
}
|
||||||
@ -170,57 +151,6 @@ func assertRouteEqual(a, b *route.Route) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertRouteHopRecordsEqual(r1, r2 *route.Route) error {
|
|
||||||
if len(r1.Hops) != len(r2.Hops) {
|
|
||||||
return errors.New("route hop count mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(r1.Hops); i++ {
|
|
||||||
records1 := r1.Hops[i].TLVRecords
|
|
||||||
records2 := r2.Hops[i].TLVRecords
|
|
||||||
if len(records1) != len(records2) {
|
|
||||||
return fmt.Errorf("route record count for hop %v "+
|
|
||||||
"mismatch", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := 0; j < len(records1); j++ {
|
|
||||||
expectedRecord := records1[j]
|
|
||||||
newRecord := records2[j]
|
|
||||||
|
|
||||||
err := assertHopRecordsEqual(expectedRecord, newRecord)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("route record mismatch: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertHopRecordsEqual(h1, h2 tlv.Record) error {
|
|
||||||
if h1.Type() != h2.Type() {
|
|
||||||
return fmt.Errorf("wrong type: expected %v, got %v", h1.Type(),
|
|
||||||
h2.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
if err := h2.Encode(&b); err != nil {
|
|
||||||
return fmt.Errorf("unable to encode record: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(b.Bytes(), tlvBytes) {
|
|
||||||
return fmt.Errorf("wrong raw record: expected %x, got %x",
|
|
||||||
tlvBytes, b.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
if h1.Size() != h2.Size() {
|
|
||||||
return fmt.Errorf("wrong size: expected %v, "+
|
|
||||||
"got %v", h1.Size(), h2.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRouteSerialization(t *testing.T) {
|
func TestRouteSerialization(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -29,12 +29,6 @@ const (
|
|||||||
RequiredViolation
|
RequiredViolation
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// CustomTypeStart is the start of the custom tlv type range as defined
|
|
||||||
// in BOLT 01.
|
|
||||||
CustomTypeStart = 65536
|
|
||||||
)
|
|
||||||
|
|
||||||
// String returns a human-readable description of the violation as a verb.
|
// String returns a human-readable description of the violation as a verb.
|
||||||
func (v PayloadViolation) String() string {
|
func (v PayloadViolation) String() string {
|
||||||
switch v {
|
switch v {
|
||||||
@ -79,9 +73,6 @@ func (e ErrInvalidPayload) Error() string {
|
|||||||
hopType, e.Violation, e.Type)
|
hopType, e.Violation, e.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomRecordSet stores a set of custom key/value pairs.
|
|
||||||
type CustomRecordSet map[uint64][]byte
|
|
||||||
|
|
||||||
// Payload encapsulates all information delivered to a hop in an onion payload.
|
// Payload encapsulates all information delivered to a hop in an onion payload.
|
||||||
// A Hop can represent either a TLV or legacy payload. The primary forwarding
|
// A Hop can represent either a TLV or legacy payload. The primary forwarding
|
||||||
// instruction can be accessed via ForwardingInfo, and additional records can be
|
// instruction can be accessed via ForwardingInfo, and additional records can be
|
||||||
@ -97,7 +88,7 @@ type Payload struct {
|
|||||||
|
|
||||||
// customRecords are user-defined records in the custom type range that
|
// customRecords are user-defined records in the custom type range that
|
||||||
// were included in the payload.
|
// were included in the payload.
|
||||||
customRecords CustomRecordSet
|
customRecords record.CustomSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLegacyPayload builds a Payload from the amount, cltv, and next hop
|
// NewLegacyPayload builds a Payload from the amount, cltv, and next hop
|
||||||
@ -112,7 +103,7 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload {
|
|||||||
AmountToForward: lnwire.MilliSatoshi(f.ForwardAmount),
|
AmountToForward: lnwire.MilliSatoshi(f.ForwardAmount),
|
||||||
OutgoingCTLV: f.OutgoingCltv,
|
OutgoingCTLV: f.OutgoingCltv,
|
||||||
},
|
},
|
||||||
customRecords: make(CustomRecordSet),
|
customRecords: make(record.CustomSet),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,10 +179,10 @@ func (h *Payload) ForwardingInfo() ForwardingInfo {
|
|||||||
|
|
||||||
// NewCustomRecords filters the types parsed from the tlv stream for custom
|
// NewCustomRecords filters the types parsed from the tlv stream for custom
|
||||||
// records.
|
// records.
|
||||||
func NewCustomRecords(parsedTypes tlv.TypeMap) CustomRecordSet {
|
func NewCustomRecords(parsedTypes tlv.TypeMap) record.CustomSet {
|
||||||
customRecords := make(CustomRecordSet)
|
customRecords := make(record.CustomSet)
|
||||||
for t, parseResult := range parsedTypes {
|
for t, parseResult := range parsedTypes {
|
||||||
if parseResult == nil || t < CustomTypeStart {
|
if parseResult == nil || t < record.CustomTypeStart {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
customRecords[uint64(t)] = parseResult
|
customRecords[uint64(t)] = parseResult
|
||||||
@ -261,7 +252,7 @@ func (h *Payload) MultiPath() *record.MPP {
|
|||||||
|
|
||||||
// CustomRecords returns the custom tlv type records that were parsed from the
|
// CustomRecords returns the custom tlv type records that were parsed from the
|
||||||
// payload.
|
// payload.
|
||||||
func (h *Payload) CustomRecords() CustomRecordSet {
|
func (h *Payload) CustomRecords() record.CustomSet {
|
||||||
return h.customRecords
|
return h.customRecords
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +271,9 @@ func getMinRequiredViolation(set tlv.TypeMap) *tlv.Type {
|
|||||||
//
|
//
|
||||||
// We always accept custom fields, because a higher level
|
// We always accept custom fields, because a higher level
|
||||||
// application may understand them.
|
// application may understand them.
|
||||||
if parseResult == nil || t%2 != 0 || t >= CustomTypeStart {
|
if parseResult == nil || t%2 != 0 ||
|
||||||
|
t >= record.CustomTypeStart {
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ func testDecodeHopPayloadValidation(t *testing.T, test decodePayloadTest) {
|
|||||||
|
|
||||||
// Convert expected nil map to empty map, because we always expect an
|
// Convert expected nil map to empty map, because we always expect an
|
||||||
// initiated map from the payload.
|
// initiated map from the payload.
|
||||||
expCustomRecords := make(hop.CustomRecordSet)
|
expCustomRecords := make(record.CustomSet)
|
||||||
if test.expCustomRecords != nil {
|
if test.expCustomRecords != nil {
|
||||||
expCustomRecords = test.expCustomRecords
|
expCustomRecords = test.expCustomRecords
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package invoices
|
package invoices
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,5 +13,5 @@ type Payload interface {
|
|||||||
|
|
||||||
// CustomRecords returns the custom tlv type records that were parsed
|
// CustomRecords returns the custom tlv type records that were parsed
|
||||||
// from the payload.
|
// from the payload.
|
||||||
CustomRecords() hop.CustomRecordSet
|
CustomRecords() record.CustomSet
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
@ -690,8 +689,8 @@ func (p *mockPayload) MultiPath() *record.MPP {
|
|||||||
return p.mpp
|
return p.mpp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *mockPayload) CustomRecords() hop.CustomRecordSet {
|
func (p *mockPayload) CustomRecords() record.CustomSet {
|
||||||
return make(hop.CustomRecordSet)
|
return make(record.CustomSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSettleMpp tests settling of an invoice with multiple partial payments.
|
// TestSettleMpp tests settling of an invoice with multiple partial payments.
|
||||||
|
@ -3,8 +3,6 @@ package invoices
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
@ -105,7 +103,7 @@ type invoiceUpdateCtx struct {
|
|||||||
expiry uint32
|
expiry uint32
|
||||||
currentHeight int32
|
currentHeight int32
|
||||||
finalCltvRejectDelta int32
|
finalCltvRejectDelta int32
|
||||||
customRecords hop.CustomRecordSet
|
customRecords record.CustomSet
|
||||||
mpp *record.MPP
|
mpp *record.MPP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,14 +13,12 @@ import (
|
|||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/record"
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,7 +44,7 @@ type RouterBackend struct {
|
|||||||
// routes.
|
// routes.
|
||||||
FindRoute func(source, target route.Vertex,
|
FindRoute func(source, target route.Vertex,
|
||||||
amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams,
|
amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams,
|
||||||
destTlvRecords []tlv.Record,
|
destCustomRecords record.CustomSet,
|
||||||
finalExpiry ...uint16) (*route.Route, error)
|
finalExpiry ...uint16) (*route.Route, error)
|
||||||
|
|
||||||
MissionControl MissionControl
|
MissionControl MissionControl
|
||||||
@ -226,14 +224,14 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
|||||||
fromNode, toNode, amt,
|
fromNode, toNode, amt,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
DestPayloadTLV: len(in.DestCustomRecords) != 0,
|
DestCustomRecords: record.CustomSet(in.DestCustomRecords),
|
||||||
CltvLimit: cltvLimit,
|
CltvLimit: cltvLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have any TLV records destined for the final hop, then we'll
|
// 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
|
// attempt to decode them now into a form that the router can more
|
||||||
// easily manipulate.
|
// easily manipulate.
|
||||||
destTlvRecords, err := UnmarshallCustomRecords(in.DestCustomRecords)
|
err = ValidateCustomRecords(in.DestCustomRecords)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -243,7 +241,7 @@ func (r *RouterBackend) QueryRoutes(ctx context.Context,
|
|||||||
// the route.
|
// the route.
|
||||||
route, err := r.FindRoute(
|
route, err := r.FindRoute(
|
||||||
sourcePubKey, targetPubKey, amt, restrictions,
|
sourcePubKey, targetPubKey, amt, restrictions,
|
||||||
destTlvRecords, finalCLTVDelta,
|
in.DestCustomRecords, finalCLTVDelta,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -347,11 +345,6 @@ 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{
|
resp.Hops[i] = &lnrpc.Hop{
|
||||||
ChanId: hop.ChannelID,
|
ChanId: hop.ChannelID,
|
||||||
ChanCapacity: int64(chanCapacity),
|
ChanCapacity: int64(chanCapacity),
|
||||||
@ -363,7 +356,7 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error)
|
|||||||
PubKey: hex.EncodeToString(
|
PubKey: hex.EncodeToString(
|
||||||
hop.PubKeyBytes[:],
|
hop.PubKeyBytes[:],
|
||||||
),
|
),
|
||||||
CustomRecords: tlvMap,
|
CustomRecords: hop.CustomRecords,
|
||||||
TlvPayload: !hop.LegacyPayload,
|
TlvPayload: !hop.LegacyPayload,
|
||||||
MppRecord: mpp,
|
MppRecord: mpp,
|
||||||
}
|
}
|
||||||
@ -373,24 +366,16 @@ func (r *RouterBackend) MarshallRoute(route *route.Route) (*lnrpc.Route, error)
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshallCustomRecords unmarshall rpc custom records to tlv records.
|
// ValidateCustomRecords checks that all custom records are in the custom type
|
||||||
func UnmarshallCustomRecords(rpcRecords map[uint64][]byte) ([]tlv.Record,
|
// range.
|
||||||
error) {
|
func ValidateCustomRecords(rpcRecords map[uint64][]byte) error {
|
||||||
|
for key := range rpcRecords {
|
||||||
if len(rpcRecords) == 0 {
|
if key < record.CustomTypeStart {
|
||||||
return nil, nil
|
return fmt.Errorf("no custom records with types "+
|
||||||
|
"below %v allowed", record.CustomTypeStart)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
tlvRecords := tlv.MapToRecords(rpcRecords)
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
return nil, fmt.Errorf("no custom records with types "+
|
|
||||||
"below %v allowed", hop.CustomTypeStart)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tlvRecords, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshallHopWithPubkey unmarshalls an rpc hop for which the pubkey has
|
// UnmarshallHopWithPubkey unmarshalls an rpc hop for which the pubkey has
|
||||||
@ -398,7 +383,7 @@ func UnmarshallCustomRecords(rpcRecords map[uint64][]byte) ([]tlv.Record,
|
|||||||
func UnmarshallHopWithPubkey(rpcHop *lnrpc.Hop, pubkey route.Vertex) (*route.Hop,
|
func UnmarshallHopWithPubkey(rpcHop *lnrpc.Hop, pubkey route.Vertex) (*route.Hop,
|
||||||
error) {
|
error) {
|
||||||
|
|
||||||
tlvRecords, err := UnmarshallCustomRecords(rpcHop.CustomRecords)
|
err := ValidateCustomRecords(rpcHop.CustomRecords)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -413,7 +398,7 @@ func UnmarshallHopWithPubkey(rpcHop *lnrpc.Hop, pubkey route.Vertex) (*route.Hop
|
|||||||
AmtToForward: lnwire.MilliSatoshi(rpcHop.AmtToForwardMsat),
|
AmtToForward: lnwire.MilliSatoshi(rpcHop.AmtToForwardMsat),
|
||||||
PubKeyBytes: pubkey,
|
PubKeyBytes: pubkey,
|
||||||
ChannelID: rpcHop.ChanId,
|
ChannelID: rpcHop.ChanId,
|
||||||
TLVRecords: tlvRecords,
|
CustomRecords: rpcHop.CustomRecords,
|
||||||
LegacyPayload: !rpcHop.TlvPayload,
|
LegacyPayload: !rpcHop.TlvPayload,
|
||||||
MPP: mpp,
|
MPP: mpp,
|
||||||
}, nil
|
}, nil
|
||||||
@ -541,12 +526,11 @@ func (r *RouterBackend) extractIntentFromSendRequest(
|
|||||||
return nil, errors.New("timeout_seconds must be specified")
|
return nil, errors.New("timeout_seconds must be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
payIntent.FinalDestRecords, err = UnmarshallCustomRecords(
|
err = ValidateCustomRecords(rpcPayReq.DestCustomRecords)
|
||||||
rpcPayReq.DestCustomRecords,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
payIntent.DestCustomRecords = rpcPayReq.DestCustomRecords
|
||||||
|
|
||||||
payIntent.PayAttemptTimeout = time.Second *
|
payIntent.PayAttemptTimeout = time.Second *
|
||||||
time.Duration(rpcPayReq.TimeoutSeconds)
|
time.Duration(rpcPayReq.TimeoutSeconds)
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
)
|
)
|
||||||
@ -92,7 +92,7 @@ func testQueryRoutes(t *testing.T, useMissionControl bool, useMsat bool) {
|
|||||||
|
|
||||||
findRoute := func(source, target route.Vertex,
|
findRoute := func(source, target route.Vertex,
|
||||||
amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams,
|
amt lnwire.MilliSatoshi, restrictions *routing.RestrictParams,
|
||||||
_ []tlv.Record,
|
_ record.CustomSet,
|
||||||
finalExpiry ...uint16) (*route.Route, error) {
|
finalExpiry ...uint16) (*route.Route, error) {
|
||||||
|
|
||||||
if int64(amt) != amtSat*1000 {
|
if int64(amt) != amtSat*1000 {
|
||||||
|
10
record/custom_records.go
Normal file
10
record/custom_records.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package record
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CustomTypeStart is the start of the custom tlv type range as defined
|
||||||
|
// in BOLT 01.
|
||||||
|
CustomTypeStart = 65536
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomSet stores a set of custom key/value pairs.
|
||||||
|
type CustomSet map[uint64][]byte
|
@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -100,7 +100,7 @@ type edgePolicyWithSource struct {
|
|||||||
func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
||||||
pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32,
|
pathEdges []*channeldb.ChannelEdgePolicy, currentHeight uint32,
|
||||||
finalCLTVDelta uint16,
|
finalCLTVDelta uint16,
|
||||||
finalDestRecords []tlv.Record) (*route.Route, error) {
|
destCustomRecords record.CustomSet) (*route.Route, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hops []*route.Hop
|
hops []*route.Hop
|
||||||
@ -198,8 +198,8 @@ func newRoute(amtToSend lnwire.MilliSatoshi, sourceVertex route.Vertex,
|
|||||||
|
|
||||||
// If this is the last hop, then we'll populate any TLV records
|
// If this is the last hop, then we'll populate any TLV records
|
||||||
// destined for it.
|
// destined for it.
|
||||||
if i == len(pathEdges)-1 && len(finalDestRecords) != 0 {
|
if i == len(pathEdges)-1 && len(destCustomRecords) != 0 {
|
||||||
currentHop.TLVRecords = finalDestRecords
|
currentHop.CustomRecords = destCustomRecords
|
||||||
}
|
}
|
||||||
|
|
||||||
hops = append([]*route.Hop{currentHop}, hops...)
|
hops = append([]*route.Hop{currentHop}, hops...)
|
||||||
@ -289,10 +289,9 @@ type RestrictParams struct {
|
|||||||
// all cltv expiry heights with the required final cltv delta.
|
// all cltv expiry heights with the required final cltv delta.
|
||||||
CltvLimit uint32
|
CltvLimit uint32
|
||||||
|
|
||||||
// DestPayloadTLV should be set to true if we need to drop off a TLV
|
// DestCustomRecords contains the custom records to drop off at the
|
||||||
// payload at the final hop in order to properly complete this payment
|
// final hop, if any.
|
||||||
// attempt.
|
DestCustomRecords record.CustomSet
|
||||||
DestPayloadTLV bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PathFindingConfig defines global parameters that control the trade-off in
|
// PathFindingConfig defines global parameters that control the trade-off in
|
||||||
@ -396,7 +395,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
|
|||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.DestPayloadTLV {
|
if len(r.DestCustomRecords) > 0 {
|
||||||
// Check if the target has TLV enabled
|
// Check if the target has TLV enabled
|
||||||
|
|
||||||
targetKey, err := btcec.ParsePubKey(target[:], btcec.S256())
|
targetKey, err := btcec.ParsePubKey(target[:], btcec.S256())
|
||||||
|
@ -97,6 +97,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|||||||
OutgoingChannelID: payment.OutgoingChannelID,
|
OutgoingChannelID: payment.OutgoingChannelID,
|
||||||
LastHop: payment.LastHop,
|
LastHop: payment.LastHop,
|
||||||
CltvLimit: cltvLimit,
|
CltvLimit: cltvLimit,
|
||||||
|
DestCustomRecords: payment.DestCustomRecords,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll also obtain a set of bandwidthHints from the lower layer for
|
// We'll also obtain a set of bandwidthHints from the lower layer for
|
||||||
@ -129,7 +130,7 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
|
|||||||
sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes)
|
sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes)
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
payment.Amount, sourceVertex, path, height, finalCltvDelta,
|
payment.Amount, sourceVertex, path, height, finalCltvDelta,
|
||||||
payment.FinalDestRecords,
|
payment.DestCustomRecords,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(roasbeef): return which edge/vertex didn't work
|
// TODO(roasbeef): return which edge/vertex didn't work
|
||||||
|
@ -107,9 +107,9 @@ type Hop struct {
|
|||||||
// only be set for the final hop.
|
// only be set for the final hop.
|
||||||
MPP *record.MPP
|
MPP *record.MPP
|
||||||
|
|
||||||
// TLVRecords if non-nil are a set of additional TLV records that
|
// CustomRecords if non-nil are a set of additional TLV records that
|
||||||
// should be included in the forwarding instructions for this node.
|
// should be included in the forwarding instructions for this node.
|
||||||
TLVRecords []tlv.Record
|
CustomRecords record.CustomSet
|
||||||
|
|
||||||
// LegacyPayload if true, then this signals that this node doesn't
|
// LegacyPayload if true, then this signals that this node doesn't
|
||||||
// understand the new TLV payload, so we must instead use the legacy
|
// understand the new TLV payload, so we must instead use the legacy
|
||||||
@ -165,7 +165,8 @@ func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append any custom types destined for this hop.
|
// Append any custom types destined for this hop.
|
||||||
records = append(records, h.TLVRecords...)
|
tlvRecords := tlv.MapToRecords(h.CustomRecords)
|
||||||
|
records = append(records, tlvRecords...)
|
||||||
|
|
||||||
// To ensure we produce a canonical stream, we'll sort the records
|
// To ensure we produce a canonical stream, we'll sort the records
|
||||||
// before encoding them as a stream in the hop payload.
|
// before encoding them as a stream in the hop payload.
|
||||||
|
@ -24,10 +24,10 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
|
"github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/multimutex"
|
"github.com/lightningnetwork/lnd/multimutex"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing/chainview"
|
"github.com/lightningnetwork/lnd/routing/chainview"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/ticker"
|
"github.com/lightningnetwork/lnd/ticker"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1401,7 +1401,7 @@ type routingMsg struct {
|
|||||||
// factoring in channel capacities and cumulative fees along the route.
|
// factoring in channel capacities and cumulative fees along the route.
|
||||||
func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
||||||
amt lnwire.MilliSatoshi, restrictions *RestrictParams,
|
amt lnwire.MilliSatoshi, restrictions *RestrictParams,
|
||||||
destTlvRecords []tlv.Record,
|
destCustomRecords record.CustomSet,
|
||||||
finalExpiry ...uint16) (*route.Route, error) {
|
finalExpiry ...uint16) (*route.Route, error) {
|
||||||
|
|
||||||
var finalCLTVDelta uint16
|
var finalCLTVDelta uint16
|
||||||
@ -1455,7 +1455,7 @@ func (r *ChannelRouter) FindRoute(source, target route.Vertex,
|
|||||||
// Create the route with absolute time lock values.
|
// Create the route with absolute time lock values.
|
||||||
route, err := newRoute(
|
route, err := newRoute(
|
||||||
amt, source, path, uint32(currentHeight), finalCLTVDelta,
|
amt, source, path, uint32(currentHeight), finalCLTVDelta,
|
||||||
destTlvRecords,
|
destCustomRecords,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1608,11 +1608,11 @@ type LightningPayment struct {
|
|||||||
// attempting to complete.
|
// attempting to complete.
|
||||||
PaymentRequest []byte
|
PaymentRequest []byte
|
||||||
|
|
||||||
// FinalDestRecords are TLV records that are to be sent to the final
|
// DestCustomRecords are TLV records that are to be sent to the final
|
||||||
// hop in the new onion payload format. If the destination does not
|
// hop in the new onion payload format. If the destination does not
|
||||||
// understand this new onion payload format, then the payment will
|
// understand this new onion payload format, then the payment will
|
||||||
// fail.
|
// fail.
|
||||||
FinalDestRecords []tlv.Record
|
DestCustomRecords record.CustomSet
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendPayment attempts to send a payment as described within the passed
|
// SendPayment attempts to send a payment as described within the passed
|
||||||
|
@ -50,11 +50,11 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/macaroons"
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
"github.com/lightningnetwork/lnd/monitoring"
|
"github.com/lightningnetwork/lnd/monitoring"
|
||||||
|
"github.com/lightningnetwork/lnd/record"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/lightningnetwork/lnd/signal"
|
"github.com/lightningnetwork/lnd/signal"
|
||||||
"github.com/lightningnetwork/lnd/sweep"
|
"github.com/lightningnetwork/lnd/sweep"
|
||||||
"github.com/lightningnetwork/lnd/tlv"
|
|
||||||
"github.com/lightningnetwork/lnd/watchtower"
|
"github.com/lightningnetwork/lnd/watchtower"
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
"github.com/lightningnetwork/lnd/zpay32"
|
||||||
"github.com/tv42/zbase32"
|
"github.com/tv42/zbase32"
|
||||||
@ -3103,7 +3103,7 @@ type rpcPaymentIntent struct {
|
|||||||
lastHop *route.Vertex
|
lastHop *route.Vertex
|
||||||
payReq []byte
|
payReq []byte
|
||||||
|
|
||||||
destTLV []tlv.Record
|
destCustomRecords record.CustomSet
|
||||||
|
|
||||||
route *route.Route
|
route *route.Route
|
||||||
}
|
}
|
||||||
@ -3162,12 +3162,13 @@ func (r *rpcServer) extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPayme
|
|||||||
}
|
}
|
||||||
payIntent.cltvLimit = cltvLimit
|
payIntent.cltvLimit = cltvLimit
|
||||||
|
|
||||||
payIntent.destTLV, err = routerrpc.UnmarshallCustomRecords(
|
err = routerrpc.ValidateCustomRecords(
|
||||||
rpcPayReq.DestCustomRecords,
|
rpcPayReq.DestCustomRecords,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return payIntent, err
|
return payIntent, err
|
||||||
}
|
}
|
||||||
|
payIntent.destCustomRecords = rpcPayReq.DestCustomRecords
|
||||||
|
|
||||||
validateDest := func(dest route.Vertex) error {
|
validateDest := func(dest route.Vertex) error {
|
||||||
if rpcPayReq.AllowSelfPayment {
|
if rpcPayReq.AllowSelfPayment {
|
||||||
@ -3352,7 +3353,7 @@ func (r *rpcServer) dispatchPaymentIntent(
|
|||||||
LastHop: payIntent.lastHop,
|
LastHop: payIntent.lastHop,
|
||||||
PaymentRequest: payIntent.payReq,
|
PaymentRequest: payIntent.payReq,
|
||||||
PayAttemptTimeout: routing.DefaultPayAttemptTimeout,
|
PayAttemptTimeout: routing.DefaultPayAttemptTimeout,
|
||||||
FinalDestRecords: payIntent.destTLV,
|
DestCustomRecords: payIntent.destCustomRecords,
|
||||||
}
|
}
|
||||||
|
|
||||||
preImage, route, routerErr = r.server.chanRouter.SendPayment(
|
preImage, route, routerErr = r.server.chanRouter.SendPayment(
|
||||||
|
Loading…
Reference in New Issue
Block a user