From a906c498fbe9234d5088cbec6e5dba7f1e6317af Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 24 Aug 2021 23:22:55 +0000 Subject: [PATCH] Use new BOLT 11 test vectors with payment_secrets and feature flags This pulls the BOLT 11 test vectors from https://github.com/lightningnetwork/lightning-rfc/pull/898, tweaking our tests to properly handle them. --- lightning-invoice/Cargo.toml | 1 + lightning-invoice/tests/ser_de.rs | 364 ++++++++++++++++++++++++------ 2 files changed, 290 insertions(+), 75 deletions(-) diff --git a/lightning-invoice/Cargo.toml b/lightning-invoice/Cargo.toml index 8c6623b8..baa9a79c 100644 --- a/lightning-invoice/Cargo.toml +++ b/lightning-invoice/Cargo.toml @@ -16,4 +16,5 @@ num-traits = "0.2.8" bitcoin_hashes = "0.10" [dev-dependencies] +hex = "0.3" lightning = { version = "0.0.100", path = "../lightning", features = ["_test_utils"] } diff --git a/lightning-invoice/tests/ser_de.rs b/lightning-invoice/tests/ser_de.rs index 94e75cbc..a2cc0e2e 100644 --- a/lightning-invoice/tests/ser_de.rs +++ b/lightning-invoice/tests/ser_de.rs @@ -1,28 +1,30 @@ +extern crate bech32; extern crate bitcoin_hashes; extern crate lightning; extern crate lightning_invoice; extern crate secp256k1; +extern crate hex; use bitcoin_hashes::hex::FromHex; -use bitcoin_hashes::sha256; +use bitcoin_hashes::{sha256, Hash}; +use bech32::u5; use lightning::ln::PaymentSecret; +use lightning::routing::router::{RouteHint, RouteHintHop}; +use lightning::routing::network_graph::RoutingFees; use lightning_invoice::*; -use secp256k1::Secp256k1; -use secp256k1::key::SecretKey; +use secp256k1::PublicKey; use secp256k1::recovery::{RecoverableSignature, RecoveryId}; +use std::collections::HashSet; use std::time::{Duration, UNIX_EPOCH}; use std::str::FromStr; -// TODO: add more of the examples from BOLT11 and generate ones causing SemanticErrors - -fn get_test_tuples() -> Vec<(String, SignedRawInvoice, Option)> { +fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> { vec![ ( - "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\ - wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\ - ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".to_owned(), + "lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql".to_owned(), InvoiceBuilder::new(Currency::Bitcoin) .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .payment_secret(PaymentSecret([0x11; 32])) .payment_hash(sha256::Hash::from_hex( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap()) @@ -31,26 +33,19 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, Option)> { .unwrap() .sign(|_| { RecoverableSignature::from_compact( - & [ - 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a, - 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43, - 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f, - 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad, - 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9, - 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f - ], - RecoveryId::from_i32(0).unwrap() + &hex::decode("8d3ce9e28357337f62da0162d9454df827f83cfe499aeb1c1db349d4d81127425e434ca29929406c23bba1ae8ac6ca32880b38d4bf6ff874024cac34ba9625f1").unwrap(), + RecoveryId::from_i32(1).unwrap() ) }).unwrap(), - None + false, // Same features as set in InvoiceBuilder + false, // No unknown fields ), ( - "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3\ - k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch\ - 9zw97j25emudupq63nyw24cg27h2rspfj9srp".to_owned(), + "lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgquk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgpfna3rh".to_owned(), InvoiceBuilder::new(Currency::Bitcoin) .amount_milli_satoshis(250_000_000) .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .payment_secret(PaymentSecret([0x11; 32])) .payment_hash(sha256::Hash::from_hex( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap()) @@ -60,94 +55,313 @@ fn get_test_tuples() -> Vec<(String, SignedRawInvoice, Option)> { .unwrap() .sign(|_| { RecoverableSignature::from_compact( - & [ - 0xe8, 0x96, 0x39, 0xba, 0x68, 0x14, 0xe3, 0x66, 0x89, 0xd4, 0xb9, 0x1b, - 0xf1, 0x25, 0xf1, 0x03, 0x51, 0xb5, 0x5d, 0xa0, 0x57, 0xb0, 0x06, 0x47, - 0xa8, 0xda, 0xba, 0xeb, 0x8a, 0x90, 0xc9, 0x5f, 0x16, 0x0f, 0x9d, 0x5a, - 0x6e, 0x0f, 0x79, 0xd1, 0xfc, 0x2b, 0x96, 0x42, 0x38, 0xb9, 0x44, 0xe2, - 0xfa, 0x4a, 0xa6, 0x77, 0xc6, 0xf0, 0x20, 0xd4, 0x66, 0x47, 0x2a, 0xb8, - 0x42, 0xbd, 0x75, 0x0e - ], + &hex::decode("e59e3ffbd3945e4334879158d31e89b076dff54f3fa7979ae79df2db9dcaf5896cbfe1a478b8d2307e92c88139464cb7e6ef26e414c4abe33337961ddc5e8ab1").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpu9qrsgqhtjpauu9ur7fw2thcl4y9vfvh4m9wlfyz2gem29g5ghe2aak2pm3ps8fdhtceqsaagty2vph7utlgj48u0ged6a337aewvraedendscp573dxr".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(250_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .description("ナンセンス 1杯".to_owned()) + .expiry_time(Duration::from_secs(60)) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("bae41ef385e0fc972977c7ea42b12cbd76577d2412919da8a8a22f9577b6507710c0e96dd78c821dea16453037f717f44aa7e3d196ebb18fbb97307dcb7336c3").unwrap(), RecoveryId::from_i32(1).unwrap() ) }).unwrap(), - None + false, // Same features as set in InvoiceBuilder + false, // No unknown fields ), ( - "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qq\ - dhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7k\ - hhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7".to_owned(), + "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp7ynn44".to_owned(), InvoiceBuilder::new(Currency::Bitcoin) .amount_milli_satoshis(2_000_000_000) .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .description_hash(sha256::Hash::hash(b"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")) + .payment_secret(PaymentSecret([0x11; 32])) .payment_hash(sha256::Hash::from_hex( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap()) - .description_hash(sha256::Hash::from_hex( - "3925b6f67e2c340036ed12093dd44e0368df1b6ea26c53dbe4811f58fd5db8c1" + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("f67a5f696648fa4fb102e1a07b230e54722f8e024cee71e80b4847ac191da3fb2d2cdb28cc32344d7e9a9cf5c9b6a0ee0582ae46e9938b9c81e344a4dbb5289d").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lntb20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un989qrsgqdj545axuxtnfemtpwkc45hx9d2ft7x04mt8q7y6t0k2dge9e7h8kpy9p34ytyslj3yu569aalz2xdk8xkd7ltxqld94u8h2esmsmacgpghe9k8".to_owned(), + InvoiceBuilder::new(Currency::BitcoinTestnet) + .amount_milli_satoshis(2_000_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .description_hash(sha256::Hash::hash(b"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")) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap()) + .fallback(Fallback::PubKeyHash([49, 114, 181, 101, 79, 102, 131, 200, 251, 20, 105, 89, 211, 71, 206, 48, 60, 174, 76, 167])) .build_raw() .unwrap() .sign(|_| { RecoverableSignature::from_compact( - & [ - 0xc6, 0x34, 0x86, 0xe8, 0x1f, 0x8c, 0x87, 0x8a, 0x10, 0x5b, 0xc9, 0xd9, - 0x59, 0xaf, 0x19, 0x73, 0x85, 0x4c, 0x4d, 0xc5, 0x52, 0xc4, 0xf0, 0xe0, - 0xe0, 0xc7, 0x38, 0x96, 0x03, 0xd6, 0xbd, 0xc6, 0x77, 0x07, 0xbf, 0x6b, - 0xe9, 0x92, 0xa8, 0xce, 0x7b, 0xf5, 0x00, 0x16, 0xbb, 0x41, 0xd8, 0xa9, - 0xb5, 0x35, 0x86, 0x52, 0xc4, 0x96, 0x04, 0x45, 0xa1, 0x70, 0xd0, 0x49, - 0xce, 0xd4, 0x55, 0x8c - ], + &hex::decode("6ca95a74dc32e69ced6175b15a5cc56a92bf19f5dace0f134b7d94d464b9f5cf6090a18d48b243f289394d17bdf89466d8e6b37df5981f696bc3dd5986e1bee1").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq9qrsgqdfjcdk6w3ak5pca9hwfwfh63zrrz06wwfya0ydlzpgzxkn5xagsqz7x9j4jwe7yj7vaf2k9lqsdk45kts2fd0fkr28am0u4w95tt2nsq76cqw0".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(2_000_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .description_hash(sha256::Hash::hash(b"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")) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .fallback(Fallback::PubKeyHash([4, 182, 31, 125, 193, 234, 13, 201, 148, 36, 70, 76, 196, 6, 77, 197, 100, 217, 30, 137])) + .private_route(RouteHint(vec![RouteHintHop { + src_node_id: PublicKey::from_slice(&hex::decode( + "029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255" + ).unwrap()).unwrap(), + short_channel_id: (66051 << 40) | (263430 << 16) | 1800, + fees: RoutingFees { base_msat: 1, proportional_millionths: 20 }, + cltv_expiry_delta: 3, + htlc_maximum_msat: None, htlc_minimum_msat: None, + }, RouteHintHop { + src_node_id: PublicKey::from_slice(&hex::decode( + "039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255" + ).unwrap()).unwrap(), + short_channel_id: (197637 << 40) | (395016 << 16) | 2314, + fees: RoutingFees { base_msat: 2, proportional_millionths: 30 }, + cltv_expiry_delta: 4, + htlc_maximum_msat: None, htlc_minimum_msat: None, + }])) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("6a6586db4e8f6d40e3a5bb92e4df5110c627e9ce493af237e20a046b4e86ea200178c59564ecf892f33a9558bf041b6ad2cb8292d7a6c351fbb7f2ae2d16b54e").unwrap(), RecoveryId::from_i32(0).unwrap() ) }).unwrap(), - None + false, // Same features as set in InvoiceBuilder + false, // No unknown fields ), ( - "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp59g4z52329g4z52329g4z52329g4z52329g4z52329g4z52329g4q9qrsgqzfhag3vsafx4e5qssalvw4rn0phsvpp3e5h2xxyk9l8fxsutvndx9t840dqvdrlu2gqmk0q8apqrgnjy9amc07hmjl9e9yzqjks5w2gqgjnyms".to_owned(), + "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z99qrsgqz6qsgww34xlatfj6e3sngrwfy3ytkt29d2qttr8qz2mnedfqysuqypgqex4haa2h8fx3wnypranf3pdwyluftwe680jjcfp438u82xqphf75ym".to_owned(), InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(2_000_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .description_hash(sha256::Hash::hash(b"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")) + .payment_secret(PaymentSecret([0x11; 32])) .payment_hash(sha256::Hash::from_hex( "0001020304050607080900010203040506070809000102030405060708090102" ).unwrap()) - .description("coffee beans".to_string()) + .fallback(Fallback::ScriptHash([143, 85, 86, 59, 154, 25, 243, 33, 194, 17, 233, 185, 243, 140, 223, 104, 110, 160, 120, 69])) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("16810439d1a9bfd5a65acc61340dc92448bb2d456a80b58ce012b73cb5202438020500c9ab7ef5573a4d174c811f669885ae27f895bb3a3be52c243589f87518").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7k9qrsgqt29a0wturnys2hhxpner2e3plp6jyj8qx7548zr2z7ptgjjc7hljm98xhjym0dg52sdrvqamxdezkmqg4gdrvwwnf0kv2jdfnl4xatsqmrnsse".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) .amount_milli_satoshis(2_000_000_000) .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) - .payment_secret(PaymentSecret([42; 32])) + .description_hash(sha256::Hash::hash(b"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")) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(), + program: vec![117, 30, 118, 232, 25, 145, 150, 212, 84, 148, 28, 69, 209, 179, 163, 35, 241, 67, 59, 214] + }) .build_raw() .unwrap() - .sign::<_, ()>(|msg_hash| { - let privkey = SecretKey::from_slice(&[41; 32]).unwrap(); - let secp_ctx = Secp256k1::new(); - Ok(secp_ctx.sign_recoverable(msg_hash, &privkey)) + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("5a8bd7b97c1cc9055ee60cf2356621f8752248e037a953886a1782b44a58f5ff2d94e6bc89b7b514541a3603bb33722b6c08aa1a3639d34becc549a99fea6eae").unwrap(), + RecoveryId::from_i32(0).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q9qrsgq9vlvyj8cqvq6ggvpwd53jncp9nwc47xlrsnenq2zp70fq83qlgesn4u3uyf4tesfkkwwfg3qs54qe426hp3tz7z6sweqdjg05axsrjqp9yrrwc".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(2_000_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .description_hash(sha256::Hash::hash(b"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")) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(), + program: vec![24, 99, 20, 60, 20, 197, 22, 104, 4, 189, 25, 32, 51, 86, 218, 19, 108, 152, 86, 120, 205, 77, 39, 161, 184, 198, 50, 150, 4, 144, 50, 98] }) - .unwrap(), - None - ) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("2b3ec248f80301a421817369194f012cdd8af8df1c279981420f9e901e20fa3309d791e11355e609b59ce4a220852a0cd55ab862b1785a83b206c90fa74d01c8").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(967878534) + .timestamp(UNIX_EPOCH + Duration::from_secs(1572468703)) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "462264ede7e14047e9b249da94fefc47f41f7d02ee9b091815a5506bc8abf75f" + ).unwrap()) + .expiry_time(Duration::from_secs(604800)) + .min_final_cltv_expiry(10) + .description("Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \"Back In My Day\" Sticker x 2, \"I Got Lightning Working\" Sticker x 2 and 1 more items".to_owned()) + .private_route(RouteHint(vec![RouteHintHop { + src_node_id: PublicKey::from_slice(&hex::decode( + "03d06758583bb5154774a6eb221b1276c9e82d65bbaceca806d90e20c108f4b1c7" + ).unwrap()).unwrap(), + short_channel_id: (589390 << 40) | (3312 << 16) | 1, + fees: RoutingFees { base_msat: 1000, proportional_millionths: 2500 }, + cltv_expiry_delta: 40, + htlc_maximum_msat: None, htlc_minimum_msat: None, + }])) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("1b1160cf6186b55722c1ac7ea502086baaccaabdc76b326e666b7f309d972b15069bfca11cd365304b36f48230cc12f3f13a017aab65f7c165a169df32282a58").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + false, // Same features as set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2a25dxl5hrntdtn6zvydt7d66hyzsyhqs4wdynavys42xgl6sgx9c4g7me86a27t07mdtfry458rtjr0v92cnmswpsjscgt2vcse3sgpz3uapa".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(2_500_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .description("coffee beans".to_owned()) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("5755469bf4b8e6b6ae7a1308d5f9bad5c82812e0855cd24fac242aa323fa820c5c551ede4faeabcb7fb6d5a464ad0e35c86f615589ee0e0c250c216a662198c1").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + true, // Different features than set in InvoiceBuilder + false, // No unknown fields + ), + ( + "LNBC25M1PVJLUEZPP5QQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQYPQDQ5VDHKVEN9V5SXYETPDEESSP5ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYGS9Q5SQQQQQQQQQQQQQQQQSGQ2A25DXL5HRNTDTN6ZVYDT7D66HYZSYHQS4WDYNAVYS42XGL6SGX9C4G7ME86A27T07MDTFRY458RTJR0V92CNMSWPSJSCGT2VCSE3SGPZ3UAPA".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(2_500_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .description("coffee beans".to_owned()) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("5755469bf4b8e6b6ae7a1308d5f9bad5c82812e0855cd24fac242aa323fa820c5c551ede4faeabcb7fb6d5a464ad0e35c86f615589ee0e0c250c216a662198c1").unwrap(), + RecoveryId::from_i32(1).unwrap() + ) + }).unwrap(), + true, // Different features than set in InvoiceBuilder + false, // No unknown fields + ), + ( + "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz599y53s3ujmcfjp5xrdap68qxymkqphwsexhmhr8wdz5usdzkzrse33chw6dlp3jhuhge9ley7j2ayx36kawe7kmgg8sv5ugdyusdcqzn8z9x".to_owned(), + InvoiceBuilder::new(Currency::Bitcoin) + .amount_milli_satoshis(2_500_000_000) + .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658)) + .payment_secret(PaymentSecret([0x11; 32])) + .payment_hash(sha256::Hash::from_hex( + "0001020304050607080900010203040506070809000102030405060708090102" + ).unwrap()) + .description("coffee beans".to_owned()) + .build_raw() + .unwrap() + .sign(|_| { + RecoverableSignature::from_compact( + &hex::decode("150a5252308f25bc2641a186de87470189bb003774326beee33b9a2a720d1584386631c5dda6fc3195f97464bfc93d2574868eadd767d6da1078329c4349c837").unwrap(), + RecoveryId::from_i32(0).unwrap() + ) + }).unwrap(), + true, // Different features than set in InvoiceBuilder + true, // Some unknown fields + ), ] } - #[test] -fn serialize() { - for (serialized, deserialized, _) in get_test_tuples() { - assert_eq!(deserialized.to_string(), serialized); - } -} - -#[test] -fn deserialize() { - for (serialized, deserialized, maybe_error) in get_test_tuples() { +fn invoice_deserialize() { + for (serialized, deserialized, ignore_feature_diff, ignore_unknown_fields) in get_test_tuples() { + eprintln!("Testing invoice {}...", serialized); let parsed = serialized.parse::().unwrap(); - assert_eq!(parsed, deserialized); + let (parsed_invoice, _, parsed_sig) = parsed.into_parts(); + let (deserialized_invoice, _, deserialized_sig) = deserialized.into_parts(); - let validated = Invoice::from_signed(parsed); + assert_eq!(deserialized_sig, parsed_sig); + assert_eq!(deserialized_invoice.hrp, parsed_invoice.hrp); + assert_eq!(deserialized_invoice.data.timestamp, parsed_invoice.data.timestamp); - if let Some(error) = maybe_error { - assert_eq!(Err(error), validated); - } else { - assert!(validated.is_ok()); + let mut deserialized_hunks: HashSet<_> = deserialized_invoice.data.tagged_fields.iter().collect(); + let mut parsed_hunks: HashSet<_> = parsed_invoice.data.tagged_fields.iter().collect(); + if ignore_feature_diff { + deserialized_hunks.retain(|h| + if let RawTaggedField::KnownSemantics(TaggedField::Features(_)) = h { false } else { true }); + parsed_hunks.retain(|h| + if let RawTaggedField::KnownSemantics(TaggedField::Features(_)) = h { false } else { true }); } + if ignore_unknown_fields { + parsed_hunks.retain(|h| + if let RawTaggedField::UnknownSemantics(_) = h { false } else { true }); + } + assert_eq!(deserialized_hunks, parsed_hunks); + + Invoice::from_signed(serialized.parse::().unwrap()).unwrap(); } } @@ -155,7 +369,7 @@ fn deserialize() { fn test_bolt_invalid_invoices() { // Tests the BOLT 11 invalid invoice test vectors assert_eq!(Invoice::from_str( - "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsqq40wa3khl49yue3zsgm26jrepqr2eghqlx86rttutve3ugd05em86nsefzh4pfurpd9ek9w2vp95zxqnfe2u7ckudyahsa52q66tgzcp6t2dyk" + "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqtqyx5vggfcsll4wu246hz02kp85x4katwsk9639we5n5yngc3yhqkm35jnjw4len8vrnqnf5ejh0mzj9n3vz2px97evektfm2l6wqccp3y7372" ), Err(ParseOrSemanticError::SemanticError(SemanticError::InvalidFeatures))); assert_eq!(Invoice::from_str( "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt" @@ -167,15 +381,15 @@ fn test_bolt_invalid_invoices() { "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny" ), Err(ParseOrSemanticError::ParseError(ParseError::Bech32Error(bech32::Error::MixedCase)))); assert_eq!(Invoice::from_str( - "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq" + "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2" ), Err(ParseOrSemanticError::SemanticError(SemanticError::InvalidSignature))); assert_eq!(Invoice::from_str( "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh" ), Err(ParseOrSemanticError::ParseError(ParseError::TooShortDataPart))); assert_eq!(Invoice::from_str( - "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg" + "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqrrzc4cvfue4zp3hggxp47ag7xnrlr8vgcmkjxk3j5jqethnumgkpqp23z9jclu3v0a7e0aruz366e9wqdykw6dxhdzcjjhldxq0w6wgqcnu43j" ), Err(ParseOrSemanticError::ParseError(ParseError::UnknownSiPrefix))); assert_eq!(Invoice::from_str( - "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s" + "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x" ), Err(ParseOrSemanticError::SemanticError(SemanticError::ImpreciseAmount))); } -- 2.30.2