zpay32: modify an invoice's r
field to allow multiple routes
Before this commit, if an invoice encoded multiple `r` fields, we would decode them as one single route. We fix this by allowing an invoice to store multiple routes.
This commit is contained in:
parent
beb08b3887
commit
ea9e609ef0
@ -37,9 +37,9 @@ const (
|
|||||||
// with zeroes.
|
// with zeroes.
|
||||||
pubKeyBase32Len = 53
|
pubKeyBase32Len = 53
|
||||||
|
|
||||||
// routingInfoLen is the number of bytes needed to encode the extra
|
// hopHintLen is the number of bytes needed to encode the hop hint of a
|
||||||
// routing info of a single private route.
|
// single private route.
|
||||||
routingInfoLen = 51
|
hopHintLen = 51
|
||||||
|
|
||||||
// The following byte values correspond to the supported field types.
|
// The following byte values correspond to the supported field types.
|
||||||
// The field name is the character representing that 5-bit value in the
|
// The field name is the character representing that 5-bit value in the
|
||||||
@ -141,31 +141,12 @@ type Invoice struct {
|
|||||||
// Optional.
|
// Optional.
|
||||||
FallbackAddr btcutil.Address
|
FallbackAddr btcutil.Address
|
||||||
|
|
||||||
// RoutingInfo is one or more entries containing extra routing
|
// RouteHints represents one or more different route hints. Each route
|
||||||
// information for a private route to the target node.
|
// hint can be individually used to reach the destination. These usually
|
||||||
// Optional.
|
// represent private routes.
|
||||||
RoutingInfo []ExtraRoutingInfo
|
//
|
||||||
}
|
// NOTE: This is optional.
|
||||||
|
RouteHints [][]routing.HopHint
|
||||||
// ExtraRoutingInfo holds the information needed to route a payment along one
|
|
||||||
// private channel.
|
|
||||||
type ExtraRoutingInfo struct {
|
|
||||||
// PubKey is the public key of the node at the start of this channel.
|
|
||||||
PubKey *btcec.PublicKey
|
|
||||||
|
|
||||||
// ShortChanID is the channel ID of the channel.
|
|
||||||
ShortChanID uint64
|
|
||||||
|
|
||||||
// FeeBaseMsat is the base fee in millisatoshis required for routing
|
|
||||||
// along this channel.
|
|
||||||
FeeBaseMsat uint32
|
|
||||||
|
|
||||||
// FeeProportionalMillionths is the proportional fee in millionths of a
|
|
||||||
// satoshi required for routing along this channel.
|
|
||||||
FeeProportionalMillionths uint32
|
|
||||||
|
|
||||||
// CltvExpDelta is this channel's cltv expiry delta.
|
|
||||||
CltvExpDelta uint16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amount is a functional option that allows callers of NewInvoice to set the
|
// Amount is a functional option that allows callers of NewInvoice to set the
|
||||||
@ -231,12 +212,11 @@ func FallbackAddr(fallbackAddr btcutil.Address) func(*Invoice) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoutingInfo is a functional option that allows callers of NewInvoice to set
|
// RouteHint is a functional option that allows callers of NewInvoice to add
|
||||||
// one or more entries containing extra routing information for a private route
|
// one or more hop hints that represent a private route to the destination.
|
||||||
// to the target node.
|
func RouteHint(routeHint []routing.HopHint) func(*Invoice) {
|
||||||
func RoutingInfo(routingInfo []ExtraRoutingInfo) func(*Invoice) {
|
|
||||||
return func(i *Invoice) {
|
return func(i *Invoice) {
|
||||||
i.RoutingInfo = routingInfo
|
i.RouteHints = append(i.RouteHints, routeHint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,10 +505,19 @@ func validateInvoice(invoice *Invoice) error {
|
|||||||
return fmt.Errorf("neither description nor description hash set")
|
return fmt.Errorf("neither description nor description hash set")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can have at most 20 extra hops for routing.
|
// We'll restrict invoices to include up to 20 different private route
|
||||||
if len(invoice.RoutingInfo) > 20 {
|
// hints. We do this to avoid overly large invoices.
|
||||||
return fmt.Errorf("too many extra hops: %d",
|
if len(invoice.RouteHints) > 20 {
|
||||||
len(invoice.RoutingInfo))
|
return fmt.Errorf("too many private routes: %d",
|
||||||
|
len(invoice.RouteHints))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each route hint can have at most 20 hops.
|
||||||
|
for i, routeHint := range invoice.RouteHints {
|
||||||
|
if len(routeHint) > 20 {
|
||||||
|
return fmt.Errorf("route hint %d has too many extra "+
|
||||||
|
"hops: %d", i, len(routeHint))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that we support the field lengths.
|
// Check that we support the field lengths.
|
||||||
@ -666,13 +655,15 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
|
|
||||||
invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
|
invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
|
||||||
case fieldTypeR:
|
case fieldTypeR:
|
||||||
if invoice.RoutingInfo != nil {
|
// An `r` field can be included in an invoice multiple
|
||||||
// We skip the field if we have already seen a
|
// times, so we won't skip it if we have already seen
|
||||||
// supported one.
|
// one.
|
||||||
continue
|
routeHint, err := parseRouteHint(base32Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
invoice.RoutingInfo, err = parseRoutingInfo(base32Data)
|
invoice.RouteHints = append(invoice.RouteHints, routeHint)
|
||||||
default:
|
default:
|
||||||
// Ignore unknown type.
|
// Ignore unknown type.
|
||||||
}
|
}
|
||||||
@ -850,35 +841,38 @@ func parseFallbackAddr(data []byte, net *chaincfg.Params) (btcutil.Address, erro
|
|||||||
return addr, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseRoutingInfo converts the data (encoded in base32) into an array
|
// parseRouteHint converts the data (encoded in base32) into an array containing
|
||||||
// containing one or more entries of extra routing info.
|
// one or more routing hop hints that represent a single route hint.
|
||||||
func parseRoutingInfo(data []byte) ([]ExtraRoutingInfo, error) {
|
func parseRouteHint(data []byte) ([]routing.HopHint, error) {
|
||||||
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
|
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(base256Data)%routingInfoLen != 0 {
|
if len(base256Data)%hopHintLen != 0 {
|
||||||
return nil, fmt.Errorf("expected length multiple of %d bytes, got %d",
|
return nil, fmt.Errorf("expected length multiple of %d bytes, "+
|
||||||
routingInfoLen, len(base256Data))
|
"got %d", hopHintLen, len(base256Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
var routingInfo []ExtraRoutingInfo
|
var routeHint []routing.HopHint
|
||||||
info := ExtraRoutingInfo{}
|
|
||||||
for len(base256Data) > 0 {
|
for len(base256Data) > 0 {
|
||||||
info.PubKey, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
|
hopHint := routing.HopHint{}
|
||||||
|
hopHint.NodeID, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
info.ShortChanID = binary.BigEndian.Uint64(base256Data[33:41])
|
hopHint.ChannelID = binary.BigEndian.Uint64(base256Data[33:41])
|
||||||
info.FeeBaseMsat = binary.BigEndian.Uint32(base256Data[41:45])
|
hopHint.FeeBaseMSat = binary.BigEndian.Uint32(base256Data[41:45])
|
||||||
info.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
|
hopHint.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
|
||||||
info.CltvExpDelta = binary.BigEndian.Uint16(base256Data[49:51])
|
hopHint.CLTVExpiryDelta = binary.BigEndian.Uint16(base256Data[49:51])
|
||||||
routingInfo = append(routingInfo, info)
|
|
||||||
|
routeHint = append(routeHint, hopHint)
|
||||||
|
|
||||||
base256Data = base256Data[51:]
|
base256Data = base256Data[51:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return routingInfo, nil
|
return routeHint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeTaggedFields writes the non-nil tagged fields of the Invoice to the
|
// writeTaggedFields writes the non-nil tagged fields of the Invoice to the
|
||||||
@ -977,25 +971,37 @@ func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(invoice.RoutingInfo) > 0 {
|
for _, routeHint := range invoice.RouteHints {
|
||||||
// Each extra routing info is encoded using 51 bytes.
|
// Each hop hint is encoded using 51 bytes, so we'll make to
|
||||||
routingDataBase256 := make([]byte, 0, 51*len(invoice.RoutingInfo))
|
// sure to allocate enough space for the whole route hint.
|
||||||
for _, r := range invoice.RoutingInfo {
|
routeHintBase256 := make([]byte, 0, hopHintLen*len(routeHint))
|
||||||
base256 := make([]byte, 51)
|
|
||||||
copy(base256[:33], r.PubKey.SerializeCompressed())
|
for _, hopHint := range routeHint {
|
||||||
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
hopHintBase256 := make([]byte, hopHintLen)
|
||||||
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
copy(hopHintBase256[:33], hopHint.NodeID.SerializeCompressed())
|
||||||
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
binary.BigEndian.PutUint64(
|
||||||
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
hopHintBase256[33:41], hopHint.ChannelID,
|
||||||
routingDataBase256 = append(routingDataBase256, base256...)
|
)
|
||||||
|
binary.BigEndian.PutUint32(
|
||||||
|
hopHintBase256[41:45], hopHint.FeeBaseMSat,
|
||||||
|
)
|
||||||
|
binary.BigEndian.PutUint32(
|
||||||
|
hopHintBase256[45:49], hopHint.FeeProportionalMillionths,
|
||||||
|
)
|
||||||
|
binary.BigEndian.PutUint16(
|
||||||
|
hopHintBase256[49:51], hopHint.CLTVExpiryDelta,
|
||||||
|
)
|
||||||
|
routeHintBase256 = append(routeHintBase256, hopHintBase256...)
|
||||||
}
|
}
|
||||||
routingDataBase32, err := bech32.ConvertBits(routingDataBase256,
|
|
||||||
8, 5, true)
|
routeHintBase32, err := bech32.ConvertBits(
|
||||||
|
routeHintBase256, 8, 5, true,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writeTaggedField(bufferBase32, fieldTypeR, routingDataBase32)
|
err = writeTaggedField(bufferBase32, fieldTypeR, routeHintBase32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/chaincfg"
|
"github.com/roasbeef/btcd/chaincfg"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
@ -706,18 +707,18 @@ func TestParseFallbackAddr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestParseRoutingInfo checks that the routing info is properly parsed.
|
// TestParseRouteHint checks that the routing info is properly parsed.
|
||||||
func TestParseRoutingInfo(t *testing.T) {
|
func TestParseRouteHint(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var testSingleHopData []byte
|
var testSingleHopData []byte
|
||||||
for _, r := range testSingleHop {
|
for _, r := range testSingleHop {
|
||||||
base256 := make([]byte, 51)
|
base256 := make([]byte, 51)
|
||||||
copy(base256[:33], r.PubKey.SerializeCompressed())
|
copy(base256[:33], r.NodeID.SerializeCompressed())
|
||||||
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
binary.BigEndian.PutUint64(base256[33:41], r.ChannelID)
|
||||||
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMSat)
|
||||||
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
||||||
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
binary.BigEndian.PutUint16(base256[49:51], r.CLTVExpiryDelta)
|
||||||
testSingleHopData = append(testSingleHopData, base256...)
|
testSingleHopData = append(testSingleHopData, base256...)
|
||||||
}
|
}
|
||||||
testSingleHopData, _ = bech32.ConvertBits(testSingleHopData, 8, 5, true)
|
testSingleHopData, _ = bech32.ConvertBits(testSingleHopData, 8, 5, true)
|
||||||
@ -725,11 +726,11 @@ func TestParseRoutingInfo(t *testing.T) {
|
|||||||
var testDoubleHopData []byte
|
var testDoubleHopData []byte
|
||||||
for _, r := range testDoubleHop {
|
for _, r := range testDoubleHop {
|
||||||
base256 := make([]byte, 51)
|
base256 := make([]byte, 51)
|
||||||
copy(base256[:33], r.PubKey.SerializeCompressed())
|
copy(base256[:33], r.NodeID.SerializeCompressed())
|
||||||
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
binary.BigEndian.PutUint64(base256[33:41], r.ChannelID)
|
||||||
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMSat)
|
||||||
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
||||||
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
binary.BigEndian.PutUint16(base256[49:51], r.CLTVExpiryDelta)
|
||||||
testDoubleHopData = append(testDoubleHopData, base256...)
|
testDoubleHopData = append(testDoubleHopData, base256...)
|
||||||
}
|
}
|
||||||
testDoubleHopData, _ = bech32.ConvertBits(testDoubleHopData, 8, 5, true)
|
testDoubleHopData, _ = bech32.ConvertBits(testDoubleHopData, 8, 5, true)
|
||||||
@ -737,7 +738,7 @@ func TestParseRoutingInfo(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
data []byte
|
data []byte
|
||||||
valid bool
|
valid bool
|
||||||
result []ExtraRoutingInfo
|
result []routing.HopHint
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
data: []byte{0x0, 0x0, 0x0, 0x0},
|
data: []byte{0x0, 0x0, 0x0, 0x0},
|
||||||
@ -746,7 +747,7 @@ func TestParseRoutingInfo(t *testing.T) {
|
|||||||
{
|
{
|
||||||
data: []byte{},
|
data: []byte{},
|
||||||
valid: true,
|
valid: true,
|
||||||
result: []ExtraRoutingInfo{},
|
result: []routing.HopHint{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: testSingleHopData,
|
data: testSingleHopData,
|
||||||
@ -765,13 +766,13 @@ func TestParseRoutingInfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
routingInfo, err := parseRoutingInfo(test.data)
|
routeHint, err := parseRouteHint(test.data)
|
||||||
if (err == nil) != test.valid {
|
if (err == nil) != test.valid {
|
||||||
t.Errorf("routing info decoding test %d failed: %v", i, err)
|
t.Errorf("routing info decoding test %d failed: %v", i, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if test.valid {
|
if test.valid {
|
||||||
if err := compareRoutingInfos(test.result, routingInfo); err != nil {
|
if err := compareRouteHints(test.result, routeHint); err != nil {
|
||||||
t.Fatalf("test %d failed decoding routing info: %v", i, err)
|
t.Fatalf("test %d failed decoding routing info: %v", i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/chaincfg"
|
"github.com/roasbeef/btcd/chaincfg"
|
||||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
@ -47,34 +48,34 @@ var (
|
|||||||
testAddrMainnetP2WPKH, _ = btcutil.DecodeAddress("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", &chaincfg.MainNetParams)
|
testAddrMainnetP2WPKH, _ = btcutil.DecodeAddress("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", &chaincfg.MainNetParams)
|
||||||
testAddrMainnetP2WSH, _ = btcutil.DecodeAddress("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", &chaincfg.MainNetParams)
|
testAddrMainnetP2WSH, _ = btcutil.DecodeAddress("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", &chaincfg.MainNetParams)
|
||||||
|
|
||||||
testRoutingInfoPubkeyBytes, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
testHopHintPubkeyBytes1, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
||||||
testRoutingInfoPubkey, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes, btcec.S256())
|
testHopHintPubkey1, _ = btcec.ParsePubKey(testHopHintPubkeyBytes1, btcec.S256())
|
||||||
testRoutingInfoPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
testHopHintPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
||||||
testRoutingInfoPubkey2, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes2, btcec.S256())
|
testHopHintPubkey2, _ = btcec.ParsePubKey(testHopHintPubkeyBytes2, btcec.S256())
|
||||||
|
|
||||||
testSingleHop = []ExtraRoutingInfo{
|
testSingleHop = []routing.HopHint{
|
||||||
{
|
{
|
||||||
PubKey: testRoutingInfoPubkey,
|
NodeID: testHopHintPubkey1,
|
||||||
ShortChanID: 0x0102030405060708,
|
ChannelID: 0x0102030405060708,
|
||||||
FeeBaseMsat: 0,
|
FeeBaseMSat: 0,
|
||||||
FeeProportionalMillionths: 20,
|
FeeProportionalMillionths: 20,
|
||||||
CltvExpDelta: 3,
|
CLTVExpiryDelta: 3,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
testDoubleHop = []ExtraRoutingInfo{
|
testDoubleHop = []routing.HopHint{
|
||||||
{
|
{
|
||||||
PubKey: testRoutingInfoPubkey,
|
NodeID: testHopHintPubkey1,
|
||||||
ShortChanID: 0x0102030405060708,
|
ChannelID: 0x0102030405060708,
|
||||||
FeeBaseMsat: 1,
|
FeeBaseMSat: 1,
|
||||||
FeeProportionalMillionths: 20,
|
FeeProportionalMillionths: 20,
|
||||||
CltvExpDelta: 3,
|
CLTVExpiryDelta: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKey: testRoutingInfoPubkey2,
|
NodeID: testHopHintPubkey2,
|
||||||
ShortChanID: 0x030405060708090a,
|
ChannelID: 0x030405060708090a,
|
||||||
FeeBaseMsat: 2,
|
FeeBaseMSat: 2,
|
||||||
FeeProportionalMillionths: 30,
|
FeeProportionalMillionths: 30,
|
||||||
CltvExpDelta: 4,
|
CLTVExpiryDelta: 4,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +414,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
DescriptionHash: &testDescriptionHash,
|
DescriptionHash: &testDescriptionHash,
|
||||||
Destination: testPubKey,
|
Destination: testPubKey,
|
||||||
FallbackAddr: testRustyAddr,
|
FallbackAddr: testRustyAddr,
|
||||||
RoutingInfo: testSingleHop,
|
RouteHints: [][]routing.HopHint{testSingleHop},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
@ -436,7 +437,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
DescriptionHash: &testDescriptionHash,
|
DescriptionHash: &testDescriptionHash,
|
||||||
Destination: testPubKey,
|
Destination: testPubKey,
|
||||||
FallbackAddr: testRustyAddr,
|
FallbackAddr: testRustyAddr,
|
||||||
RoutingInfo: testDoubleHop,
|
RouteHints: [][]routing.HopHint{testDoubleHop},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
@ -680,7 +681,7 @@ func TestNewInvoice(t *testing.T) {
|
|||||||
Amount(testMillisat20mBTC),
|
Amount(testMillisat20mBTC),
|
||||||
DescriptionHash(testDescriptionHash),
|
DescriptionHash(testDescriptionHash),
|
||||||
FallbackAddr(testRustyAddr),
|
FallbackAddr(testRustyAddr),
|
||||||
RoutingInfo(testDoubleHop),
|
RouteHint(testDoubleHop),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
valid: true,
|
valid: true,
|
||||||
@ -802,7 +803,19 @@ func compareInvoices(expected, actual *Invoice) error {
|
|||||||
expected.FallbackAddr, actual.FallbackAddr)
|
expected.FallbackAddr, actual.FallbackAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return compareRoutingInfos(expected.RoutingInfo, actual.RoutingInfo)
|
if len(expected.RouteHints) != len(actual.RouteHints) {
|
||||||
|
return fmt.Errorf("expected %d RouteHints, got %d",
|
||||||
|
len(expected.RouteHints), len(actual.RouteHints))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, routeHint := range expected.RouteHints {
|
||||||
|
err := compareRouteHints(routeHint, actual.RouteHints[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func comparePubkeys(a, b *btcec.PublicKey) bool {
|
func comparePubkeys(a, b *btcec.PublicKey) bool {
|
||||||
@ -831,36 +844,36 @@ func compareHashes(a, b *[32]byte) bool {
|
|||||||
return bytes.Equal(a[:], b[:])
|
return bytes.Equal(a[:], b[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareRoutingInfos(a, b []ExtraRoutingInfo) error {
|
func compareRouteHints(a, b []routing.HopHint) error {
|
||||||
if len(a) != len(b) {
|
if len(a) != len(b) {
|
||||||
return fmt.Errorf("expected len routingInfo %d, got %d",
|
return fmt.Errorf("expected len routingInfo %d, got %d",
|
||||||
len(a), len(b))
|
len(a), len(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(a); i++ {
|
for i := 0; i < len(a); i++ {
|
||||||
if !comparePubkeys(a[i].PubKey, b[i].PubKey) {
|
if !comparePubkeys(a[i].NodeID, b[i].NodeID) {
|
||||||
return fmt.Errorf("expected routingInfo pubkey %x, "+
|
return fmt.Errorf("expected routeHint nodeID %x, "+
|
||||||
"got %x", a[i].PubKey, b[i].PubKey)
|
"got %x", a[i].NodeID, b[i].NodeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a[i].ShortChanID != b[i].ShortChanID {
|
if a[i].ChannelID != b[i].ChannelID {
|
||||||
return fmt.Errorf("expected routingInfo shortChanID "+
|
return fmt.Errorf("expected routeHint channelID "+
|
||||||
"%d, got %d", a[i].ShortChanID, b[i].ShortChanID)
|
"%d, got %d", a[i].ChannelID, b[i].ChannelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a[i].FeeBaseMsat != b[i].FeeBaseMsat {
|
if a[i].FeeBaseMSat != b[i].FeeBaseMSat {
|
||||||
return fmt.Errorf("expected routingInfo feeBaseMsat %d, got %d",
|
return fmt.Errorf("expected routeHint feeBaseMsat %d, got %d",
|
||||||
a[i].FeeBaseMsat, b[i].FeeBaseMsat)
|
a[i].FeeBaseMSat, b[i].FeeBaseMSat)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a[i].FeeProportionalMillionths != b[i].FeeProportionalMillionths {
|
if a[i].FeeProportionalMillionths != b[i].FeeProportionalMillionths {
|
||||||
return fmt.Errorf("expected routingInfo feeProportionalMillionths %d, got %d",
|
return fmt.Errorf("expected routeHint feeProportionalMillionths %d, got %d",
|
||||||
a[i].FeeProportionalMillionths, b[i].FeeProportionalMillionths)
|
a[i].FeeProportionalMillionths, b[i].FeeProportionalMillionths)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a[i].CltvExpDelta != b[i].CltvExpDelta {
|
if a[i].CLTVExpiryDelta != b[i].CLTVExpiryDelta {
|
||||||
return fmt.Errorf("expected routingInfo cltvExpDelta "+
|
return fmt.Errorf("expected routeHint cltvExpiryDelta "+
|
||||||
"%d, got %d", a[i].CltvExpDelta, b[i].CltvExpDelta)
|
"%d, got %d", a[i].CLTVExpiryDelta, b[i].CLTVExpiryDelta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user