2017-09-05 19:07:14 +03:00
|
|
|
package zpay32
|
2017-08-26 16:53:45 +03:00
|
|
|
|
|
|
|
import (
|
2018-01-09 09:14:31 +03:00
|
|
|
"encoding/binary"
|
2018-03-30 02:23:47 +03:00
|
|
|
"math"
|
2018-01-09 09:14:31 +03:00
|
|
|
"reflect"
|
2017-08-26 16:53:45 +03:00
|
|
|
"testing"
|
2018-01-09 09:14:31 +03:00
|
|
|
"time"
|
2017-08-26 16:53:45 +03:00
|
|
|
|
2018-06-05 04:34:16 +03:00
|
|
|
"github.com/btcsuite/btcd/btcec"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
|
|
"github.com/btcsuite/btcutil"
|
|
|
|
"github.com/btcsuite/btcutil/bech32"
|
2018-07-31 10:17:17 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2017-08-26 16:53:45 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// TestDecodeAmount ensures that the amount string in the hrp of the Invoice
|
|
|
|
// properly gets decoded into millisatoshis.
|
|
|
|
func TestDecodeAmount(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
amount string
|
|
|
|
valid bool
|
|
|
|
result lnwire.MilliSatoshi
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
amount: "",
|
|
|
|
valid: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "20n00",
|
|
|
|
valid: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2000y",
|
|
|
|
valid: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2000mm",
|
|
|
|
valid: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2000nm",
|
|
|
|
valid: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "m",
|
|
|
|
valid: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "1p", // pBTC
|
|
|
|
valid: false, // too small
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "1109p", // pBTC
|
|
|
|
valid: false, // not divisible by 10
|
|
|
|
},
|
2018-01-19 22:21:58 +03:00
|
|
|
{
|
|
|
|
amount: "-10p", // pBTC
|
|
|
|
valid: false, // negative amount
|
|
|
|
},
|
2017-08-26 16:53:45 +03:00
|
|
|
{
|
|
|
|
amount: "10p", // pBTC
|
|
|
|
valid: true,
|
|
|
|
result: 1, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "1000p", // pBTC
|
|
|
|
valid: true,
|
|
|
|
result: 100, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "1n", // nBTC
|
|
|
|
valid: true,
|
|
|
|
result: 100, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "9000n", // nBTC
|
|
|
|
valid: true,
|
|
|
|
result: 900000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "9u", // uBTC
|
|
|
|
valid: true,
|
|
|
|
result: 900000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2000u", // uBTC
|
|
|
|
valid: true,
|
|
|
|
result: 200000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2m", // mBTC
|
|
|
|
valid: true,
|
|
|
|
result: 200000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2000m", // mBTC
|
|
|
|
valid: true,
|
|
|
|
result: 200000000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2", // BTC
|
|
|
|
valid: true,
|
|
|
|
result: 200000000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2000", // BTC
|
|
|
|
valid: true,
|
|
|
|
result: 200000000000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "2009", // BTC
|
|
|
|
valid: true,
|
|
|
|
result: 200900000000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "1234", // BTC
|
|
|
|
valid: true,
|
|
|
|
result: 123400000000000, // mSat
|
|
|
|
},
|
|
|
|
{
|
|
|
|
amount: "21000000", // BTC
|
|
|
|
valid: true,
|
|
|
|
result: 2100000000000000000, // mSat
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
sat, err := decodeAmount(test.amount)
|
|
|
|
if (err == nil) != test.valid {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Errorf("amount decoding test %d failed: %v", i, err)
|
2017-08-26 16:53:45 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if test.valid && sat != test.result {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Fatalf("test %d failed decoding amount, expected %v, "+
|
2017-08-26 16:53:45 +03:00
|
|
|
"got %v", i, test.result, sat)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestEncodeAmount checks that the given amount in millisatoshis gets encoded
|
|
|
|
// into the shortest possible amount string.
|
|
|
|
func TestEncodeAmount(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
msat lnwire.MilliSatoshi
|
|
|
|
valid bool
|
|
|
|
result string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
msat: 1, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "10p", // pBTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 120, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "1200p", // pBTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 100, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "1n", // nBTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 900000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "9u", // uBTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 200000000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "2m", // mBTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 200000000000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "2", // BTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 200000000000000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "2000", // BTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 200900000000000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "2009", // BTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 123400000000000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "1234", // BTC
|
|
|
|
},
|
|
|
|
{
|
|
|
|
msat: 2100000000000000000, // mSat
|
|
|
|
valid: true,
|
|
|
|
result: "21000000", // BTC
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
shortened, err := encodeAmount(test.msat)
|
|
|
|
if (err == nil) != test.valid {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Errorf("amount encoding test %d failed: %v", i, err)
|
2017-08-26 16:53:45 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if test.valid && shortened != test.result {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Fatalf("test %d failed encoding amount, expected %v, "+
|
2017-08-26 16:53:45 +03:00
|
|
|
"got %v", i, test.result, shortened)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestParseTimestamp checks that the 35 bit timestamp is properly parsed.
|
|
|
|
func TestParseTimestamp(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
data []byte
|
|
|
|
valid bool
|
|
|
|
result uint64
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
data: []byte(""),
|
|
|
|
valid: false, // empty data
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
valid: false, // data too short
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: []byte{0x01, 0x0c, 0x12, 0x1f, 0x1c, 0x19, 0x02},
|
|
|
|
valid: true, // timestamp 1496314658
|
|
|
|
result: 1496314658,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
time, err := parseTimestamp(test.data)
|
|
|
|
if (err == nil) != test.valid {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Errorf("timestamp decoding test %d failed: %v", i, err)
|
2017-08-26 16:53:45 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if test.valid && time != test.result {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Fatalf("test %d failed decoding timestamp: "+
|
|
|
|
"expected %d, got %d",
|
|
|
|
i, test.result, time)
|
2017-08-26 16:53:45 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-09 09:14:31 +03:00
|
|
|
|
|
|
|
// 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,
|
|
|
|
},
|
|
|
|
{
|
2018-03-30 02:23:47 +03:00
|
|
|
data: []byte{
|
|
|
|
0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
|
|
|
|
0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
|
|
|
|
0xc, 0x3,
|
|
|
|
},
|
2018-01-09 09:14:31 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 02:23:47 +03:00
|
|
|
// TestParseMinFinalCLTVExpiry checks that the minFinalCLTVExpiry is properly
|
|
|
|
// parsed.
|
2018-01-09 09:14:31 +03:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
{
|
2018-03-30 02:23:47 +03:00
|
|
|
data: []byte{
|
|
|
|
0x1, 0x2, 0x3, 0x4, 0x5,
|
|
|
|
0x6, 0x7, 0x8, 0x9, 0xa,
|
|
|
|
0xb, 0xc,
|
|
|
|
},
|
2018-01-09 09:14:31 +03:00
|
|
|
valid: true,
|
|
|
|
result: 38390726480144748,
|
|
|
|
},
|
|
|
|
{
|
2018-03-30 02:23:47 +03:00
|
|
|
data: []byte{
|
|
|
|
0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
|
|
|
|
0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
|
|
|
|
0xc, 0x94,
|
|
|
|
},
|
2018-01-09 09:14:31 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 02:23:47 +03:00
|
|
|
// TestParseMinFinalCLTVExpiry tests that were able to properly encode/decode
|
|
|
|
// the math.MaxUint64 integer without panicking.
|
|
|
|
func TestParseMaxUint64Expiry(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
expiry := uint64(math.MaxUint64)
|
|
|
|
|
|
|
|
expiryBytes := uint64ToBase32(expiry)
|
|
|
|
|
|
|
|
expiryReParse, err := base32ToUint64(expiryBytes)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unable to parse uint64: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if expiryReParse != expiry {
|
|
|
|
t.Fatalf("wrong expiry: expected %v got %v", expiry,
|
|
|
|
expiryReParse)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 09:14:31 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-27 07:42:35 +03:00
|
|
|
// TestParseRouteHint checks that the routing info is properly parsed.
|
|
|
|
func TestParseRouteHint(t *testing.T) {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
var testSingleHopData []byte
|
|
|
|
for _, r := range testSingleHop {
|
|
|
|
base256 := make([]byte, 51)
|
2018-03-27 07:42:35 +03:00
|
|
|
copy(base256[:33], r.NodeID.SerializeCompressed())
|
|
|
|
binary.BigEndian.PutUint64(base256[33:41], r.ChannelID)
|
|
|
|
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMSat)
|
2018-01-09 09:14:31 +03:00
|
|
|
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
2018-03-27 07:42:35 +03:00
|
|
|
binary.BigEndian.PutUint16(base256[49:51], r.CLTVExpiryDelta)
|
2018-01-09 09:14:31 +03:00
|
|
|
testSingleHopData = append(testSingleHopData, base256...)
|
|
|
|
}
|
|
|
|
testSingleHopData, _ = bech32.ConvertBits(testSingleHopData, 8, 5, true)
|
|
|
|
|
|
|
|
var testDoubleHopData []byte
|
|
|
|
for _, r := range testDoubleHop {
|
|
|
|
base256 := make([]byte, 51)
|
2018-03-27 07:42:35 +03:00
|
|
|
copy(base256[:33], r.NodeID.SerializeCompressed())
|
|
|
|
binary.BigEndian.PutUint64(base256[33:41], r.ChannelID)
|
|
|
|
binary.BigEndian.PutUint32(base256[41:45], r.FeeBaseMSat)
|
2018-01-09 09:14:31 +03:00
|
|
|
binary.BigEndian.PutUint32(base256[45:49], r.FeeProportionalMillionths)
|
2018-03-27 07:42:35 +03:00
|
|
|
binary.BigEndian.PutUint16(base256[49:51], r.CLTVExpiryDelta)
|
2018-01-09 09:14:31 +03:00
|
|
|
testDoubleHopData = append(testDoubleHopData, base256...)
|
|
|
|
}
|
|
|
|
testDoubleHopData, _ = bech32.ConvertBits(testDoubleHopData, 8, 5, true)
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
data []byte
|
|
|
|
valid bool
|
2019-02-19 11:09:01 +03:00
|
|
|
result []HopHint
|
2018-01-09 09:14:31 +03:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
data: []byte{0x0, 0x0, 0x0, 0x0},
|
|
|
|
valid: false, // data too short, not multiple of 51 bytes
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: []byte{},
|
|
|
|
valid: true,
|
2019-02-19 11:09:01 +03:00
|
|
|
result: []HopHint{},
|
2018-01-09 09:14:31 +03:00
|
|
|
},
|
|
|
|
{
|
|
|
|
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 {
|
2018-03-27 07:42:35 +03:00
|
|
|
routeHint, err := parseRouteHint(test.data)
|
2018-01-09 09:14:31 +03:00
|
|
|
if (err == nil) != test.valid {
|
|
|
|
t.Errorf("routing info decoding test %d failed: %v", i, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if test.valid {
|
2018-03-27 07:42:35 +03:00
|
|
|
if err := compareRouteHints(test.result, routeHint); err != nil {
|
2018-01-09 09:14:31 +03:00
|
|
|
t.Fatalf("test %d failed decoding routing info: %v", i, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-25 23:25:12 +03:00
|
|
|
|
|
|
|
// TestParseTaggedFields checks that tagged field data is correctly parsed or
|
|
|
|
// errors as expected.
|
|
|
|
func TestParseTaggedFields(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
netParams := &chaincfg.SimNetParams
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
data []byte
|
|
|
|
wantErr error
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "nil data",
|
|
|
|
data: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "empty data",
|
|
|
|
data: []byte{},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// Type 0xff cannot be encoded in a single 5-bit
|
|
|
|
// element, so it's technically invalid but
|
|
|
|
// parseTaggedFields doesn't error on non-5bpp
|
|
|
|
// compatible codes so we can use a code in tests which
|
|
|
|
// will never become known in the future.
|
|
|
|
name: "valid unknown field",
|
|
|
|
data: []byte{0xff, 0x00, 0x00},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "unknown field valid data",
|
|
|
|
data: []byte{0xff, 0x00, 0x01, 0xab},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "only type specified",
|
|
|
|
data: []byte{0x0d},
|
|
|
|
wantErr: ErrBrokenTaggedField,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "not enough bytes for len",
|
|
|
|
data: []byte{0x0d, 0x00},
|
|
|
|
wantErr: ErrBrokenTaggedField,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no bytes after len",
|
|
|
|
data: []byte{0x0d, 0x00, 0x01},
|
|
|
|
wantErr: ErrInvalidFieldLength,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "not enough bytes after len",
|
|
|
|
data: []byte{0x0d, 0x00, 0x02, 0x01},
|
|
|
|
wantErr: ErrInvalidFieldLength,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "not enough bytes after len with unknown type",
|
|
|
|
data: []byte{0xff, 0x00, 0x02, 0x01},
|
|
|
|
wantErr: ErrInvalidFieldLength,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
|
|
tc := tc // pin
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
var invoice Invoice
|
|
|
|
gotErr := parseTaggedFields(&invoice, tc.data, netParams)
|
|
|
|
if tc.wantErr != gotErr {
|
|
|
|
t.Fatalf("Unexpected error. want=%v got=%v",
|
|
|
|
tc.wantErr, gotErr)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|