Merge pull request #573 from wilmerpaulino/parse-data-fields
invoice: refactor parsing data tagged fields and add unit tests
This commit is contained in:
commit
8cce2ad630
@ -37,6 +37,10 @@ const (
|
|||||||
// with zeroes.
|
// with zeroes.
|
||||||
pubKeyBase32Len = 53
|
pubKeyBase32Len = 53
|
||||||
|
|
||||||
|
// routingInfoLen is the number of bytes needed to encode the extra
|
||||||
|
// routing info of a single private route.
|
||||||
|
routingInfoLen = 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
|
||||||
// bech32 string.
|
// bech32 string.
|
||||||
@ -557,8 +561,7 @@ func parseData(invoice *Invoice, data []byte, net *chaincfg.Params) error {
|
|||||||
return fmt.Errorf("data too short: %d", len(data))
|
return fmt.Errorf("data too short: %d", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp: 35 bits, 7 groups.
|
t, err := parseTimestamp(data[:timestampBase32Len])
|
||||||
t, err := base32ToUint64(data[:7])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -575,7 +578,7 @@ func parseData(invoice *Invoice, data []byte, net *chaincfg.Params) error {
|
|||||||
|
|
||||||
// parseTimestamp converts a 35-bit timestamp (encoded in base32) to uint64.
|
// parseTimestamp converts a 35-bit timestamp (encoded in base32) to uint64.
|
||||||
func parseTimestamp(data []byte) (uint64, error) {
|
func parseTimestamp(data []byte) (uint64, error) {
|
||||||
if len(data) != 7 {
|
if len(data) != timestampBase32Len {
|
||||||
return 0, fmt.Errorf("timestamp must be 35 bits, was %d",
|
return 0, fmt.Errorf("timestamp must be 35 bits, was %d",
|
||||||
len(data)*5)
|
len(data)*5)
|
||||||
}
|
}
|
||||||
@ -588,7 +591,7 @@ func parseTimestamp(data []byte) (uint64, error) {
|
|||||||
func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) error {
|
func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) error {
|
||||||
index := 0
|
index := 0
|
||||||
for {
|
for {
|
||||||
// If less than 3 groups less, it cannot possibly contain more
|
// If there are less than 3 groups to read, there cannot be more
|
||||||
// interesting information, as we need the type (1 group) and
|
// interesting information, as we need the type (1 group) and
|
||||||
// length (2 groups).
|
// length (2 groups).
|
||||||
if len(fields)-index < 3 {
|
if len(fields)-index < 3 {
|
||||||
@ -596,7 +599,10 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
typ := fields[index]
|
typ := fields[index]
|
||||||
dataLength := uint16(fields[index+1]<<5) | uint16(fields[index+2])
|
dataLength, err := parseFieldDataLength(fields[index+1 : index+3])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// If we don't have enough field data left to read this length,
|
// If we don't have enough field data left to read this length,
|
||||||
// return error.
|
// return error.
|
||||||
@ -616,17 +622,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if dataLength != hashBase32Len {
|
invoice.PaymentHash, err = parsePaymentHash(base32Data)
|
||||||
// Skipping unknown field length.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hash, err := bech32.ConvertBits(base32Data, 5, 8, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var pHash [32]byte
|
|
||||||
copy(pHash[:], hash[:])
|
|
||||||
invoice.PaymentHash = &pHash
|
|
||||||
case fieldTypeD:
|
case fieldTypeD:
|
||||||
if invoice.Description != nil {
|
if invoice.Description != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -634,13 +630,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
base256Data, err := bech32.ConvertBits(base32Data, 5, 8,
|
invoice.Description, err = parseDescription(base32Data)
|
||||||
false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
desc := string(base256Data)
|
|
||||||
invoice.Description = &desc
|
|
||||||
case fieldTypeN:
|
case fieldTypeN:
|
||||||
if invoice.Destination != nil {
|
if invoice.Destination != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -648,21 +638,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(base32Data) != pubKeyBase32Len {
|
invoice.Destination, err = parseDestination(base32Data)
|
||||||
// Skip unknown length.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
base256Data, err := bech32.ConvertBits(base32Data, 5, 8,
|
|
||||||
false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
invoice.Destination, err = btcec.ParsePubKey(base256Data,
|
|
||||||
btcec.S256())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case fieldTypeH:
|
case fieldTypeH:
|
||||||
if invoice.DescriptionHash != nil {
|
if invoice.DescriptionHash != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -670,17 +646,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(base32Data) != hashBase32Len {
|
invoice.DescriptionHash, err = parseDescriptionHash(base32Data)
|
||||||
// Skip unknown length.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hash, err := bech32.ConvertBits(base32Data, 5, 8, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var dHash [32]byte
|
|
||||||
copy(dHash[:], hash[:])
|
|
||||||
invoice.DescriptionHash = &dHash
|
|
||||||
case fieldTypeX:
|
case fieldTypeX:
|
||||||
if invoice.expiry != nil {
|
if invoice.expiry != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -688,12 +654,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
exp, err := base32ToUint64(base32Data)
|
invoice.expiry, err = parseExpiry(base32Data)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dur := time.Duration(exp) * time.Second
|
|
||||||
invoice.expiry = &dur
|
|
||||||
case fieldTypeC:
|
case fieldTypeC:
|
||||||
if invoice.minFinalCLTVExpiry != nil {
|
if invoice.minFinalCLTVExpiry != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -701,11 +662,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
expiry, err := base32ToUint64(base32Data)
|
invoice.minFinalCLTVExpiry, err = parseMinFinalCLTVExpiry(base32Data)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
invoice.minFinalCLTVExpiry = &expiry
|
|
||||||
case fieldTypeF:
|
case fieldTypeF:
|
||||||
if invoice.FallbackAddr != nil {
|
if invoice.FallbackAddr != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -713,56 +670,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var addr btcutil.Address
|
invoice.FallbackAddr, err = parseFallbackAddr(base32Data, net)
|
||||||
version := base32Data[0]
|
|
||||||
switch version {
|
|
||||||
case 0:
|
|
||||||
witness, err := bech32.ConvertBits(
|
|
||||||
base32Data[1:], 5, 8, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch len(witness) {
|
|
||||||
case 20:
|
|
||||||
addr, err = btcutil.NewAddressWitnessPubKeyHash(
|
|
||||||
witness, net)
|
|
||||||
case 32:
|
|
||||||
addr, err = btcutil.NewAddressWitnessScriptHash(
|
|
||||||
witness, net)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown witness "+
|
|
||||||
"program length: %d", len(witness))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case 17:
|
|
||||||
pkHash, err := bech32.ConvertBits(base32Data[1:],
|
|
||||||
5, 8, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
addr, err = btcutil.NewAddressPubKeyHash(pkHash,
|
|
||||||
net)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case 18:
|
|
||||||
scriptHash, err := bech32.ConvertBits(
|
|
||||||
base32Data[1:], 5, 8, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
addr, err = btcutil.NewAddressScriptHashFromHash(
|
|
||||||
scriptHash, net)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// Skipping unknown witness version.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
invoice.FallbackAddr = addr
|
|
||||||
case fieldTypeR:
|
case fieldTypeR:
|
||||||
if invoice.RoutingInfo != nil {
|
if invoice.RoutingInfo != nil {
|
||||||
// We skip the field if we have already seen a
|
// We skip the field if we have already seen a
|
||||||
@ -770,39 +678,215 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
base256Data, err := bech32.ConvertBits(base32Data, 5, 8,
|
invoice.RoutingInfo, err = parseRoutingInfo(base32Data)
|
||||||
false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for len(base256Data) > 0 {
|
|
||||||
info := ExtraRoutingInfo{}
|
|
||||||
info.PubKey, err = btcec.ParsePubKey(
|
|
||||||
base256Data[:33], btcec.S256())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
info.ShortChanID = binary.BigEndian.Uint64(
|
|
||||||
base256Data[33:41])
|
|
||||||
info.FeeBaseMsat = binary.BigEndian.Uint32(
|
|
||||||
base256Data[41:45])
|
|
||||||
info.FeeProportionalMillionths = binary.BigEndian.Uint32(
|
|
||||||
base256Data[45:49])
|
|
||||||
info.CltvExpDelta = binary.BigEndian.Uint16(
|
|
||||||
base256Data[49:51])
|
|
||||||
invoice.RoutingInfo = append(
|
|
||||||
invoice.RoutingInfo, info)
|
|
||||||
base256Data = base256Data[51:]
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
// Ignore unknown type.
|
// Ignore unknown type.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if there was an error from parsing any of the tagged
|
||||||
|
// fields and return it.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseFieldDataLength converts the two byte slice into a uint16.
|
||||||
|
func parseFieldDataLength(data []byte) (uint16, error) {
|
||||||
|
if len(data) != 2 {
|
||||||
|
return 0, fmt.Errorf("data length must be 2 bytes, was %d",
|
||||||
|
len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint16(data[0])<<5 | uint16(data[1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePaymentHash converts a 256-bit payment hash (encoded in base32)
|
||||||
|
// to *[32]byte.
|
||||||
|
func parsePaymentHash(data []byte) (*[32]byte, error) {
|
||||||
|
var paymentHash [32]byte
|
||||||
|
|
||||||
|
// As BOLT-11 states, a reader must skip over the payment hash field if
|
||||||
|
// it does not have a length of 52, so avoid returning an error.
|
||||||
|
if len(data) != hashBase32Len {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(paymentHash[:], hash[:])
|
||||||
|
|
||||||
|
return &paymentHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDescription converts the data (encoded in base32) into a string to use
|
||||||
|
// as the description.
|
||||||
|
func parseDescription(data []byte) (*string, error) {
|
||||||
|
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
description := string(base256Data)
|
||||||
|
|
||||||
|
return &description, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDestination converts the data (encoded in base32) into a 33-byte public
|
||||||
|
// key of the payee node.
|
||||||
|
func parseDestination(data []byte) (*btcec.PublicKey, error) {
|
||||||
|
// As BOLT-11 states, a reader must skip over the destination field
|
||||||
|
// if it does not have a length of 53, so avoid returning an error.
|
||||||
|
if len(data) != pubKeyBase32Len {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcec.ParsePubKey(base256Data, btcec.S256())
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDescriptionHash converts a 256-bit description hash (encoded in base32)
|
||||||
|
// to *[32]byte.
|
||||||
|
func parseDescriptionHash(data []byte) (*[32]byte, error) {
|
||||||
|
var descriptionHash [32]byte
|
||||||
|
|
||||||
|
// As BOLT-11 states, a reader must skip over the description hash field
|
||||||
|
// if it does not have a length of 52, so avoid returning an error.
|
||||||
|
if len(data) != hashBase32Len {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(descriptionHash[:], hash[:])
|
||||||
|
|
||||||
|
return &descriptionHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseExpiry converts the data (encoded in base32) into the expiry time.
|
||||||
|
func parseExpiry(data []byte) (*time.Duration, error) {
|
||||||
|
expiry, err := base32ToUint64(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := time.Duration(expiry) * time.Second
|
||||||
|
|
||||||
|
return &duration, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseMinFinalCLTVExpiry converts the data (encoded in base32) into a uint64
|
||||||
|
// to use as the minFinalCLTVExpiry.
|
||||||
|
func parseMinFinalCLTVExpiry(data []byte) (*uint64, error) {
|
||||||
|
expiry, err := base32ToUint64(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &expiry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFallbackAddr converts the data (encoded in base32) into a fallback
|
||||||
|
// on-chain address.
|
||||||
|
func parseFallbackAddr(data []byte, net *chaincfg.Params) (btcutil.Address, error) {
|
||||||
|
// Checks if the data is empty or contains a version without an address.
|
||||||
|
if len(data) < 2 {
|
||||||
|
return nil, fmt.Errorf("empty fallback address field")
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr btcutil.Address
|
||||||
|
|
||||||
|
version := data[0]
|
||||||
|
switch version {
|
||||||
|
case 0:
|
||||||
|
witness, err := bech32.ConvertBits(data[1:], 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(witness) {
|
||||||
|
case 20:
|
||||||
|
addr, err = btcutil.NewAddressWitnessPubKeyHash(witness, net)
|
||||||
|
case 32:
|
||||||
|
addr, err = btcutil.NewAddressWitnessScriptHash(witness, net)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown witness program length %d",
|
||||||
|
len(witness))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case 17:
|
||||||
|
pubKeyHash, err := bech32.ConvertBits(data[1:], 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err = btcutil.NewAddressPubKeyHash(pubKeyHash, net)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case 18:
|
||||||
|
scriptHash, err := bech32.ConvertBits(data[1:], 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err = btcutil.NewAddressScriptHashFromHash(scriptHash, net)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Ignore unknown version.
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRoutingInfo converts the data (encoded in base32) into an array
|
||||||
|
// containing one or more entries of extra routing info.
|
||||||
|
func parseRoutingInfo(data []byte) ([]ExtraRoutingInfo, error) {
|
||||||
|
base256Data, err := bech32.ConvertBits(data, 5, 8, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(base256Data)%routingInfoLen != 0 {
|
||||||
|
return nil, fmt.Errorf("expected length multiple of %d bytes, got %d",
|
||||||
|
routingInfoLen, len(base256Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
var routingInfo []ExtraRoutingInfo
|
||||||
|
info := ExtraRoutingInfo{}
|
||||||
|
for len(base256Data) > 0 {
|
||||||
|
info.PubKey, err = btcec.ParsePubKey(base256Data[:33], btcec.S256())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
info.ShortChanID = binary.BigEndian.Uint64(base256Data[33:41])
|
||||||
|
info.FeeBaseMsat = binary.BigEndian.Uint32(base256Data[41:45])
|
||||||
|
info.FeeProportionalMillionths = binary.BigEndian.Uint32(base256Data[45:49])
|
||||||
|
info.CltvExpDelta = binary.BigEndian.Uint16(base256Data[49:51])
|
||||||
|
routingInfo = append(routingInfo, info)
|
||||||
|
base256Data = base256Data[51:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return routingInfo, 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
|
||||||
// base32 buffer.
|
// base32 buffer.
|
||||||
func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
|
func writeTaggedFields(bufferBase32 *bytes.Buffer, invoice *Invoice) error {
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
package zpay32
|
package zpay32
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"github.com/roasbeef/btcd/chaincfg"
|
||||||
|
"github.com/roasbeef/btcutil"
|
||||||
|
"github.com/roasbeef/btcutil/bech32"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestDecodeAmount ensures that the amount string in the hrp of the Invoice
|
// TestDecodeAmount ensures that the amount string in the hrp of the Invoice
|
||||||
@ -118,11 +125,11 @@ func TestDecodeAmount(t *testing.T) {
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
sat, err := decodeAmount(test.amount)
|
sat, err := decodeAmount(test.amount)
|
||||||
if (err == nil) != test.valid {
|
if (err == nil) != test.valid {
|
||||||
t.Errorf("Amount decoding test %d failed: %v", i, err)
|
t.Errorf("amount decoding test %d failed: %v", i, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if test.valid && sat != test.result {
|
if test.valid && sat != test.result {
|
||||||
t.Fatalf("%d) failed decoding amount, expected %v, "+
|
t.Fatalf("test %d failed decoding amount, expected %v, "+
|
||||||
"got %v", i, test.result, sat)
|
"got %v", i, test.result, sat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,11 +200,11 @@ func TestEncodeAmount(t *testing.T) {
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
shortened, err := encodeAmount(test.msat)
|
shortened, err := encodeAmount(test.msat)
|
||||||
if (err == nil) != test.valid {
|
if (err == nil) != test.valid {
|
||||||
t.Errorf("Amount encoding test %d failed: %v", i, err)
|
t.Errorf("amount encoding test %d failed: %v", i, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if test.valid && shortened != test.result {
|
if test.valid && shortened != test.result {
|
||||||
t.Fatalf("%d) failed encoding amount, expected %v, "+
|
t.Fatalf("test %d failed encoding amount, expected %v, "+
|
||||||
"got %v", i, test.result, shortened)
|
"got %v", i, test.result, shortened)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,13 +237,505 @@ func TestParseTimestamp(t *testing.T) {
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
time, err := parseTimestamp(test.data)
|
time, err := parseTimestamp(test.data)
|
||||||
if (err == nil) != test.valid {
|
if (err == nil) != test.valid {
|
||||||
t.Errorf("Data decoding test %d failed: %v", i, err)
|
t.Errorf("timestamp decoding test %d failed: %v", i, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if test.valid && time != test.result {
|
if test.valid && time != test.result {
|
||||||
t.Errorf("Timestamp decoding test %d failed: expected "+
|
t.Fatalf("test %d failed decoding timestamp: "+
|
||||||
"timestamp %d, got %d", i, test.result, time)
|
"expected %d, got %d",
|
||||||
|
i, test.result, time)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestParseFieldDataLength checks that the 16 bit length is properly parsed.
|
||||||
|
func TestParseFieldDataLength(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result uint16
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: false, // empty data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0},
|
||||||
|
valid: false, // data too short
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x0, 0x0},
|
||||||
|
valid: false, // data too long
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x0},
|
||||||
|
valid: true,
|
||||||
|
result: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x1f, 0x1f},
|
||||||
|
valid: true,
|
||||||
|
result: 1023,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// The first byte is <= 3 bits long.
|
||||||
|
data: []byte{0x1, 0x2},
|
||||||
|
valid: true,
|
||||||
|
result: 34,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// The first byte is > 3 bits long.
|
||||||
|
data: []byte{0xa, 0x0},
|
||||||
|
valid: true,
|
||||||
|
result: 320,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
length, err := parseFieldDataLength(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("field data length decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && length != test.result {
|
||||||
|
t.Fatalf("test %d failed decoding field data length: "+
|
||||||
|
"expected %d, got %d",
|
||||||
|
i, test.result, length)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParsePaymentHash checks that the payment hash is properly parsed.
|
||||||
|
// If the data does not have a length of 52 bytes, we skip over parsing the
|
||||||
|
// field and do not return an error.
|
||||||
|
func TestParsePaymentHash(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testPaymentHashData, _ := bech32.ConvertBits(testPaymentHash[:], 8, 5, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result *[32]byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 52 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 52 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testPaymentHashData,
|
||||||
|
valid: true,
|
||||||
|
result: &testPaymentHash,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: append(testPaymentHashData, 0x0),
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 52 bytes
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
paymentHash, err := parsePaymentHash(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("payment hash decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && !compareHashes(paymentHash, test.result) {
|
||||||
|
t.Fatalf("test %d failed decoding payment hash: "+
|
||||||
|
"expected %x, got %x",
|
||||||
|
i, *test.result, *paymentHash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseDescription checks that the description is properly parsed.
|
||||||
|
func TestParseDescription(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCupOfCoffeeData, _ := bech32.ConvertBits([]byte(testCupOfCoffee), 8, 5, true)
|
||||||
|
testPleaseConsiderData, _ := bech32.ConvertBits([]byte(testPleaseConsider), 8, 5, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result *string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: &testEmptyString,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testCupOfCoffeeData,
|
||||||
|
valid: true,
|
||||||
|
result: &testCupOfCoffee,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testPleaseConsiderData,
|
||||||
|
valid: true,
|
||||||
|
result: &testPleaseConsider,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
description, err := parseDescription(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("description decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && !reflect.DeepEqual(description, test.result) {
|
||||||
|
t.Fatalf("test %d failed decoding description: "+
|
||||||
|
"expected \"%s\", got \"%s\"",
|
||||||
|
i, *test.result, *description)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseDestination checks that the destination is properly parsed.
|
||||||
|
// If the data does not have a length of 53 bytes, we skip over parsing the
|
||||||
|
// field and do not return an error.
|
||||||
|
func TestParseDestination(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testPubKeyData, _ := bech32.ConvertBits(testPubKey.SerializeCompressed(), 8, 5, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result *btcec.PublicKey
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 53 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 53 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testPubKeyData,
|
||||||
|
valid: true,
|
||||||
|
result: testPubKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: append(testPubKeyData, 0x0),
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 53 bytes
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
destination, err := parseDestination(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("destination decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && !comparePubkeys(destination, test.result) {
|
||||||
|
t.Fatalf("test %d failed decoding destination: "+
|
||||||
|
"expected %x, got %x",
|
||||||
|
i, *test.result, *destination)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseDescriptionHash checks that the description hash is properly parsed.
|
||||||
|
// If the data does not have a length of 52 bytes, we skip over parsing the
|
||||||
|
// field and do not return an error.
|
||||||
|
func TestParseDescriptionHash(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testDescriptionHashData, _ := bech32.ConvertBits(testDescriptionHash[:], 8, 5, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result *[32]byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 52 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 52 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testDescriptionHashData,
|
||||||
|
valid: true,
|
||||||
|
result: &testDescriptionHash,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: append(testDescriptionHashData, 0x0),
|
||||||
|
valid: true,
|
||||||
|
result: nil, // skip unknown length, not 52 bytes
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
descriptionHash, err := parseDescriptionHash(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("description hash decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && !compareHashes(descriptionHash, test.result) {
|
||||||
|
t.Fatalf("test %d failed decoding description hash: "+
|
||||||
|
"expected %x, got %x",
|
||||||
|
i, *test.result, *descriptionHash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseExpiry checks that the expiry is properly parsed.
|
||||||
|
func TestParseExpiry(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result *time.Duration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: &testExpiry0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x1, 0x1c},
|
||||||
|
valid: true,
|
||||||
|
result: &testExpiry60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc},
|
||||||
|
valid: false, // data too long
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
expiry, err := parseExpiry(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("expiry decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && !reflect.DeepEqual(expiry, test.result) {
|
||||||
|
t.Fatalf("test %d failed decoding expiry: "+
|
||||||
|
"expected expiry %v, got %v",
|
||||||
|
i, *test.result, *expiry)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseMinFinalCLTVExpiry checks that the minFinalCLTVExpiry is properly parsed.
|
||||||
|
func TestParseMinFinalCLTVExpiry(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x1, 0x1c},
|
||||||
|
valid: true,
|
||||||
|
result: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc},
|
||||||
|
valid: true,
|
||||||
|
result: 38390726480144748,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc},
|
||||||
|
valid: false, // data too long
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
expiry, err := parseMinFinalCLTVExpiry(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("minFinalCLTVExpiry decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && *expiry != test.result {
|
||||||
|
t.Fatalf("test %d failed decoding minFinalCLTVExpiry: "+
|
||||||
|
"expected %d, got %d",
|
||||||
|
i, test.result, *expiry)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseFallbackAddr checks that the fallback address is properly parsed.
|
||||||
|
func TestParseFallbackAddr(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testAddrTestnetData, _ := bech32.ConvertBits(testAddrTestnet.ScriptAddress(), 8, 5, true)
|
||||||
|
testAddrTestnetDataWithVersion := append([]byte{17}, testAddrTestnetData...)
|
||||||
|
|
||||||
|
testRustyAddrData, _ := bech32.ConvertBits(testRustyAddr.ScriptAddress(), 8, 5, true)
|
||||||
|
testRustyAddrDataWithVersion := append([]byte{17}, testRustyAddrData...)
|
||||||
|
|
||||||
|
testAddrMainnetP2SHData, _ := bech32.ConvertBits(testAddrMainnetP2SH.ScriptAddress(), 8, 5, true)
|
||||||
|
testAddrMainnetP2SHDataWithVersion := append([]byte{18}, testAddrMainnetP2SHData...)
|
||||||
|
|
||||||
|
testAddrMainnetP2WPKHData, _ := bech32.ConvertBits(testAddrMainnetP2WPKH.ScriptAddress(), 8, 5, true)
|
||||||
|
testAddrMainnetP2WPKHDataWithVersion := append([]byte{0}, testAddrMainnetP2WPKHData...)
|
||||||
|
|
||||||
|
testAddrMainnetP2WSHData, _ := bech32.ConvertBits(testAddrMainnetP2WSH.ScriptAddress(), 8, 5, true)
|
||||||
|
testAddrMainnetP2WSHDataWithVersion := append([]byte{0}, testAddrMainnetP2WSHData...)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
net *chaincfg.Params
|
||||||
|
valid bool
|
||||||
|
result btcutil.Address
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: false, // empty data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{0x0},
|
||||||
|
valid: false, // data too short, version without address
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testAddrTestnetDataWithVersion,
|
||||||
|
net: &chaincfg.TestNet3Params,
|
||||||
|
valid: true,
|
||||||
|
result: testAddrTestnet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testRustyAddrDataWithVersion,
|
||||||
|
net: &chaincfg.MainNetParams,
|
||||||
|
valid: true,
|
||||||
|
result: testRustyAddr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testAddrMainnetP2SHDataWithVersion,
|
||||||
|
net: &chaincfg.MainNetParams,
|
||||||
|
valid: true,
|
||||||
|
result: testAddrMainnetP2SH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testAddrMainnetP2WPKHDataWithVersion,
|
||||||
|
net: &chaincfg.MainNetParams,
|
||||||
|
valid: true,
|
||||||
|
result: testAddrMainnetP2WPKH,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testAddrMainnetP2WSHDataWithVersion,
|
||||||
|
net: &chaincfg.MainNetParams,
|
||||||
|
valid: true,
|
||||||
|
result: testAddrMainnetP2WSH,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
fallbackAddr, err := parseFallbackAddr(test.data, test.net)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("fallback addr decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid && !reflect.DeepEqual(test.result, fallbackAddr) {
|
||||||
|
t.Fatalf("test %d failed decoding fallback addr: "+
|
||||||
|
"expected %v, got %v",
|
||||||
|
i, test.result, fallbackAddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseRoutingInfo checks that the routing info is properly parsed.
|
||||||
|
func TestParseRoutingInfo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var testSingleHopData []byte
|
||||||
|
for _, r := range testSingleHop {
|
||||||
|
base256 := make([]byte, 51)
|
||||||
|
copy(base256[:33], r.PubKey.SerializeCompressed())
|
||||||
|
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
||||||
|
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
||||||
|
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
||||||
|
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
||||||
|
testSingleHopData = append(testSingleHopData, base256...)
|
||||||
|
}
|
||||||
|
testSingleHopData, _ = bech32.ConvertBits(testSingleHopData, 8, 5, true)
|
||||||
|
|
||||||
|
var testDoubleHopData []byte
|
||||||
|
for _, r := range testDoubleHop {
|
||||||
|
base256 := make([]byte, 51)
|
||||||
|
copy(base256[:33], r.PubKey.SerializeCompressed())
|
||||||
|
binary.BigEndian.PutUint64(base256[33:41], r.ShortChanID)
|
||||||
|
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMsat)
|
||||||
|
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
||||||
|
binary.BigEndian.PutUint16(base256[49:51], r.CltvExpDelta)
|
||||||
|
testDoubleHopData = append(testDoubleHopData, base256...)
|
||||||
|
}
|
||||||
|
testDoubleHopData, _ = bech32.ConvertBits(testDoubleHopData, 8, 5, true)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data []byte
|
||||||
|
valid bool
|
||||||
|
result []ExtraRoutingInfo
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: []byte{0x0, 0x0, 0x0, 0x0},
|
||||||
|
valid: false, // data too short, not multiple of 51 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: []byte{},
|
||||||
|
valid: true,
|
||||||
|
result: []ExtraRoutingInfo{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testSingleHopData,
|
||||||
|
valid: true,
|
||||||
|
result: testSingleHop,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: append(testSingleHopData, 0x0),
|
||||||
|
valid: false, // data too long, not multiple of 51 bytes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: testDoubleHopData,
|
||||||
|
valid: true,
|
||||||
|
result: testDoubleHop,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
routingInfo, err := parseRoutingInfo(test.data)
|
||||||
|
if (err == nil) != test.valid {
|
||||||
|
t.Errorf("routing info decoding test %d failed: %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if test.valid {
|
||||||
|
if err := compareRoutingInfos(test.result, routingInfo); err != nil {
|
||||||
|
t.Fatalf("test %d failed decoding routing info: %v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
package zpay32_test
|
package zpay32
|
||||||
|
|
||||||
|
// We use package `zpay32` rather than `zpay32_test` in order to share test data
|
||||||
|
// with the internal tests.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -9,7 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/zpay32"
|
|
||||||
"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"
|
||||||
@ -17,35 +19,62 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
testMillisat24BTC = lnwire.MilliSatoshi(2400000000000)
|
||||||
|
testMillisat2500uBTC = lnwire.MilliSatoshi(250000000)
|
||||||
|
testMillisat20mBTC = lnwire.MilliSatoshi(2000000000)
|
||||||
|
|
||||||
|
testPaymentHashSlice, _ = hex.DecodeString("0001020304050607080900010203040506070809000102030405060708090102")
|
||||||
|
|
||||||
|
testEmptyString = ""
|
||||||
|
testCupOfCoffee = "1 cup coffee"
|
||||||
|
testPleaseConsider = "Please consider supporting this project"
|
||||||
|
|
||||||
testPrivKeyBytes, _ = hex.DecodeString("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734")
|
testPrivKeyBytes, _ = hex.DecodeString("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734")
|
||||||
testPrivKey, testPubKey = btcec.PrivKeyFromBytes(btcec.S256(), testPrivKeyBytes)
|
testPrivKey, testPubKey = btcec.PrivKeyFromBytes(btcec.S256(), testPrivKeyBytes)
|
||||||
|
|
||||||
|
testDescriptionHashSlice = chainhash.HashB([]byte("One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
|
||||||
|
|
||||||
|
testExpiry0 = time.Duration(0) * time.Second
|
||||||
|
testExpiry60 = time.Duration(60) * time.Second
|
||||||
|
|
||||||
|
testAddrTestnet, _ = btcutil.DecodeAddress("mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP", &chaincfg.TestNet3Params)
|
||||||
|
testRustyAddr, _ = btcutil.DecodeAddress("1RustyRX2oai4EYYDpQGWvEL62BBGqN9T", &chaincfg.MainNetParams)
|
||||||
|
testAddrMainnetP2SH, _ = btcutil.DecodeAddress("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX", &chaincfg.MainNetParams)
|
||||||
|
testAddrMainnetP2WPKH, _ = btcutil.DecodeAddress("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", &chaincfg.MainNetParams)
|
||||||
|
testAddrMainnetP2WSH, _ = btcutil.DecodeAddress("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", &chaincfg.MainNetParams)
|
||||||
|
|
||||||
testRoutingInfoPubkeyBytes, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
testRoutingInfoPubkeyBytes, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
||||||
testRoutingInfoPubkey, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes, btcec.S256())
|
testRoutingInfoPubkey, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes, btcec.S256())
|
||||||
testRoutingInfoPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
testRoutingInfoPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
|
||||||
testRoutingInfoPubkey2, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes2, btcec.S256())
|
testRoutingInfoPubkey2, _ = btcec.ParsePubKey(testRoutingInfoPubkeyBytes2, btcec.S256())
|
||||||
|
|
||||||
testMillisat24BTC = lnwire.MilliSatoshi(2400000000000)
|
testSingleHop = []ExtraRoutingInfo{
|
||||||
testMillisat2500uBTC = lnwire.MilliSatoshi(250000000)
|
{
|
||||||
testMillisat20mBTC = lnwire.MilliSatoshi(2000000000)
|
PubKey: testRoutingInfoPubkey,
|
||||||
|
ShortChanID: 0x0102030405060708,
|
||||||
|
FeeBaseMsat: 0,
|
||||||
|
FeeProportionalMillionths: 20,
|
||||||
|
CltvExpDelta: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testDoubleHop = []ExtraRoutingInfo{
|
||||||
|
{
|
||||||
|
PubKey: testRoutingInfoPubkey,
|
||||||
|
ShortChanID: 0x0102030405060708,
|
||||||
|
FeeBaseMsat: 1,
|
||||||
|
FeeProportionalMillionths: 20,
|
||||||
|
CltvExpDelta: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PubKey: testRoutingInfoPubkey2,
|
||||||
|
ShortChanID: 0x030405060708090a,
|
||||||
|
FeeBaseMsat: 2,
|
||||||
|
FeeProportionalMillionths: 30,
|
||||||
|
CltvExpDelta: 4,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
testExpiry60 = 60 * time.Second
|
testMessageSigner = MessageSigner{
|
||||||
testEmptyString = ""
|
|
||||||
testCupOfCoffee = "1 cup coffee"
|
|
||||||
testPleaseConsider = "Please consider supporting this project"
|
|
||||||
testRustyAddr, _ = btcutil.DecodeAddress("1RustyRX2oai4EYYDpQGWvEL62BBGqN9T", &chaincfg.MainNetParams)
|
|
||||||
testAddrTestnet, _ = btcutil.DecodeAddress("mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP", &chaincfg.TestNet3Params)
|
|
||||||
testAddrMainnetP2SH, _ = btcutil.DecodeAddress("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX", &chaincfg.MainNetParams)
|
|
||||||
testAddrMainnetP2WPKH, _ = btcutil.DecodeAddress("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", &chaincfg.MainNetParams)
|
|
||||||
testAddrMainnetP2WSH, _ = btcutil.DecodeAddress("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", &chaincfg.MainNetParams)
|
|
||||||
testPaymentHashSlice, _ = hex.DecodeString("0001020304050607080900010203040506070809000102030405060708090102")
|
|
||||||
testDescriptionHashSlice = chainhash.HashB([]byte("One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
|
|
||||||
|
|
||||||
// Must be initialized in init().
|
|
||||||
testPaymentHash [32]byte
|
|
||||||
testDescriptionHash [32]byte
|
|
||||||
|
|
||||||
testMessageSigner = zpay32.MessageSigner{
|
|
||||||
SignCompact: func(hash []byte) ([]byte, error) {
|
SignCompact: func(hash []byte) ([]byte, error) {
|
||||||
sig, err := btcec.SignCompact(btcec.S256(),
|
sig, err := btcec.SignCompact(btcec.S256(),
|
||||||
testPrivKey, hash, true)
|
testPrivKey, hash, true)
|
||||||
@ -56,6 +85,10 @@ var (
|
|||||||
return sig, nil
|
return sig, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must be initialized in init().
|
||||||
|
testPaymentHash [32]byte
|
||||||
|
testDescriptionHash [32]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -72,9 +105,9 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
encodedInvoice string
|
encodedInvoice string
|
||||||
valid bool
|
valid bool
|
||||||
decodedInvoice func() *zpay32.Invoice
|
decodedInvoice func() *Invoice
|
||||||
skipEncoding bool
|
skipEncoding bool
|
||||||
beforeEncoding func(*zpay32.Invoice)
|
beforeEncoding func(*Invoice)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
encodedInvoice: "asdsaddnasdnas", // no hrp
|
encodedInvoice: "asdsaddnasdnas", // no hrp
|
||||||
@ -112,8 +145,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// no payment hash set
|
// no payment hash set
|
||||||
encodedInvoice: "lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsjv38luh6p6s2xrv3mzvlmzaya43376h0twal5ax0k6p47498hp3hnaymzhsn424rxqjs0q7apn26yrhaxltq3vzwpqj9nc2r3kzwccsplnq470",
|
encodedInvoice: "lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsjv38luh6p6s2xrv3mzvlmzaya43376h0twal5ax0k6p47498hp3hnaymzhsn424rxqjs0q7apn26yrhaxltq3vzwpqj9nc2r3kzwccsplnq470",
|
||||||
valid: false,
|
valid: false,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -126,8 +159,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Both Description and DescriptionHash set.
|
// Both Description and DescriptionHash set.
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs03vghs8y0kuj4ulrzls8ln7fnm9dk7sjsnqmghql6hd6jut36clkqpyuq0s5m6fhureyz0szx2qjc8hkgf4xc2hpw8jpu26jfeyvf4cpga36gt",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs03vghs8y0kuj4ulrzls8ln7fnm9dk7sjsnqmghql6hd6jut36clkqpyuq0s5m6fhureyz0szx2qjc8hkgf4xc2hpw8jpu26jfeyvf4cpga36gt",
|
||||||
valid: false,
|
valid: false,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -142,8 +175,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Neither Description nor DescriptionHash set.
|
// Neither Description nor DescriptionHash set.
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqn2rne0kagfl4e0xag0w6hqeg2dwgc54hrm9m0auw52dhwhwcu559qav309h598pyzn69wh2nqauneyyesnpmaax0g6acr8lh9559jmcquyq5a9",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqn2rne0kagfl4e0xag0w6hqeg2dwgc54hrm9m0auw52dhwhwcu559qav309h598pyzn69wh2nqauneyyesnpmaax0g6acr8lh9559jmcquyq5a9",
|
||||||
valid: false,
|
valid: false,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -156,8 +189,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Has a few unknown fields, should just be ignored.
|
// Has a few unknown fields, should just be ignored.
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqtq2v93xxer9vczq8v93xxeqv72xr42ca60022jqu6fu73n453tmnr0ukc0pl0t23w7eavtensjz0j2wcu7nkxhfdgp9y37welajh5kw34mq7m4xuay0a72cwec8qwgqt5vqht",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqtq2v93xxer9vczq8v93xxeqv72xr42ca60022jqu6fu73n453tmnr0ukc0pl0t23w7eavtensjz0j2wcu7nkxhfdgp9y37welajh5kw34mq7m4xuay0a72cwec8qwgqt5vqht",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -172,8 +205,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Ignore unknown witness version in fallback address.
|
// Ignore unknown witness version in fallback address.
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpppw508d6qejxtdg4y5r3zarvary0c5xw7k8txqv6x0a75xuzp0zsdzk5hq6tmfgweltvs6jk5nhtyd9uqksvr48zga9mw08667w8264gkspluu66jhtcmct36nx363km6cquhhv2cpc6q43r",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpppw508d6qejxtdg4y5r3zarvary0c5xw7k8txqv6x0a75xuzp0zsdzk5hq6tmfgweltvs6jk5nhtyd9uqksvr48zga9mw08667w8264gkspluu66jhtcmct36nx363km6cquhhv2cpc6q43r",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -188,8 +221,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Ignore fields with unknown lengths.
|
// Ignore fields with unknown lengths.
|
||||||
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqpp3qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqshp38yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66np3q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfy8huflvs2zwkymx47cszugvzn5v64ahemzzlmm62rpn9l9rm05h35aceq00tkt296289wepws9jh4499wq2l0vk6xcxffd90dpuqchqqztyayq",
|
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqpp3qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqshp38yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66np3q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfy8huflvs2zwkymx47cszugvzn5v64ahemzzlmm62rpn9l9rm05h35aceq00tkt296289wepws9jh4499wq2l0vk6xcxffd90dpuqchqqztyayq",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat24BTC,
|
MilliSat: &testMillisat24BTC,
|
||||||
Timestamp: time.Unix(1503429093, 0),
|
Timestamp: time.Unix(1503429093, 0),
|
||||||
@ -204,8 +237,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Please make a donation of any amount using rhash 0001020304050607080900010203040506070809000102030405060708090102 to me @03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad
|
// Please make a donation of any amount using rhash 0001020304050607080900010203040506070809000102030405060708090102 to me @03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad
|
||||||
encodedInvoice: "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w",
|
encodedInvoice: "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
PaymentHash: &testPaymentHash,
|
PaymentHash: &testPaymentHash,
|
||||||
@ -213,7 +246,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
Destination: testPubKey,
|
Destination: testPubKey,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -224,8 +257,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Same as above, pubkey set in 'n' field.
|
// Same as above, pubkey set in 'n' field.
|
||||||
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
|
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat24BTC,
|
MilliSat: &testMillisat24BTC,
|
||||||
Timestamp: time.Unix(1503429093, 0),
|
Timestamp: time.Unix(1503429093, 0),
|
||||||
@ -239,18 +272,18 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Please send $3 for a cup of coffee to the same peer, within 1 minute
|
// Please send $3 for a cup of coffee to the same peer, within 1 minute
|
||||||
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp",
|
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
i, _ := zpay32.NewInvoice(
|
i, _ := NewInvoice(
|
||||||
&chaincfg.MainNetParams,
|
&chaincfg.MainNetParams,
|
||||||
testPaymentHash,
|
testPaymentHash,
|
||||||
time.Unix(1496314658, 0),
|
time.Unix(1496314658, 0),
|
||||||
zpay32.Amount(testMillisat2500uBTC),
|
Amount(testMillisat2500uBTC),
|
||||||
zpay32.Description(testCupOfCoffee),
|
Description(testCupOfCoffee),
|
||||||
zpay32.Destination(testPubKey),
|
Destination(testPubKey),
|
||||||
zpay32.Expiry(testExpiry60))
|
Expiry(testExpiry60))
|
||||||
return i
|
return i
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -261,8 +294,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// Now send $24 for an entire list of things (hashed)
|
// Now send $24 for an entire list of things (hashed)
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -271,7 +304,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
Destination: testPubKey,
|
Destination: testPubKey,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -282,8 +315,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// The same, on testnet, with a fallback address mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP
|
// The same, on testnet, with a fallback address mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP
|
||||||
encodedInvoice: "lntb20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3x9et2e20v6pu37c5d9vax37wxq72un98k6vcx9fz94w0qf237cm2rqv9pmn5lnexfvf5579slr4zq3u8kmczecytdx0xg9rwzngp7e6guwqpqlhssu04sucpnz4axcv2dstmknqq6jsk2l",
|
encodedInvoice: "lntb20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3x9et2e20v6pu37c5d9vax37wxq72un98k6vcx9fz94w0qf237cm2rqv9pmn5lnexfvf5579slr4zq3u8kmczecytdx0xg9rwzngp7e6guwqpqlhssu04sucpnz4axcv2dstmknqq6jsk2l",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.TestNet3Params,
|
Net: &chaincfg.TestNet3Params,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -293,7 +326,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
FallbackAddr: testAddrTestnet,
|
FallbackAddr: testAddrTestnet,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -304,8 +337,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to get to node 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
|
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to get to node 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85frzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvncsk57n4v9ehw86wq8fzvjejhv9z3w3q5zh6qkql005x9xl240ch23jk79ujzvr4hsmmafyxghpqe79psktnjl668ntaf4ne7ucs5csqh5mnnk",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85frzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvncsk57n4v9ehw86wq8fzvjejhv9z3w3q5zh6qkql005x9xl240ch23jk79ujzvr4hsmmafyxghpqe79psktnjl668ntaf4ne7ucs5csqh5mnnk",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -313,18 +346,10 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
DescriptionHash: &testDescriptionHash,
|
DescriptionHash: &testDescriptionHash,
|
||||||
Destination: testPubKey,
|
Destination: testPubKey,
|
||||||
FallbackAddr: testRustyAddr,
|
FallbackAddr: testRustyAddr,
|
||||||
RoutingInfo: []zpay32.ExtraRoutingInfo{
|
RoutingInfo: testSingleHop,
|
||||||
{
|
|
||||||
PubKey: testRoutingInfoPubkey,
|
|
||||||
ShortChanID: 0x0102030405060708,
|
|
||||||
FeeBaseMsat: 0,
|
|
||||||
FeeProportionalMillionths: 20,
|
|
||||||
CltvExpDelta: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -335,8 +360,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
|
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -344,25 +369,10 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
DescriptionHash: &testDescriptionHash,
|
DescriptionHash: &testDescriptionHash,
|
||||||
Destination: testPubKey,
|
Destination: testPubKey,
|
||||||
FallbackAddr: testRustyAddr,
|
FallbackAddr: testRustyAddr,
|
||||||
RoutingInfo: []zpay32.ExtraRoutingInfo{
|
RoutingInfo: testDoubleHop,
|
||||||
{
|
|
||||||
PubKey: testRoutingInfoPubkey,
|
|
||||||
ShortChanID: 0x0102030405060708,
|
|
||||||
FeeBaseMsat: 1,
|
|
||||||
FeeProportionalMillionths: 20,
|
|
||||||
CltvExpDelta: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PubKey: testRoutingInfoPubkey2,
|
|
||||||
ShortChanID: 0x030405060708090a,
|
|
||||||
FeeBaseMsat: 2,
|
|
||||||
FeeProportionalMillionths: 30,
|
|
||||||
CltvExpDelta: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -373,8 +383,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// On mainnet, with fallback (p2sh) address 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
|
// On mainnet, with fallback (p2sh) address 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kk822r8plup77n9yq5ep2dfpcydrjwzxs0la84v3tfw43t3vqhek7f05m6uf8lmfkjn7zv7enn76sq65d8u9lxav2pl6x3xnc2ww3lqpagnh0u",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kk822r8plup77n9yq5ep2dfpcydrjwzxs0la84v3tfw43t3vqhek7f05m6uf8lmfkjn7zv7enn76sq65d8u9lxav2pl6x3xnc2ww3lqpagnh0u",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -384,7 +394,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
FallbackAddr: testAddrMainnetP2SH,
|
FallbackAddr: testAddrMainnetP2SH,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -395,8 +405,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// On mainnet, with fallback (p2wpkh) address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
|
// On mainnet, with fallback (p2wpkh) address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfppqw508d6qejxtdg4y5r3zarvary0c5xw7kknt6zz5vxa8yh8jrnlkl63dah48yh6eupakk87fjdcnwqfcyt7snnpuz7vp83txauq4c60sys3xyucesxjf46yqnpplj0saq36a554cp9wt865",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfppqw508d6qejxtdg4y5r3zarvary0c5xw7kknt6zz5vxa8yh8jrnlkl63dah48yh6eupakk87fjdcnwqfcyt7snnpuz7vp83txauq4c60sys3xyucesxjf46yqnpplj0saq36a554cp9wt865",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -406,7 +416,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
FallbackAddr: testAddrMainnetP2WPKH,
|
FallbackAddr: testAddrMainnetP2WPKH,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -417,8 +427,8 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// On mainnet, with fallback (p2wsh) address bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3
|
// On mainnet, with fallback (p2wsh) address bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3
|
||||||
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qvnjha2auylmwrltv2pkp2t22uy8ura2xsdwhq5nm7s574xva47djmnj2xeycsu7u5v8929mvuux43j0cqhhf32wfyn2th0sv4t9x55sppz5we8",
|
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qvnjha2auylmwrltv2pkp2t22uy8ura2xsdwhq5nm7s574xva47djmnj2xeycsu7u5v8929mvuux43j0cqhhf32wfyn2th0sv4t9x55sppz5we8",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
return &zpay32.Invoice{
|
return &Invoice{
|
||||||
Net: &chaincfg.MainNetParams,
|
Net: &chaincfg.MainNetParams,
|
||||||
MilliSat: &testMillisat20mBTC,
|
MilliSat: &testMillisat20mBTC,
|
||||||
Timestamp: time.Unix(1496314658, 0),
|
Timestamp: time.Unix(1496314658, 0),
|
||||||
@ -428,7 +438,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
FallbackAddr: testAddrMainnetP2WSH,
|
FallbackAddr: testAddrMainnetP2WSH,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEncoding: func(i *zpay32.Invoice) {
|
beforeEncoding: func(i *Invoice) {
|
||||||
// Since this destination pubkey was recovered
|
// Since this destination pubkey was recovered
|
||||||
// from the signature, we must set it nil before
|
// from the signature, we must set it nil before
|
||||||
// encoding to get back the same invoice string.
|
// encoding to get back the same invoice string.
|
||||||
@ -440,15 +450,15 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
// expiry value.
|
// expiry value.
|
||||||
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jscqzysnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66ysxkvnxhcvhz48sn72lp77h4fxcur27z0he48u5qvk3sxse9mr9jhkltt962s8arjnzk8rk59yj5nw4p495747gksj30gza0crhzwjcpgxzy00",
|
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jscqzysnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66ysxkvnxhcvhz48sn72lp77h4fxcur27z0he48u5qvk3sxse9mr9jhkltt962s8arjnzk8rk59yj5nw4p495747gksj30gza0crhzwjcpgxzy00",
|
||||||
valid: true,
|
valid: true,
|
||||||
decodedInvoice: func() *zpay32.Invoice {
|
decodedInvoice: func() *Invoice {
|
||||||
i, _ := zpay32.NewInvoice(
|
i, _ := NewInvoice(
|
||||||
&chaincfg.MainNetParams,
|
&chaincfg.MainNetParams,
|
||||||
testPaymentHash,
|
testPaymentHash,
|
||||||
time.Unix(1496314658, 0),
|
time.Unix(1496314658, 0),
|
||||||
zpay32.Amount(testMillisat2500uBTC),
|
Amount(testMillisat2500uBTC),
|
||||||
zpay32.Description(testCupOfCoffee),
|
Description(testCupOfCoffee),
|
||||||
zpay32.Destination(testPubKey),
|
Destination(testPubKey),
|
||||||
zpay32.CLTVExpiry(144),
|
CLTVExpiry(144),
|
||||||
)
|
)
|
||||||
|
|
||||||
return i
|
return i
|
||||||
@ -457,7 +467,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
invoice, err := zpay32.Decode(test.encodedInvoice)
|
invoice, err := Decode(test.encodedInvoice)
|
||||||
if (err == nil) != test.valid {
|
if (err == nil) != test.valid {
|
||||||
t.Errorf("Decoding test %d failed: %v", i, err)
|
t.Errorf("Decoding test %d failed: %v", i, err)
|
||||||
return
|
return
|
||||||
@ -474,7 +484,7 @@ func TestDecodeEncode(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var decodedInvoice *zpay32.Invoice
|
var decodedInvoice *Invoice
|
||||||
if test.decodedInvoice != nil {
|
if test.decodedInvoice != nil {
|
||||||
decodedInvoice = test.decodedInvoice()
|
decodedInvoice = test.decodedInvoice()
|
||||||
}
|
}
|
||||||
@ -507,58 +517,41 @@ func TestNewInvoice(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
newInvoice func() (*zpay32.Invoice, error)
|
newInvoice func() (*Invoice, error)
|
||||||
encodedInvoice string
|
encodedInvoice string
|
||||||
valid bool
|
valid bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// Both Description and DescriptionHash set.
|
// Both Description and DescriptionHash set.
|
||||||
newInvoice: func() (*zpay32.Invoice, error) {
|
newInvoice: func() (*Invoice, error) {
|
||||||
return zpay32.NewInvoice(&chaincfg.MainNetParams,
|
return NewInvoice(&chaincfg.MainNetParams,
|
||||||
testPaymentHash, time.Unix(1496314658, 0),
|
testPaymentHash, time.Unix(1496314658, 0),
|
||||||
zpay32.DescriptionHash(testDescriptionHash),
|
DescriptionHash(testDescriptionHash),
|
||||||
zpay32.Description(testPleaseConsider))
|
Description(testPleaseConsider))
|
||||||
},
|
},
|
||||||
valid: false, // Both Description and DescriptionHash set.
|
valid: false, // Both Description and DescriptionHash set.
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// 'n' field set.
|
// 'n' field set.
|
||||||
newInvoice: func() (*zpay32.Invoice, error) {
|
newInvoice: func() (*Invoice, error) {
|
||||||
return zpay32.NewInvoice(&chaincfg.MainNetParams,
|
return NewInvoice(&chaincfg.MainNetParams,
|
||||||
testPaymentHash, time.Unix(1503429093, 0),
|
testPaymentHash, time.Unix(1503429093, 0),
|
||||||
zpay32.Amount(testMillisat24BTC),
|
Amount(testMillisat24BTC),
|
||||||
zpay32.Description(testEmptyString),
|
Description(testEmptyString),
|
||||||
zpay32.Destination(testPubKey))
|
Destination(testPubKey))
|
||||||
},
|
},
|
||||||
valid: true,
|
valid: true,
|
||||||
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
|
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
|
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
|
||||||
newInvoice: func() (*zpay32.Invoice, error) {
|
newInvoice: func() (*Invoice, error) {
|
||||||
return zpay32.NewInvoice(&chaincfg.MainNetParams,
|
return NewInvoice(&chaincfg.MainNetParams,
|
||||||
testPaymentHash, time.Unix(1496314658, 0),
|
testPaymentHash, time.Unix(1496314658, 0),
|
||||||
zpay32.Amount(testMillisat20mBTC),
|
Amount(testMillisat20mBTC),
|
||||||
zpay32.DescriptionHash(testDescriptionHash),
|
DescriptionHash(testDescriptionHash),
|
||||||
zpay32.FallbackAddr(testRustyAddr),
|
FallbackAddr(testRustyAddr),
|
||||||
zpay32.RoutingInfo(
|
RoutingInfo(testDoubleHop),
|
||||||
[]zpay32.ExtraRoutingInfo{
|
|
||||||
{
|
|
||||||
PubKey: testRoutingInfoPubkey,
|
|
||||||
ShortChanID: 0x0102030405060708,
|
|
||||||
FeeBaseMsat: 1,
|
|
||||||
FeeProportionalMillionths: 20,
|
|
||||||
CltvExpDelta: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PubKey: testRoutingInfoPubkey2,
|
|
||||||
ShortChanID: 0x030405060708090a,
|
|
||||||
FeeBaseMsat: 2,
|
|
||||||
FeeProportionalMillionths: 30,
|
|
||||||
CltvExpDelta: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
valid: true,
|
valid: true,
|
||||||
@ -586,7 +579,7 @@ func TestNewInvoice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareInvoices(expected, actual *zpay32.Invoice) error {
|
func compareInvoices(expected, actual *Invoice) error {
|
||||||
if !reflect.DeepEqual(expected.Net, actual.Net) {
|
if !reflect.DeepEqual(expected.Net, actual.Net) {
|
||||||
return fmt.Errorf("expected net %v, got %v",
|
return fmt.Errorf("expected net %v, got %v",
|
||||||
expected.Net, actual.Net)
|
expected.Net, actual.Net)
|
||||||
@ -596,8 +589,9 @@ func compareInvoices(expected, actual *zpay32.Invoice) error {
|
|||||||
return fmt.Errorf("expected milli sat %d, got %d",
|
return fmt.Errorf("expected milli sat %d, got %d",
|
||||||
*expected.MilliSat, *actual.MilliSat)
|
*expected.MilliSat, *actual.MilliSat)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected.Timestamp != actual.Timestamp {
|
if expected.Timestamp != actual.Timestamp {
|
||||||
return fmt.Errorf("expected Timestamp %d, got %d",
|
return fmt.Errorf("expected timestamp %v, got %v",
|
||||||
expected.Timestamp, actual.Timestamp)
|
expected.Timestamp, actual.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,40 +625,10 @@ func compareInvoices(expected, actual *zpay32.Invoice) error {
|
|||||||
expected.FallbackAddr, actual.FallbackAddr)
|
expected.FallbackAddr, actual.FallbackAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(expected.RoutingInfo) != len(actual.RoutingInfo) {
|
if err := compareRoutingInfos(expected.RoutingInfo, actual.RoutingInfo); err != nil {
|
||||||
return fmt.Errorf("expected len routingInfo %d, got %d",
|
return err
|
||||||
len(expected.RoutingInfo), len(actual.RoutingInfo))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(expected.RoutingInfo); i++ {
|
|
||||||
a := expected.RoutingInfo[i]
|
|
||||||
b := actual.RoutingInfo[i]
|
|
||||||
|
|
||||||
if !comparePubkeys(a.PubKey, b.PubKey) {
|
|
||||||
return fmt.Errorf("expected routingInfo pubkey %x, "+
|
|
||||||
"got %x", a.PubKey, b.PubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.ShortChanID != b.ShortChanID {
|
|
||||||
return fmt.Errorf("expected routingInfo shortChanID "+
|
|
||||||
"%d, got %d", a.ShortChanID, b.ShortChanID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.FeeBaseMsat != b.FeeBaseMsat {
|
|
||||||
return fmt.Errorf("expected routingInfo feeBaseMsat %d, got %d",
|
|
||||||
a.FeeBaseMsat, b.FeeBaseMsat)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.FeeProportionalMillionths != b.FeeProportionalMillionths {
|
|
||||||
return fmt.Errorf("expected routingInfo feeProportionalMillionths %d, got %d",
|
|
||||||
a.FeeProportionalMillionths, b.FeeProportionalMillionths)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.CltvExpDelta != b.CltvExpDelta {
|
|
||||||
return fmt.Errorf("expected routingInfo cltvExpDelta "+
|
|
||||||
"%d, got %d", a.CltvExpDelta, b.CltvExpDelta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,3 +657,39 @@ func compareHashes(a, b *[32]byte) bool {
|
|||||||
}
|
}
|
||||||
return bytes.Equal(a[:], b[:])
|
return bytes.Equal(a[:], b[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func compareRoutingInfos(a, b []ExtraRoutingInfo) error {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return fmt.Errorf("expected len routingInfo %d, got %d",
|
||||||
|
len(a), len(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(a); i++ {
|
||||||
|
if !comparePubkeys(a[i].PubKey, b[i].PubKey) {
|
||||||
|
return fmt.Errorf("expected routingInfo pubkey %x, "+
|
||||||
|
"got %x", a[i].PubKey, b[i].PubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[i].ShortChanID != b[i].ShortChanID {
|
||||||
|
return fmt.Errorf("expected routingInfo shortChanID "+
|
||||||
|
"%d, got %d", a[i].ShortChanID, b[i].ShortChanID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[i].FeeBaseMsat != b[i].FeeBaseMsat {
|
||||||
|
return fmt.Errorf("expected routingInfo feeBaseMsat %d, got %d",
|
||||||
|
a[i].FeeBaseMsat, b[i].FeeBaseMsat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[i].FeeProportionalMillionths != b[i].FeeProportionalMillionths {
|
||||||
|
return fmt.Errorf("expected routingInfo feeProportionalMillionths %d, got %d",
|
||||||
|
a[i].FeeProportionalMillionths, b[i].FeeProportionalMillionths)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a[i].CltvExpDelta != b[i].CltvExpDelta {
|
||||||
|
return fmt.Errorf("expected routingInfo cltvExpDelta "+
|
||||||
|
"%d, got %d", a[i].CltvExpDelta, b[i].CltvExpDelta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user