zpay32: move the BOLT-11 compatible invoice format to zpay32 namespace

This commit is contained in:
Johan T. Halseth 2017-09-05 18:07:14 +02:00
parent 9d31b6c4fd
commit b645f02418
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
7 changed files with 55 additions and 316 deletions

@ -1,4 +1,4 @@
package invoice
package zpay32
import (
"fmt"

@ -1,4 +1,4 @@
package invoice
package zpay32
import (
"fmt"

@ -1,4 +1,4 @@
package invoice
package zpay32
import (
"bytes"

@ -1,4 +1,4 @@
package invoice
package zpay32
import (
"testing"

@ -1,4 +1,4 @@
package invoice_test
package zpay32_test
import (
"bytes"
@ -8,8 +8,8 @@ import (
"testing"
"time"
"github.com/lightningnetwork/lnd/invoice"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/zpay32"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/chaincfg/chainhash"
@ -45,7 +45,7 @@ var (
testPaymentHash [32]byte
testDescriptionHash [32]byte
testMessageSigner = invoice.MessageSigner{
testMessageSigner = zpay32.MessageSigner{
SignCompact: func(hash []byte) ([]byte, error) {
sig, err := btcec.SignCompact(btcec.S256(),
testPrivKey, hash, true)
@ -72,9 +72,9 @@ func TestDecodeEncode(t *testing.T) {
tests := []struct {
encodedInvoice string
valid bool
decodedInvoice *invoice.Invoice
decodedInvoice *zpay32.Invoice
skipEncoding bool
beforeEncoding func(*invoice.Invoice)
beforeEncoding func(*zpay32.Invoice)
}{
{
encodedInvoice: "asdsaddnasdnas", // no hrp
@ -112,7 +112,7 @@ func TestDecodeEncode(t *testing.T) {
// no payment hash set
encodedInvoice: "lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsjv38luh6p6s2xrv3mzvlmzaya43376h0twal5ax0k6p47498hp3hnaymzhsn424rxqjs0q7apn26yrhaxltq3vzwpqj9nc2r3kzwccsplnq470",
valid: false,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -124,7 +124,7 @@ func TestDecodeEncode(t *testing.T) {
// Both Description and DescriptionHash set.
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs03vghs8y0kuj4ulrzls8ln7fnm9dk7sjsnqmghql6hd6jut36clkqpyuq0s5m6fhureyz0szx2qjc8hkgf4xc2hpw8jpu26jfeyvf4cpga36gt",
valid: false,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -138,7 +138,7 @@ func TestDecodeEncode(t *testing.T) {
// Neither Description nor DescriptionHash set.
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqn2rne0kagfl4e0xag0w6hqeg2dwgc54hrm9m0auw52dhwhwcu559qav309h598pyzn69wh2nqauneyyesnpmaax0g6acr8lh9559jmcquyq5a9",
valid: false,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -150,7 +150,7 @@ func TestDecodeEncode(t *testing.T) {
// Has a few unknown fields, should just be ignored.
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqtq2v93xxer9vczq8v93xxeqv72xr42ca60022jqu6fu73n453tmnr0ukc0pl0t23w7eavtensjz0j2wcu7nkxhfdgp9y37welajh5kw34mq7m4xuay0a72cwec8qwgqt5vqht",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -164,7 +164,7 @@ func TestDecodeEncode(t *testing.T) {
// Ignore unknown witness version in fallback address.
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpppw508d6qejxtdg4y5r3zarvary0c5xw7k8txqv6x0a75xuzp0zsdzk5hq6tmfgweltvs6jk5nhtyd9uqksvr48zga9mw08667w8264gkspluu66jhtcmct36nx363km6cquhhv2cpc6q43r",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -178,7 +178,7 @@ func TestDecodeEncode(t *testing.T) {
// Ignore fields with unknown lengths.
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqpp3qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqshp38yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66np3q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfy8huflvs2zwkymx47cszugvzn5v64ahemzzlmm62rpn9l9rm05h35aceq00tkt296289wepws9jh4499wq2l0vk6xcxffd90dpuqchqqztyayq",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat24BTC,
Timestamp: time.Unix(1503429093, 0),
@ -192,14 +192,14 @@ func TestDecodeEncode(t *testing.T) {
// Please make a donation of any amount using rhash 0001020304050607080900010203040506070809000102030405060708090102 to me @03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad
encodedInvoice: "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
Timestamp: time.Unix(1496314658, 0),
PaymentHash: &testPaymentHash,
Description: &testPleaseConsider,
Destination: testPubKey,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -210,7 +210,7 @@ func TestDecodeEncode(t *testing.T) {
// Same as above, pubkey set in 'n' field.
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat24BTC,
Timestamp: time.Unix(1503429093, 0),
@ -223,7 +223,7 @@ func TestDecodeEncode(t *testing.T) {
// Please send $3 for a cup of coffee to the same peer, within 1 minute
encodedInvoice: "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat2500uBTC,
Timestamp: time.Unix(1496314658, 0),
@ -232,7 +232,7 @@ func TestDecodeEncode(t *testing.T) {
Destination: testPubKey,
Expiry: &testExpiry60,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -243,7 +243,7 @@ func TestDecodeEncode(t *testing.T) {
// Now send $24 for an entire list of things (hashed)
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -251,7 +251,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash,
Destination: testPubKey,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -262,7 +262,7 @@ func TestDecodeEncode(t *testing.T) {
// The same, on testnet, with a fallback address mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP
encodedInvoice: "lntb20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3x9et2e20v6pu37c5d9vax37wxq72un98k6vcx9fz94w0qf237cm2rqv9pmn5lnexfvf5579slr4zq3u8kmczecytdx0xg9rwzngp7e6guwqpqlhssu04sucpnz4axcv2dstmknqq6jsk2l",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.TestNet3Params,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -271,7 +271,7 @@ func TestDecodeEncode(t *testing.T) {
Destination: testPubKey,
FallbackAddr: testAddrTestnet,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -282,7 +282,7 @@ func TestDecodeEncode(t *testing.T) {
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to get to node 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85frzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvncsk57n4v9ehw86wq8fzvjejhv9z3w3q5zh6qkql005x9xl240ch23jk79ujzvr4hsmmafyxghpqe79psktnjl668ntaf4ne7ucs5csqh5mnnk",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -290,7 +290,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash,
Destination: testPubKey,
FallbackAddr: testRustyAddr,
RoutingInfo: []invoice.ExtraRoutingInfo{
RoutingInfo: []zpay32.ExtraRoutingInfo{
{
PubKey: testRoutingInfoPubkey,
ShortChanID: 0x0102030405060708,
@ -299,7 +299,7 @@ func TestDecodeEncode(t *testing.T) {
},
},
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -310,7 +310,7 @@ func TestDecodeEncode(t *testing.T) {
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqqqqqqq7qqzqfnlkwydm8rg30gjku7wmxmk06sevjp53fmvrcfegvwy7d5443jvyhxsel0hulkstws7vqv400q4j3wgpk4crg49682hr4scqvmad43cqd5m7tf",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -318,7 +318,7 @@ func TestDecodeEncode(t *testing.T) {
DescriptionHash: &testDescriptionHash,
Destination: testPubKey,
FallbackAddr: testRustyAddr,
RoutingInfo: []invoice.ExtraRoutingInfo{
RoutingInfo: []zpay32.ExtraRoutingInfo{
{
PubKey: testRoutingInfoPubkey,
ShortChanID: 0x0102030405060708,
@ -333,7 +333,7 @@ func TestDecodeEncode(t *testing.T) {
},
},
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -344,7 +344,7 @@ func TestDecodeEncode(t *testing.T) {
// On mainnet, with fallback (p2sh) address 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kk822r8plup77n9yq5ep2dfpcydrjwzxs0la84v3tfw43t3vqhek7f05m6uf8lmfkjn7zv7enn76sq65d8u9lxav2pl6x3xnc2ww3lqpagnh0u",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -353,7 +353,7 @@ func TestDecodeEncode(t *testing.T) {
Destination: testPubKey,
FallbackAddr: testAddrMainnetP2SH,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -364,7 +364,7 @@ func TestDecodeEncode(t *testing.T) {
// On mainnet, with fallback (p2wpkh) address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfppqw508d6qejxtdg4y5r3zarvary0c5xw7kknt6zz5vxa8yh8jrnlkl63dah48yh6eupakk87fjdcnwqfcyt7snnpuz7vp83txauq4c60sys3xyucesxjf46yqnpplj0saq36a554cp9wt865",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -373,7 +373,7 @@ func TestDecodeEncode(t *testing.T) {
Destination: testPubKey,
FallbackAddr: testAddrMainnetP2WPKH,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -384,7 +384,7 @@ func TestDecodeEncode(t *testing.T) {
// On mainnet, with fallback (p2wsh) address bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3
encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qvnjha2auylmwrltv2pkp2t22uy8ura2xsdwhq5nm7s574xva47djmnj2xeycsu7u5v8929mvuux43j0cqhhf32wfyn2th0sv4t9x55sppz5we8",
valid: true,
decodedInvoice: &invoice.Invoice{
decodedInvoice: &zpay32.Invoice{
Net: &chaincfg.MainNetParams,
MilliSat: &testMillisat20mBTC,
Timestamp: time.Unix(1496314658, 0),
@ -393,7 +393,7 @@ func TestDecodeEncode(t *testing.T) {
Destination: testPubKey,
FallbackAddr: testAddrMainnetP2WSH,
},
beforeEncoding: func(i *invoice.Invoice) {
beforeEncoding: func(i *zpay32.Invoice) {
// Since this destination pubkey was recovered
// from the signature, we must set it nil before
// encoding to get back the same invoice string.
@ -403,7 +403,7 @@ func TestDecodeEncode(t *testing.T) {
}
for i, test := range tests {
invoice, err := invoice.Decode(test.encodedInvoice)
invoice, err := zpay32.Decode(test.encodedInvoice)
if (err == nil) != test.valid {
t.Errorf("Decoding test %d failed: %v", i, err)
return
@ -448,42 +448,42 @@ func TestNewInvoice(t *testing.T) {
t.Parallel()
tests := []struct {
newInvoice func() (*invoice.Invoice, error)
newInvoice func() (*zpay32.Invoice, error)
encodedInvoice string
valid bool
}{
{
// Both Description and DescriptionHash set.
newInvoice: func() (*invoice.Invoice, error) {
return invoice.NewInvoice(&chaincfg.MainNetParams,
newInvoice: func() (*zpay32.Invoice, error) {
return zpay32.NewInvoice(&chaincfg.MainNetParams,
testPaymentHash, time.Unix(1496314658, 0),
invoice.DescriptionHash(testDescriptionHash),
invoice.Description(testPleaseConsider))
zpay32.DescriptionHash(testDescriptionHash),
zpay32.Description(testPleaseConsider))
},
valid: false, // Both Description and DescriptionHash set.
},
{
// 'n' field set.
newInvoice: func() (*invoice.Invoice, error) {
return invoice.NewInvoice(&chaincfg.MainNetParams,
newInvoice: func() (*zpay32.Invoice, error) {
return zpay32.NewInvoice(&chaincfg.MainNetParams,
testPaymentHash, time.Unix(1503429093, 0),
invoice.Amount(testMillisat24BTC),
invoice.Description(testEmptyString),
invoice.Destination(testPubKey))
zpay32.Amount(testMillisat24BTC),
zpay32.Description(testEmptyString),
zpay32.Destination(testPubKey))
},
valid: true,
encodedInvoice: "lnbc241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66jd3m5klcwhq68vdsmx2rjgxeay5v0tkt2v5sjaky4eqahe4fx3k9sqavvce3capfuwv8rvjng57jrtfajn5dkpqv8yelsewtljwmmycq62k443",
},
{
// On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
newInvoice: func() (*invoice.Invoice, error) {
return invoice.NewInvoice(&chaincfg.MainNetParams,
newInvoice: func() (*zpay32.Invoice, error) {
return zpay32.NewInvoice(&chaincfg.MainNetParams,
testPaymentHash, time.Unix(1496314658, 0),
invoice.Amount(testMillisat20mBTC),
invoice.DescriptionHash(testDescriptionHash),
invoice.FallbackAddr(testRustyAddr),
invoice.RoutingInfo(
[]invoice.ExtraRoutingInfo{
zpay32.Amount(testMillisat20mBTC),
zpay32.DescriptionHash(testDescriptionHash),
zpay32.FallbackAddr(testRustyAddr),
zpay32.RoutingInfo(
[]zpay32.ExtraRoutingInfo{
{
PubKey: testRoutingInfoPubkey,
ShortChanID: 0x0102030405060708,
@ -525,7 +525,7 @@ func TestNewInvoice(t *testing.T) {
}
}
func compareInvoices(expected, actual *invoice.Invoice) error {
func compareInvoices(expected, actual *zpay32.Invoice) error {
if !reflect.DeepEqual(expected.Net, actual.Net) {
return fmt.Errorf("expected net %v, got %v",
expected.Net, actual.Net)

@ -1,150 +0,0 @@
package zpay32
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"io"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcutil"
"github.com/tv42/zbase32"
)
// invoiceSize is the size of an encoded invoice without the added check-sum.
// The size of broken down as follows: 33-bytes (destination pub key), 32-bytes
// (payment hash), 8-bytes for the payment amount in satoshis.
const invoiceSize = 33 + 32 + 8
// ErrCheckSumMismatch is returned byt he Decode function fi when
// decoding an encoded invoice, the checksum doesn't match indicating
// an error somewhere in the bitstream.
var ErrCheckSumMismatch = errors.New("the checksum is incorrect")
// ErrDataTooShort is returned by the Decode function if when
// decoding an encoded payment request, the number of bytes decoded
// is too few for a valid invoice indicating invalid input.
var ErrDataTooShort = errors.New("the decoded data is too short")
// PaymentRequest is a bare-bones invoice for a payment within the Lightning
// Network. With the details of the invoice, the sender has all the data
// necessary to send a payment to the recipient.
type PaymentRequest struct {
// Destination is the public key of the node to be paid.
Destination *btcec.PublicKey
// PaymentHash is the has to use within the HTLC extended throughout
// the payment path to the destination.
PaymentHash [32]byte
// Amount is the amount to be sent to the destination expressed in
// satoshis.
Amount btcutil.Amount
}
// castagnoli is an initialized crc32 checksum generated which Castagnoli's
// polynomial.
var castagnoli = crc32.MakeTable(crc32.Castagnoli)
// checkSum calculates a 4-byte crc32 checksum of the passed data. The returned
// uint32 is serialized as a big-endian integer.
func checkSum(data []byte) []byte {
crc := crc32.New(castagnoli)
crc.Write(data)
return crc.Sum(nil)
}
// Encode encodes the passed payment request using zbase32 with an added 4-byte
// crc32 checksum. The resulting encoding is 77-bytes long and consists of 124
// ASCII characters.
// TODO(roasbeef): add version byte?
func Encode(payReq *PaymentRequest) string {
var (
invoiceBytes [invoiceSize]byte
n int
)
// First copy each of the elements of the payment request into the
// buffer. Creating a stream that resembles: dest || r_hash || amt
n += copy(invoiceBytes[:], payReq.Destination.SerializeCompressed())
n += copy(invoiceBytes[n:], payReq.PaymentHash[:])
binary.BigEndian.PutUint64(invoiceBytes[n:], uint64(payReq.Amount))
// Next, we append the checksum to the end of the buffer which covers
// the serialized payment request.
b := append(invoiceBytes[:], checkSum(invoiceBytes[:])...)
// Finally encode the raw bytes as a zbase32 encoded string.
return zbase32.EncodeToString(b)
}
// Decode attempts to decode the zbase32 encoded payment request. If the
// trailing checksum doesn't match, then an error is returned.
func Decode(payData string) (*PaymentRequest, error) {
if payData == "" {
return nil, fmt.Errorf("encoded payment request must be a " +
"non-empty string")
}
// First we decode the zbase32 encoded string into a series of raw
// bytes.
payReqBytes, err := zbase32.DecodeString(payData)
if err != nil {
return nil, err
}
// Check if there are at least enough bytes to represent the invoice
// and the checksum.
if len(payReqBytes) < invoiceSize+crc32.Size {
return nil, ErrDataTooShort
}
// With the bytes decoded, we verify the checksum to ensure the
// payment request wasn't altered in its decoded form.
invoiceBytes := payReqBytes[:invoiceSize]
generatedSum := checkSum(invoiceBytes)
// If the checksums don't match, then we return an error to the
// possibly detected error.
encodedSum := payReqBytes[invoiceSize:]
if !bytes.Equal(encodedSum, generatedSum) {
return nil, ErrCheckSumMismatch
}
// Otherwise, we've verified the integrity of the encoded payment
// request and can safely decode the payReq, passing it back up to the
// caller.
invoiceReader := bytes.NewReader(invoiceBytes)
return decodePaymentRequest(invoiceReader)
}
func decodePaymentRequest(r io.Reader) (*PaymentRequest, error) {
var err error
i := &PaymentRequest{}
var pubKey [33]byte
if _, err := io.ReadFull(r, pubKey[:]); err != nil {
return nil, err
}
i.Destination, err = btcec.ParsePubKey(pubKey[:], btcec.S256())
if err != nil {
return nil, err
}
if _, err := io.ReadFull(r, i.PaymentHash[:]); err != nil {
return nil, err
}
var amt [8]byte
if _, err := io.ReadFull(r, amt[:]); err != nil {
return nil, err
}
i.Amount = btcutil.Amount(binary.BigEndian.Uint64(amt[:]))
return i, nil
}

@ -1,111 +0,0 @@
package zpay32
import (
"bytes"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcutil"
)
var (
testPrivKey = []byte{
0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
}
_, testPubKey = btcec.PrivKeyFromBytes(btcec.S256(), testPrivKey)
testPayHash = [32]byte{
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
}
)
func TestEncodeDecode(t *testing.T) {
t.Parallel()
testPubKey.Curve = nil
tests := []struct {
version int
payReq PaymentRequest
encoding string
}{
{
payReq: PaymentRequest{
Destination: testPubKey,
PaymentHash: testPayHash,
Amount: btcutil.Amount(50000),
},
encoding: "yj8p9uh793syszrd4giu66gtsqprp47cc6cqbdo3" +
"qaxi7sr63y6bbphw8bx148zzipg3rh6t1btadpnxf7z1mnfd76" +
"hsw1eaoca3ot4uyyyyyyyyydbib998je6o",
version: 1,
},
}
for i, test := range tests {
// First ensure encoding the test payment request string in the
// specified encoding.
encodedReq := Encode(&test.payReq)
if encodedReq != test.encoding {
t.Fatalf("encoding mismatch for %v: expected %v got %v",
spew.Sdump(test.payReq), test.encoding, encodedReq)
}
// Next ensure the correctness of the transformation in the
// other direction.
decodedReq, err := Decode(test.encoding)
if err != nil {
t.Fatalf("unable to decode invoice #%v: %v", i, err)
}
if !test.payReq.Destination.IsEqual(decodedReq.Destination) {
t.Fatalf("test #%v:, destination mismatch for decoded request: "+
"expected %v got %v", i, test.payReq.Destination,
decodedReq.Destination)
}
if !bytes.Equal(test.payReq.PaymentHash[:], decodedReq.PaymentHash[:]) {
t.Fatalf("test #%v: payment hash mismatch for decoded request: "+
"expected %x got %x", i, test.payReq.PaymentHash,
decodedReq.PaymentHash)
}
if test.payReq.Amount != decodedReq.Amount {
t.Fatalf("test #%v: amount mismatch for decoded request: "+
"expected %x got %x", i, test.payReq.Amount,
decodedReq.Amount)
}
}
}
func TestChecksumMismatch(t *testing.T) {
t.Parallel()
// We start with a pre-encoded invoice, which has a valid checksum.
payReqString := []byte("ycyr8brdjic6oak3bemztc5nupo56y3itq4z5q4qxwb35orf7fmj5phw8bx148zzipg3rh6t1btadpnxf7z1mnfd76hsw1eaoca3ot4uyyyyyyyyydbibt79jo1o")
// To modify the resulting checksum, we shift a few of the bytes within the
// string itself.
payReqString[1] = 98
payReqString[5] = 102
if _, err := Decode(string(payReqString)); err != ErrCheckSumMismatch {
t.Fatalf("decode should fail with checksum mismatch, instead: %v", err)
}
}
func TestDecodeTooShort(t *testing.T) {
t.Parallel()
// We start with a pre-encoded too-short string.
payReqString := "ycyr8brdji"
if _, err := Decode(payReqString); err != ErrDataTooShort {
t.Fatalf("decode should fail with data too short, instead: %v", err)
}
}