github.com/decred/dcrlnd@v0.7.6/zpay32/invoice_test.go (about)

     1  package zpay32
     2  
     3  // We use package `zpay32` rather than `zpay32_test` in order to share test data
     4  // with the internal tests.
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/hex"
     9  	"fmt"
    10  	"reflect"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/decred/dcrd/chaincfg/chainhash"
    16  	"github.com/decred/dcrd/chaincfg/v3"
    17  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    18  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    19  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    20  	"github.com/decred/dcrlnd/lnwire"
    21  )
    22  
    23  var (
    24  	testMilliAt24DCR    = lnwire.MilliAtom(2400000000000)
    25  	testMilliAt2500uDCR = lnwire.MilliAtom(250000000)
    26  	testMilliAt25mDCR   = lnwire.MilliAtom(2500000000)
    27  	testMilliAt20mDCR   = lnwire.MilliAtom(2000000000)
    28  
    29  	testPaymentHash = [32]byte{
    30  		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    31  		0x08, 0x09, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
    32  		0x06, 0x07, 0x08, 0x09, 0x00, 0x01, 0x02, 0x03,
    33  		0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x01, 0x02,
    34  	}
    35  
    36  	testPaymentAddr = [32]byte{
    37  		0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x01, 0x02,
    38  		0x06, 0x07, 0x08, 0x09, 0x00, 0x01, 0x02, 0x03,
    39  		0x08, 0x09, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
    40  		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    41  	}
    42  
    43  	specPaymentAddr = [32]byte{
    44  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
    45  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
    46  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
    47  		0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
    48  	}
    49  
    50  	testEmptyString    = ""
    51  	testCupOfCoffee    = "1 cup coffee"
    52  	testCoffeeBeans    = "coffee beans"
    53  	testCupOfNonsense  = "ナンセンス 1杯"
    54  	testPleaseConsider = "Please consider supporting this project"
    55  
    56  	testPrivKeyBytes, _ = hex.DecodeString("e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734")
    57  	testPrivKey         = secp256k1.PrivKeyFromBytes(testPrivKeyBytes)
    58  	testPubKey          = testPrivKey.PubKey()
    59  
    60  	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"))
    61  
    62  	testExpiry0  = time.Duration(0) * time.Second
    63  	testExpiry60 = time.Duration(60) * time.Second
    64  
    65  	testAddrTestnet, _     = stdaddr.DecodeAddress("TsR28UZRprhgQQhzWns2M6cAwchrNVvbYq2", chaincfg.TestNet3Params())
    66  	testRustyAddr, _       = stdaddr.DecodeAddress("DsQxuVRvS4eaJ42dhQEsCXauMWjvopWgrVg", chaincfg.MainNetParams())
    67  	testAddrMainnetP2SH, _ = stdaddr.DecodeAddress("DcXTb4QtmnyRsnzUVViYQawqFE5PuYTdX2C", chaincfg.MainNetParams())
    68  
    69  	testHopHintPubkeyBytes1, _ = hex.DecodeString("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
    70  	testHopHintPubkey1, _      = secp256k1.ParsePubKey(testHopHintPubkeyBytes1)
    71  	testHopHintPubkeyBytes2, _ = hex.DecodeString("039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255")
    72  	testHopHintPubkey2, _      = secp256k1.ParsePubKey(testHopHintPubkeyBytes2)
    73  
    74  	testSingleHop = []HopHint{
    75  		{
    76  			NodeID:                    testHopHintPubkey1,
    77  			ChannelID:                 0x0102030405060708,
    78  			FeeBaseMAtoms:             0,
    79  			FeeProportionalMillionths: 20,
    80  			CLTVExpiryDelta:           3,
    81  		},
    82  	}
    83  	testDoubleHop = []HopHint{
    84  		{
    85  			NodeID:                    testHopHintPubkey1,
    86  			ChannelID:                 0x0102030405060708,
    87  			FeeBaseMAtoms:             1,
    88  			FeeProportionalMillionths: 20,
    89  			CLTVExpiryDelta:           3,
    90  		},
    91  		{
    92  			NodeID:                    testHopHintPubkey2,
    93  			ChannelID:                 0x030405060708090a,
    94  			FeeBaseMAtoms:             2,
    95  			FeeProportionalMillionths: 30,
    96  			CLTVExpiryDelta:           4,
    97  		},
    98  	}
    99  
   100  	testMessageSigner = MessageSigner{
   101  		SignCompact: func(msg []byte) ([]byte, error) {
   102  			hash := chainhash.HashB(msg)
   103  			return ecdsa.SignCompact(testPrivKey, hash, true), nil
   104  		},
   105  	}
   106  
   107  	emptyFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
   108  
   109  	// Must be initialized in init().
   110  	testDescriptionHash [32]byte
   111  )
   112  
   113  func init() {
   114  	copy(testDescriptionHash[:], testDescriptionHashSlice)
   115  }
   116  
   117  // TestDecodeEncode tests that an encoded invoice gets decoded into the expected
   118  // Invoice object, and that reencoding the decoded invoice gets us back to the
   119  // original encoded string.
   120  func TestDecodeEncode(t *testing.T) {
   121  	t.Parallel()
   122  
   123  	tests := []struct {
   124  		encodedInvoice string
   125  		valid          bool
   126  		decodedInvoice func() *Invoice
   127  		skipEncoding   bool
   128  		beforeEncoding func(*Invoice)
   129  	}{
   130  		{
   131  			encodedInvoice: "asdsaddnasdnas", // no hrp
   132  			valid:          false,
   133  		},
   134  		{
   135  			encodedInvoice: "lndcr1abcde", // too short
   136  			valid:          false,
   137  		},
   138  		{
   139  			encodedInvoice: "1asdsaddnv4wudz", // empty hrp
   140  			valid:          false,
   141  		},
   142  		{
   143  			encodedInvoice: "ln1asdsaddnv4wudz", // hrp too short
   144  			valid:          false,
   145  		},
   146  		{
   147  			encodedInvoice: "llts1dasdajtkfl6", // no "ln" prefix
   148  			valid:          false,
   149  		},
   150  		{
   151  			encodedInvoice: "lnts1dasdapukz0w", // invalid segwit prefix
   152  			valid:          false,
   153  		},
   154  		{
   155  			encodedInvoice: "lndcrm1aaamcu25m", // invalid amount
   156  			valid:          false,
   157  		},
   158  		{
   159  			encodedInvoice: "lndcr1000000000m1", // invalid amount
   160  			valid:          false,
   161  		},
   162  		{
   163  			encodedInvoice: "lndcr20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfqqepvrhrm9s57hejg0p662ur5j5cr03890fa7k2pypgttmh4897d3raaq85a293e9jpuqwl0rnfuwzam7yr8e690nd2ypcq9hlkdwdvycqjhlqg5", // empty fallback address field
   164  			valid:          false,
   165  		},
   166  		{
   167  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85frqg00000000j9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqsj5cgu", // invalid routing info length: not a multiple of 51
   168  			valid:          false,
   169  		},
   170  		{
   171  			// no payment hash set
   172  			encodedInvoice: "lndcr20m1pvjluezhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsqdnpkqmwa5g78xlhq029a2238kjf9klaes6pc6qvgasvvz729r0zjurxzvj5ssr2ypjs9af6qdfhkdrwwf0urkhh8p8lx4p65jq0tkcpfdajj0",
   173  			valid:          false,
   174  			decodedInvoice: func() *Invoice {
   175  				return &Invoice{
   176  					Net:             chaincfg.MainNetParams(),
   177  					MilliAt:         &testMilliAt20mDCR,
   178  					Timestamp:       time.Unix(1496314658, 0),
   179  					DescriptionHash: &testDescriptionHash,
   180  					Destination:     testPubKey,
   181  					Features:        emptyFeatures,
   182  				}
   183  			},
   184  		},
   185  		{
   186  			// Both Description and DescriptionHash set.
   187  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hs0pwljxh5kezjcylatfknd2wgpc5kyayf8wntsjsaxhyw3cw0a6s9ue5y4lkeja470cldvwx075d2s06acaphjsnc4mq74nzcu0lcr0qqkady8f",
   188  			valid:          false,
   189  			decodedInvoice: func() *Invoice {
   190  				return &Invoice{
   191  					Net:             chaincfg.MainNetParams(),
   192  					MilliAt:         &testMilliAt20mDCR,
   193  					Timestamp:       time.Unix(1496314658, 0),
   194  					PaymentHash:     &testPaymentHash,
   195  					Description:     &testPleaseConsider,
   196  					DescriptionHash: &testDescriptionHash,
   197  					Destination:     testPubKey,
   198  					Features:        emptyFeatures,
   199  				}
   200  			},
   201  		},
   202  		{
   203  			// Neither Description nor DescriptionHash set.
   204  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq8puwce8w28d8elye424a7y7l845llxkwtku8sjpf2c42ltqd7vehefdhpr0zgq4dxjl36savrdn0perhuu2n9dxhd30suv24rltkdtcpgx6lc2",
   205  			valid:          false,
   206  			decodedInvoice: func() *Invoice {
   207  				return &Invoice{
   208  					Net:         chaincfg.MainNetParams(),
   209  					MilliAt:     &testMilliAt20mDCR,
   210  					Timestamp:   time.Unix(1496314658, 0),
   211  					PaymentHash: &testPaymentHash,
   212  					Destination: testPubKey,
   213  					Features:    emptyFeatures,
   214  				}
   215  			},
   216  		},
   217  		{
   218  			// Has a few unknown fields, should just be ignored.
   219  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaqtq2v93xxer9vczq8v93xxeqy2e8qjln3aelvnu077437ta5l3c6eq2ag7ervsa6l24kaanjgusy8984984ykpv73jent2c2c0zfj6sjrfraz52dq4f77hup0azr0cgpak5p5f",
   220  			valid:          true,
   221  			decodedInvoice: func() *Invoice {
   222  				return &Invoice{
   223  					Net:         chaincfg.MainNetParams(),
   224  					MilliAt:     &testMilliAt20mDCR,
   225  					Timestamp:   time.Unix(1496314658, 0),
   226  					PaymentHash: &testPaymentHash,
   227  					Description: &testPleaseConsider,
   228  					Destination: testPubKey,
   229  					Features:    emptyFeatures,
   230  				}
   231  			},
   232  			skipEncoding: true, // Skip encoding since we don't have the unknown fields to encode.
   233  		},
   234  		{
   235  			// Ignore unknown witness version in fallback address.
   236  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsfpzpw508d6qejxtdg4y5r3zarvary0c5xw7kqt00sjdsfuzdw87n0rlvqw0h6ul6m66s38cr3wfh3epe7e3qn8e88wjdu2n39aalhnxtpx8faczr8g4uhktkkedms76qcs0tuqaju2egppmk8nq",
   237  			valid:          true,
   238  			decodedInvoice: func() *Invoice {
   239  				return &Invoice{
   240  					Net:             chaincfg.MainNetParams(),
   241  					MilliAt:         &testMilliAt20mDCR,
   242  					Timestamp:       time.Unix(1496314658, 0),
   243  					PaymentHash:     &testPaymentHash,
   244  					DescriptionHash: &testDescriptionHash,
   245  					Destination:     testPubKey,
   246  					Features:        emptyFeatures,
   247  				}
   248  			},
   249  			skipEncoding: true, // Skip encoding since we don't have the unknown fields to encode.
   250  		},
   251  		{
   252  			// Ignore fields with unknown lengths.
   253  			encodedInvoice: "lndcr241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqpp3qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hshp38yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66np3q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfkq0wl2r2kwnhqc7mm8wmseekc2wj0rsj9nssxprqwerqfgx9lau5samrfeenge47pfjlectk4yj9axr42jpcl53e43wdlxln6amlmmcpat04z9",
   254  			valid:          true,
   255  			decodedInvoice: func() *Invoice {
   256  				return &Invoice{
   257  					Net:             chaincfg.MainNetParams(),
   258  					MilliAt:         &testMilliAt24DCR,
   259  					Timestamp:       time.Unix(1503429093, 0),
   260  					PaymentHash:     &testPaymentHash,
   261  					Destination:     testPubKey,
   262  					DescriptionHash: &testDescriptionHash,
   263  					Features:        emptyFeatures,
   264  				}
   265  			},
   266  			skipEncoding: true, // Skip encoding since we don't have the unknown fields to encode.
   267  		},
   268  		{
   269  			// Invoice with no amount.
   270  			encodedInvoice: "lndcr1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsmvp0ygkvzd3zh9wkfj59cuze0se5fzuh4f7rysdukv68n6fafa45sudrzg8d33paaw50zczd5mzmppqaalvzneu0yd3zfrvzhnfzpkgppyrza2",
   271  			valid:          true,
   272  			decodedInvoice: func() *Invoice {
   273  				return &Invoice{
   274  					Net:         chaincfg.MainNetParams(),
   275  					Timestamp:   time.Unix(1496314658, 0),
   276  					PaymentHash: &testPaymentHash,
   277  					Description: &testCupOfCoffee,
   278  					Destination: testPubKey,
   279  					Features:    emptyFeatures,
   280  				}
   281  			},
   282  			beforeEncoding: func(i *Invoice) {
   283  				// Since this destination pubkey was recovered
   284  				// from the signature, we must set it nil before
   285  				// encoding to get back the same invoice string.
   286  				i.Destination = nil
   287  			},
   288  		},
   289  		{
   290  			// Please make a donation of any amount using rhash 0001020304050607080900010203040506070809000102030405060708090102 to me @03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad
   291  			encodedInvoice: "lndcr1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq708hahqy65t7nzxer9t26yxkxxmh84rc7u7hfv2wxrjjkf5v8wqz3tf472p8dagx7u2fayqzfp8ycekwmfmacz5g83tunacfzw48m0sq4jcr74",
   292  			valid:          true,
   293  			decodedInvoice: func() *Invoice {
   294  				return &Invoice{
   295  					Net:         chaincfg.MainNetParams(),
   296  					Timestamp:   time.Unix(1496314658, 0),
   297  					PaymentHash: &testPaymentHash,
   298  					Description: &testPleaseConsider,
   299  					Destination: testPubKey,
   300  					Features:    emptyFeatures,
   301  				}
   302  			},
   303  			beforeEncoding: func(i *Invoice) {
   304  				// Since this destination pubkey was recovered
   305  				// from the signature, we must set it nil before
   306  				// encoding to get back the same invoice string.
   307  				i.Destination = nil
   308  			},
   309  		},
   310  		{
   311  			// Same as above, pubkey set in 'n' field.
   312  			encodedInvoice: "lndcr241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66husxpmqj9fh878hrkccqzvazqk2mhj0fdtjyngvhz5vje86eh39zu8cmp7k0kml38p3d3ujyuuhqe32kfgdt98t5e8r74xmwk53u5mqqm45579",
   313  			valid:          true,
   314  			decodedInvoice: func() *Invoice {
   315  				return &Invoice{
   316  					Net:         chaincfg.MainNetParams(),
   317  					MilliAt:     &testMilliAt24DCR,
   318  					Timestamp:   time.Unix(1503429093, 0),
   319  					PaymentHash: &testPaymentHash,
   320  					Destination: testPubKey,
   321  					Description: &testEmptyString,
   322  					Features:    emptyFeatures,
   323  				}
   324  			},
   325  		},
   326  		{
   327  			// Please send $3 for a cup of coffee to the same peer, within 1 minute
   328  			encodedInvoice: "lndcr2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpumkqh4xxuwhghf0dy5mglqnnttyg46a3ursmwv33dlwvmvkt9d8z9k7h4nhm0uun3a8hly8e92hd926j0tm0afrnzqeyapnlqhrx6cugphffhw0",
   329  			valid:          true,
   330  			decodedInvoice: func() *Invoice {
   331  				i, _ := NewInvoice(
   332  					chaincfg.MainNetParams(),
   333  					testPaymentHash,
   334  					time.Unix(1496314658, 0),
   335  					Amount(testMilliAt2500uDCR),
   336  					Description(testCupOfCoffee),
   337  					Destination(testPubKey),
   338  					Expiry(testExpiry60))
   339  				return i
   340  			},
   341  			beforeEncoding: func(i *Invoice) {
   342  				// Since this destination pubkey was recovered
   343  				// from the signature, we must set it nil before
   344  				// encoding to get back the same invoice string.
   345  				i.Destination = nil
   346  			},
   347  		},
   348  		{
   349  			// Please send 0.0025 DCR for a cup of nonsense (ナンセンス 1杯) to the same peer, within 1 minute
   350  			encodedInvoice: "lndcr2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpu20gghk3mf6c570cuquvdxd0p0wym6pdcmcdmpnnhjs557vvxzvprplpwrzxef7a6emdtkv5vjeqg5mk4ea55u4wt0qk6pp0gc67w4zgq7xl5pn",
   351  			valid:          true,
   352  			decodedInvoice: func() *Invoice {
   353  				i, _ := NewInvoice(
   354  					chaincfg.MainNetParams(),
   355  					testPaymentHash,
   356  					time.Unix(1496314658, 0),
   357  					Amount(testMilliAt2500uDCR),
   358  					Description(testCupOfNonsense),
   359  					Destination(testPubKey),
   360  					Expiry(testExpiry60))
   361  				return i
   362  			},
   363  			beforeEncoding: func(i *Invoice) {
   364  				// Since this destination pubkey was recovered
   365  				// from the signature, we must set it nil before
   366  				// encoding to get back the same invoice string.
   367  				i.Destination = nil
   368  			},
   369  		},
   370  		{
   371  			// Now send $24 for an entire list of things (hashed)
   372  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hs3uzs6up5wjzjy7pl352h0rd0tujcwrvej3035gs59x2funkpx44z7r3ku04xf8xgvlxrc4dhaut5t9yxvwv2kvdge6g25zk6p87550qp0c2rnh",
   373  			valid:          true,
   374  			decodedInvoice: func() *Invoice {
   375  				return &Invoice{
   376  					Net:             chaincfg.MainNetParams(),
   377  					MilliAt:         &testMilliAt20mDCR,
   378  					Timestamp:       time.Unix(1496314658, 0),
   379  					PaymentHash:     &testPaymentHash,
   380  					DescriptionHash: &testDescriptionHash,
   381  					Destination:     testPubKey,
   382  					Features:        emptyFeatures,
   383  				}
   384  			},
   385  			beforeEncoding: func(i *Invoice) {
   386  				// Since this destination pubkey was recovered
   387  				// from the signature, we must set it nil before
   388  				// encoding to get back the same invoice string.
   389  				i.Destination = nil
   390  			},
   391  		},
   392  		{
   393  			// The same, on testnet, with a fallback address TsR28UZRprhgQQhzWns2M6cAwchrNVvbYq2
   394  			encodedInvoice: "lntdcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsfpp3qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpf094cd5v782dw6hu4uu2nadncanzy8emn3xzmp77n0nnzyrwzzxuel7sqyzgmrpvl4p3hncrztujznemavdwy38sa9wdmlrnzcdlscqytjln6",
   395  			valid:          true,
   396  			decodedInvoice: func() *Invoice {
   397  				return &Invoice{
   398  					Net:             chaincfg.TestNet3Params(),
   399  					MilliAt:         &testMilliAt20mDCR,
   400  					Timestamp:       time.Unix(1496314658, 0),
   401  					PaymentHash:     &testPaymentHash,
   402  					DescriptionHash: &testDescriptionHash,
   403  					Destination:     testPubKey,
   404  					FallbackAddr:    testAddrTestnet,
   405  					Features:        emptyFeatures,
   406  				}
   407  			},
   408  			beforeEncoding: func(i *Invoice) {
   409  				// Since this destination pubkey was recovered
   410  				// from the signature, we must set it nil before
   411  				// encoding to get back the same invoice string.
   412  				i.Destination = nil
   413  			},
   414  		},
   415  		{
   416  			// On mainnet, with fallback address DsQxuVRvS4eaJ42dhQEsCXauMWjvopWgrVg with extra routing info to get to node 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
   417  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsfpp3qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvchhyegdla6jsqjquef6f7k9m7gfj3kze2rmqphv24tcsr0v3geqk6w4mgzmup6040rvy9gy0jxlwwvqfv2ua0ycggkammcquq57y4wcqpyayvm",
   418  			valid:          true,
   419  			decodedInvoice: func() *Invoice {
   420  				return &Invoice{
   421  					Net:             chaincfg.MainNetParams(),
   422  					MilliAt:         &testMilliAt20mDCR,
   423  					Timestamp:       time.Unix(1496314658, 0),
   424  					PaymentHash:     &testPaymentHash,
   425  					DescriptionHash: &testDescriptionHash,
   426  					Destination:     testPubKey,
   427  					FallbackAddr:    testRustyAddr,
   428  					RouteHints:      [][]HopHint{testSingleHop},
   429  					Features:        emptyFeatures,
   430  				}
   431  			},
   432  			beforeEncoding: func(i *Invoice) {
   433  				// Since this destination pubkey was recovered
   434  				// from the signature, we must set it nil before
   435  				// encoding to get back the same invoice string.
   436  				i.Destination = nil
   437  			},
   438  		},
   439  		{
   440  			// On mainnet, with fallback address DsQxuVRvS4eaJ42dhQEsCXauMWjvopWgrVg with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
   441  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsfpp3qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqykl3fr9qy3yxam6xh55lxtfcp7uxsdl4krv6206de6j4lvfdu0l4hjwsy9aad8ap527ygzpc0gcrx8t98gxn3kr2xaq2nympn0jv9rqpqjas5d",
   442  			valid:          true,
   443  			decodedInvoice: func() *Invoice {
   444  				return &Invoice{
   445  					Net:             chaincfg.MainNetParams(),
   446  					MilliAt:         &testMilliAt20mDCR,
   447  					Timestamp:       time.Unix(1496314658, 0),
   448  					PaymentHash:     &testPaymentHash,
   449  					DescriptionHash: &testDescriptionHash,
   450  					Destination:     testPubKey,
   451  					FallbackAddr:    testRustyAddr,
   452  					RouteHints:      [][]HopHint{testDoubleHop},
   453  					Features:        emptyFeatures,
   454  				}
   455  			},
   456  			beforeEncoding: func(i *Invoice) {
   457  				// Since this destination pubkey was recovered
   458  				// from the signature, we must set it nil before
   459  				// encoding to get back the same invoice string.
   460  				i.Destination = nil
   461  			},
   462  		},
   463  		{
   464  			// On mainnet, with fallback (p2sh) address DcXTb4QtmnyRsnzUVViYQawqFE5PuYTdX2C
   465  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsfppjqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3wf2zppfn42k4t02y02rrlqner5zkg90qhy9km6m7pkd2euet2dsuqxr4qjgwns45pjhrc4vauau0f05576du50ahs87c7pvt4hm2mcq3gfmam",
   466  			valid:          true,
   467  			decodedInvoice: func() *Invoice {
   468  				return &Invoice{
   469  					Net:             chaincfg.MainNetParams(),
   470  					MilliAt:         &testMilliAt20mDCR,
   471  					Timestamp:       time.Unix(1496314658, 0),
   472  					PaymentHash:     &testPaymentHash,
   473  					DescriptionHash: &testDescriptionHash,
   474  					Destination:     testPubKey,
   475  					FallbackAddr:    testAddrMainnetP2SH,
   476  					Features:        emptyFeatures,
   477  				}
   478  			},
   479  			beforeEncoding: func(i *Invoice) {
   480  				// Since this destination pubkey was recovered
   481  				// from the signature, we must set it nil before
   482  				// encoding to get back the same invoice string.
   483  				i.Destination = nil
   484  			},
   485  		},
   486  		{
   487  			// On mainnet, please send $30 coffee beans supporting
   488  			// features 9, 15 and 99, using secret 0x11...
   489  			encodedInvoice: "lndcr25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsqe7rvhqg67n4e5kemqe9mknrt3hjvqfqkzge5k78qgfrltt202wnkvusx44ulvm7z0s9u80p3xj3s8g25r0qszmc78v8ywwkf76ly2hsp0k62xw",
   490  			valid:          true,
   491  			decodedInvoice: func() *Invoice {
   492  				return &Invoice{
   493  					Net:         chaincfg.MainNetParams(),
   494  					MilliAt:     &testMilliAt25mDCR,
   495  					Timestamp:   time.Unix(1496314658, 0),
   496  					PaymentHash: &testPaymentHash,
   497  					PaymentAddr: &specPaymentAddr,
   498  					Description: &testCoffeeBeans,
   499  					Destination: testPubKey,
   500  					Features: lnwire.NewFeatureVector(
   501  						lnwire.NewRawFeatureVector(9, 15, 99),
   502  						lnwire.Features,
   503  					),
   504  				}
   505  			},
   506  			beforeEncoding: func(i *Invoice) {
   507  				// Since this destination pubkey was recovered
   508  				// from the signature, we must set it nil before
   509  				// encoding to get back the same invoice string.
   510  				i.Destination = nil
   511  			},
   512  		},
   513  		{
   514  			// On mainnet, please send $30 coffee beans supporting
   515  			// features 9, 15, 99, and 100, using secret 0x11...
   516  			encodedInvoice: "lndcr25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsqh82n8qrqmvqkkqjajnv0xgrlfhf02u7j7872t00narrlf33e60cx9dctzd59v7smeuykdtxg6sx2z5a2k0qyakyjhneysc37ghfs6hgqrrjdpu",
   517  			valid:          true,
   518  			decodedInvoice: func() *Invoice {
   519  				return &Invoice{
   520  					Net:         chaincfg.MainNetParams(),
   521  					MilliAt:     &testMilliAt25mDCR,
   522  					Timestamp:   time.Unix(1496314658, 0),
   523  					PaymentHash: &testPaymentHash,
   524  					PaymentAddr: &specPaymentAddr,
   525  					Description: &testCoffeeBeans,
   526  					Destination: testPubKey,
   527  					Features: lnwire.NewFeatureVector(
   528  						lnwire.NewRawFeatureVector(9, 15, 99, 100),
   529  						lnwire.Features,
   530  					),
   531  				}
   532  			},
   533  			beforeEncoding: func(i *Invoice) {
   534  				// Since this destination pubkey was recovered
   535  				// from the signature, we must set it nil before
   536  				// encoding to get back the same invoice string.
   537  				i.Destination = nil
   538  			},
   539  		},
   540  		{
   541  			// Send 2500uDCR for a cup of coffee with a custom CLTV
   542  			// expiry value.
   543  			encodedInvoice: "lndcr2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jscqzysnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66fxyjhqjk2p5tfgzh8ddfkc7s25nc0qefzt86v7ct6rccv6577f753juhjra927ma6cg05wpd6a2tqjgc56ttp5xygrk237apy5e5vgqq660qqy",
   544  			valid:          true,
   545  			decodedInvoice: func() *Invoice {
   546  				i, _ := NewInvoice(
   547  					chaincfg.MainNetParams(),
   548  					testPaymentHash,
   549  					time.Unix(1496314658, 0),
   550  					Amount(testMilliAt2500uDCR),
   551  					Description(testCupOfCoffee),
   552  					Destination(testPubKey),
   553  					CLTVExpiry(144),
   554  				)
   555  
   556  				return i
   557  			},
   558  		},
   559  		{
   560  			// Send 2500uDCR for a cup of coffee with a payment
   561  			// address.
   562  			encodedInvoice: "lndcr2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66sp5qszsvpcgpyqsyps8pqysqqgzqvyqjqqpqgpsgpgqqypqxpq9qcrsaug6v235r6p6pmyv4gkk8rddnjwryap5vfgl843425p9ng8lttvxr2rvk7690qjpy60qvu6d32l4f06uezh6ahywuh3cu0p0wyrmeksptmu700",
   563  			valid:          true,
   564  			decodedInvoice: func() *Invoice {
   565  				i, _ := NewInvoice(
   566  					chaincfg.MainNetParams(),
   567  					testPaymentHash,
   568  					time.Unix(1496314658, 0),
   569  					Amount(testMilliAt2500uDCR),
   570  					Description(testCupOfCoffee),
   571  					Destination(testPubKey),
   572  					PaymentAddr(testPaymentAddr),
   573  				)
   574  
   575  				return i
   576  			},
   577  		},
   578  		{
   579  			// Decode a mainnet invoice while expecting active net to be testnet
   580  			encodedInvoice: "lndcr241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66husxpmqj9fh878hrkccqzvazqk2mhj0fdtjyngvhz5vje86eh39zu8cmp7k0kml38p3d3ujyuuhqe32kfgdt98t5e8r74xmwk53u5mqqm45579",
   581  			valid:          false,
   582  			decodedInvoice: func() *Invoice {
   583  				return &Invoice{
   584  					Net:         chaincfg.TestNet3Params(),
   585  					MilliAt:     &testMilliAt24DCR,
   586  					Timestamp:   time.Unix(1503429093, 0),
   587  					PaymentHash: &testPaymentHash,
   588  					Destination: testPubKey,
   589  					Description: &testEmptyString,
   590  					Features:    emptyFeatures,
   591  				}
   592  			},
   593  			skipEncoding: true, // Skip encoding since we were given the wrong net
   594  		},
   595  	}
   596  
   597  	for i, test := range tests {
   598  		var decodedInvoice *Invoice
   599  		net := chaincfg.MainNetParams()
   600  		if test.decodedInvoice != nil {
   601  			decodedInvoice = test.decodedInvoice()
   602  			net = decodedInvoice.Net
   603  		}
   604  
   605  		invoice, err := Decode(test.encodedInvoice, net)
   606  		if (err == nil) != test.valid {
   607  			t.Errorf("Decoding test %d failed: %v", i, err)
   608  			return
   609  		}
   610  
   611  		if test.valid {
   612  			if err := compareInvoices(decodedInvoice, invoice); err != nil {
   613  				t.Errorf("Invoice decoding result %d not as expected: %v", i, err)
   614  				return
   615  			}
   616  		}
   617  
   618  		if test.skipEncoding {
   619  			continue
   620  		}
   621  
   622  		if test.beforeEncoding != nil {
   623  			test.beforeEncoding(decodedInvoice)
   624  		}
   625  
   626  		if decodedInvoice != nil {
   627  			reencoded, err := decodedInvoice.Encode(
   628  				testMessageSigner,
   629  			)
   630  			if (err == nil) != test.valid {
   631  				t.Errorf("Encoding test %d failed: %v", i, err)
   632  				return
   633  			}
   634  
   635  			if test.valid && test.encodedInvoice != reencoded {
   636  				t.Errorf("Encoding %d failed, expected %v, got %v",
   637  					i, test.encodedInvoice, reencoded)
   638  				return
   639  			}
   640  		}
   641  	}
   642  }
   643  
   644  // TestNewInvoice tests that providing the optional arguments to the NewInvoice
   645  // method creates an Invoice that encodes to the expected string.
   646  func TestNewInvoice(t *testing.T) {
   647  	t.Parallel()
   648  
   649  	tests := []struct {
   650  		newInvoice     func() (*Invoice, error)
   651  		encodedInvoice string
   652  		valid          bool
   653  	}{
   654  		{
   655  			// Both Description and DescriptionHash set.
   656  			newInvoice: func() (*Invoice, error) {
   657  				return NewInvoice(chaincfg.MainNetParams(),
   658  					testPaymentHash, time.Unix(1496314658, 0),
   659  					DescriptionHash(testDescriptionHash),
   660  					Description(testPleaseConsider))
   661  			},
   662  			valid: false, // Both Description and DescriptionHash set.
   663  		},
   664  		{
   665  			// Invoice with no amount.
   666  			newInvoice: func() (*Invoice, error) {
   667  				return NewInvoice(
   668  					chaincfg.MainNetParams(),
   669  					testPaymentHash,
   670  					time.Unix(1496314658, 0),
   671  					Description(testCupOfCoffee),
   672  				)
   673  			},
   674  			valid:          true,
   675  			encodedInvoice: "lndcr1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsmvp0ygkvzd3zh9wkfj59cuze0se5fzuh4f7rysdukv68n6fafa45sudrzg8d33paaw50zczd5mzmppqaalvzneu0yd3zfrvzhnfzpkgppyrza2",
   676  		},
   677  		{
   678  			// 'n' field set.
   679  			newInvoice: func() (*Invoice, error) {
   680  				return NewInvoice(chaincfg.MainNetParams(),
   681  					testPaymentHash, time.Unix(1503429093, 0),
   682  					Amount(testMilliAt24DCR),
   683  					Description(testEmptyString),
   684  					Destination(testPubKey))
   685  			},
   686  			valid:          true,
   687  			encodedInvoice: "lndcr241pveeq09pp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66husxpmqj9fh878hrkccqzvazqk2mhj0fdtjyngvhz5vje86eh39zu8cmp7k0kml38p3d3ujyuuhqe32kfgdt98t5e8r74xmwk53u5mqqm45579",
   688  		},
   689  		{
   690  			// On mainnet, with fallback address DsQxuVRvS4eaJ42dhQEsCXauMWjvopWgrVg with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255
   691  			newInvoice: func() (*Invoice, error) {
   692  				return NewInvoice(chaincfg.MainNetParams(),
   693  					testPaymentHash, time.Unix(1496314658, 0),
   694  					Amount(testMilliAt20mDCR),
   695  					DescriptionHash(testDescriptionHash),
   696  					FallbackAddr(testRustyAddr),
   697  					RouteHint(testDoubleHop),
   698  				)
   699  			},
   700  			valid:          true,
   701  			encodedInvoice: "lndcr20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp5p0y6smqsu95wrj2v9dzntwn88pmz4ck92063nkhxju832w0tr5hsfpp3qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqykl3fr9qy3yxam6xh55lxtfcp7uxsdl4krv6206de6j4lvfdu0l4hjwsy9aad8ap527ygzpc0gcrx8t98gxn3kr2xaq2nympn0jv9rqpqjas5d",
   702  		},
   703  		{
   704  			// On simnet
   705  			newInvoice: func() (*Invoice, error) {
   706  				return NewInvoice(chaincfg.SimNetParams(),
   707  					testPaymentHash, time.Unix(1496314658, 0),
   708  					Amount(testMilliAt24DCR),
   709  					Description(testEmptyString),
   710  					Destination(testPubKey))
   711  			},
   712  			valid:          true,
   713  			encodedInvoice: "lnsdcr241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66zh5xhvtchse36pt88lj4djy8g58lx26xfz3np7humcd9594rmgv92nws6vllf9mhq670x9nrwhjzw0shsklr6gq235whh9x9089ue7gpjur6cc",
   714  		},
   715  		{
   716  			// On regtest
   717  			newInvoice: func() (*Invoice, error) {
   718  				return NewInvoice(chaincfg.RegNetParams(),
   719  					testPaymentHash, time.Unix(1496314658, 0),
   720  					Amount(testMilliAt24DCR),
   721  					Description(testEmptyString),
   722  					Destination(testPubKey))
   723  			},
   724  			valid:          true,
   725  			encodedInvoice: "lnrdcr241pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdqqnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv668eyx3hxz79l45wdm93chts7yvd7n5dd4peq0dwdkrdamnrylws34pynkyyw7dndfy047tcelp4l8w26j8jjht8urq204g3ca6tgm7ycpq5qkd2",
   726  		},
   727  	}
   728  
   729  	for i, test := range tests {
   730  
   731  		invoice, err := test.newInvoice()
   732  		if err != nil && !test.valid {
   733  			continue
   734  		}
   735  		encoded, err := invoice.Encode(testMessageSigner)
   736  		if (err == nil) != test.valid {
   737  			t.Errorf("NewInvoice test %d failed: %v", i, err)
   738  			return
   739  		}
   740  
   741  		if test.valid && test.encodedInvoice != encoded {
   742  			t.Errorf("Encoding %d failed, expected %v, got %v",
   743  				i, test.encodedInvoice, encoded)
   744  			return
   745  		}
   746  	}
   747  }
   748  
   749  // TestMaxInvoiceLength tests that attempting to decode an invoice greater than
   750  // maxInvoiceLength fails with ErrInvoiceTooLarge.
   751  func TestMaxInvoiceLength(t *testing.T) {
   752  	t.Parallel()
   753  
   754  	tests := []struct {
   755  		encodedInvoice string
   756  		expectedError  error
   757  	}{
   758  		{
   759  			// Valid since it is less than maxInvoiceLength.
   760  			encodedInvoice: "lndcr25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeesrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66zlnttha3xvcs5vdzqchtd462jgkqzxv59jvj5d06ne2wpajp6etptmk5krggr93xywxm6nfapsnln7n5jcgy9s56g2sd2gthvcvd3hqpv6fnqy",
   761  		},
   762  		{
   763  			// Invalid since it is greater than maxInvoiceLength.
   764  			encodedInvoice: "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvrzjq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvnp4q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66fxq08u2ye04k6d2v2hgd8naeu0mfszlz2h6ze5mnufsc7y07s2z5v7vswj7jjcqumqchd646hnrx9pznxfj98565uc84zpc82x3nr8sqqn2zzu",
   765  			expectedError:  ErrInvoiceTooLarge,
   766  		},
   767  	}
   768  
   769  	net := chaincfg.MainNetParams()
   770  
   771  	for i, test := range tests {
   772  		_, err := Decode(test.encodedInvoice, net)
   773  		if err != test.expectedError {
   774  			t.Errorf("Expected test %d to have error: %v, instead have: %v",
   775  				i, test.expectedError, err)
   776  			return
   777  		}
   778  	}
   779  }
   780  
   781  // TestInvoiceChecksumMalleability ensures that the malleability of the
   782  // checksum in bech32 strings cannot cause a signature to become valid and
   783  // therefore cause a wrong destination to be decoded for invoices where the
   784  // destination is extracted from the signature.
   785  func TestInvoiceChecksumMalleability(t *testing.T) {
   786  	privKeyHex := "7f9f2872307ba178b75434250da5fcac12e9d47fe47d90c1f0cb0641a450cff8"
   787  	privKeyBytes, _ := hex.DecodeString(privKeyHex)
   788  	chain := chaincfg.RegNetParams()
   789  	var payHash [32]byte
   790  	ts := time.Unix(0, 0)
   791  	privKey := secp256k1.PrivKeyFromBytes(privKeyBytes)
   792  	pubKey := privKey.PubKey()
   793  	msgSigner := MessageSigner{
   794  		SignCompact: func(msg []byte) ([]byte, error) {
   795  			hash := chainhash.HashB(msg)
   796  			return ecdsa.SignCompact(privKey, hash, true), nil
   797  		},
   798  	}
   799  	opts := []func(*Invoice){Description("test")}
   800  	invoice, err := NewInvoice(chain, payHash, ts, opts...)
   801  	if err != nil {
   802  		t.Fatal(err)
   803  	}
   804  
   805  	encoded, err := invoice.Encode(msgSigner)
   806  	if err != nil {
   807  		t.Fatal(err)
   808  	}
   809  
   810  	t.Logf("encoded %s", encoded)
   811  	t.Logf("pubkey %x", pubKey.SerializeCompressed())
   812  
   813  	// Changing a bech32 string which checksum ends in "p" to "(q*)p" can
   814  	// cause the checksum to return as a valid bech32 string _but_ the
   815  	// signature field immediately preceding it would be mutaded.  In rare
   816  	// cases (about 3%) it is still seen as a valid signature and public
   817  	// key recovery causes a different node than the originally intended
   818  	// one to be derived.
   819  	//
   820  	// We thus modify the checksum here and verify the invoice gets broken
   821  	// enough that it fails to decode.
   822  	if !strings.HasSuffix(encoded, "p") {
   823  		t.Logf("Invoice: %s", encoded)
   824  		t.Fatalf("Generated invoice checksum does not end in 'p'")
   825  	}
   826  	encoded = encoded[:len(encoded)-1] + "qp"
   827  
   828  	_, err = Decode(encoded, chain)
   829  	if err == nil {
   830  		t.Fatalf("Did not get expected error when decoding invoice")
   831  	}
   832  
   833  }
   834  
   835  func compareInvoices(expected, actual *Invoice) error {
   836  	if !reflect.DeepEqual(expected.Net, actual.Net) {
   837  		return fmt.Errorf("expected net %v, got %v",
   838  			expected.Net, actual.Net)
   839  	}
   840  
   841  	if !reflect.DeepEqual(expected.MilliAt, actual.MilliAt) {
   842  		return fmt.Errorf("expected milli atoms %d, got %d",
   843  			*expected.MilliAt, *actual.MilliAt)
   844  	}
   845  
   846  	if expected.Timestamp != actual.Timestamp {
   847  		return fmt.Errorf("expected timestamp %v, got %v",
   848  			expected.Timestamp, actual.Timestamp)
   849  	}
   850  
   851  	if !compareHashes(expected.PaymentHash, actual.PaymentHash) {
   852  		return fmt.Errorf("expected payment hash %x, got %x",
   853  			*expected.PaymentHash, *actual.PaymentHash)
   854  	}
   855  
   856  	if !reflect.DeepEqual(expected.Description, actual.Description) {
   857  		return fmt.Errorf("expected description \"%s\", got \"%s\"",
   858  			*expected.Description, *actual.Description)
   859  	}
   860  
   861  	if !comparePubkeys(expected.Destination, actual.Destination) {
   862  		return fmt.Errorf("expected destination pubkey %x, got %x",
   863  			expected.Destination.SerializeCompressed(), actual.Destination.SerializeCompressed())
   864  	}
   865  
   866  	if !compareHashes(expected.DescriptionHash, actual.DescriptionHash) {
   867  		return fmt.Errorf("expected description hash %x, got %x",
   868  			*expected.DescriptionHash, *actual.DescriptionHash)
   869  	}
   870  
   871  	if expected.Expiry() != actual.Expiry() {
   872  		return fmt.Errorf("expected expiry %d, got %d",
   873  			expected.Expiry(), actual.Expiry())
   874  	}
   875  
   876  	if !reflect.DeepEqual(expected.FallbackAddr, actual.FallbackAddr) {
   877  		return fmt.Errorf("expected FallbackAddr %v, got %v",
   878  			expected.FallbackAddr, actual.FallbackAddr)
   879  	}
   880  
   881  	if len(expected.RouteHints) != len(actual.RouteHints) {
   882  		return fmt.Errorf("expected %d RouteHints, got %d",
   883  			len(expected.RouteHints), len(actual.RouteHints))
   884  	}
   885  
   886  	for i, routeHint := range expected.RouteHints {
   887  		err := compareRouteHints(routeHint, actual.RouteHints[i])
   888  		if err != nil {
   889  			return err
   890  		}
   891  	}
   892  
   893  	if !reflect.DeepEqual(expected.Features, actual.Features) {
   894  		return fmt.Errorf("expected features %v, got %v",
   895  			expected.Features, actual.Features)
   896  	}
   897  
   898  	return nil
   899  }
   900  
   901  func comparePubkeys(a, b *secp256k1.PublicKey) bool {
   902  	if a == b {
   903  		return true
   904  	}
   905  	if a == nil && b != nil {
   906  		return false
   907  	}
   908  	if b == nil && a != nil {
   909  		return false
   910  	}
   911  	return a.IsEqual(b)
   912  }
   913  
   914  func compareHashes(a, b *[32]byte) bool {
   915  	if a == b {
   916  		return true
   917  	}
   918  	if a == nil && b != nil {
   919  		return false
   920  	}
   921  	if b == nil && a != nil {
   922  		return false
   923  	}
   924  	return bytes.Equal(a[:], b[:])
   925  }
   926  
   927  func compareRouteHints(a, b []HopHint) error {
   928  	if len(a) != len(b) {
   929  		return fmt.Errorf("expected len routingInfo %d, got %d",
   930  			len(a), len(b))
   931  	}
   932  
   933  	for i := 0; i < len(a); i++ {
   934  		if !comparePubkeys(a[i].NodeID, b[i].NodeID) {
   935  			return fmt.Errorf("expected routeHint nodeID %x, "+
   936  				"got %x", a[i].NodeID.SerializeCompressed(), b[i].NodeID.SerializeCompressed())
   937  		}
   938  
   939  		if a[i].ChannelID != b[i].ChannelID {
   940  			return fmt.Errorf("expected routeHint channelID "+
   941  				"%d, got %d", a[i].ChannelID, b[i].ChannelID)
   942  		}
   943  
   944  		if a[i].FeeBaseMAtoms != b[i].FeeBaseMAtoms {
   945  			return fmt.Errorf("expected routeHint feeBaseMAtoms %d, got %d",
   946  				a[i].FeeBaseMAtoms, b[i].FeeBaseMAtoms)
   947  		}
   948  
   949  		if a[i].FeeProportionalMillionths != b[i].FeeProportionalMillionths {
   950  			return fmt.Errorf("expected routeHint feeProportionalMillionths %d, got %d",
   951  				a[i].FeeProportionalMillionths, b[i].FeeProportionalMillionths)
   952  		}
   953  
   954  		if a[i].CLTVExpiryDelta != b[i].CLTVExpiryDelta {
   955  			return fmt.Errorf("expected routeHint cltvExpiryDelta "+
   956  				"%d, got %d", a[i].CLTVExpiryDelta, b[i].CLTVExpiryDelta)
   957  		}
   958  	}
   959  
   960  	return nil
   961  }