Skip to content

Commit ffed552

Browse files
committed
offers: parse invoice and invoice request
Add the ability to parse and display BOLT12 invoices and invoice requests. This makes it easy for users of LDK to serialize and deserialize BOLT12 invoices and invoice requests.
1 parent 101aa6f commit ffed552

File tree

3 files changed

+86
-20
lines changed

3 files changed

+86
-20
lines changed

lightning/src/offers/invoice.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ use crate::offers::offer::{
138138
Amount, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OfferTlvStream,
139139
OfferTlvStreamRef, Quantity, EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES,
140140
};
141-
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
141+
use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
142142
use crate::offers::payer::{PayerTlvStream, PayerTlvStreamRef, PAYER_METADATA_TYPE};
143143
use crate::offers::refund::{
144144
Refund, RefundContents, IV_BYTES_WITHOUT_METADATA as REFUND_IV_BYTES_WITHOUT_METADATA,
@@ -158,6 +158,7 @@ use bitcoin::secp256k1::schnorr::Signature;
158158
use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1};
159159
use bitcoin::{Network, WitnessProgram, WitnessVersion};
160160
use core::hash::{Hash, Hasher};
161+
use core::str::FromStr;
161162
use core::time::Duration;
162163

163164
#[allow(unused_imports)]
@@ -1416,6 +1417,30 @@ impl Writeable for InvoiceContents {
14161417
}
14171418
}
14181419

1420+
impl AsRef<[u8]> for Bolt12Invoice {
1421+
fn as_ref(&self) -> &[u8] {
1422+
&self.bytes
1423+
}
1424+
}
1425+
1426+
impl Bech32Encode for Bolt12Invoice {
1427+
const BECH32_HRP: &'static str = "lni";
1428+
}
1429+
1430+
impl FromStr for Bolt12Invoice {
1431+
type Err = Bolt12ParseError;
1432+
1433+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
1434+
Self::from_bech32_str(s)
1435+
}
1436+
}
1437+
1438+
impl core::fmt::Display for Bolt12Invoice {
1439+
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
1440+
self.fmt_bech32_str(f)
1441+
}
1442+
}
1443+
14191444
impl TryFrom<Vec<u8>> for UnsignedBolt12Invoice {
14201445
type Error = Bolt12ParseError;
14211446

@@ -2572,6 +2597,22 @@ mod tests {
25722597
}
25732598
}
25742599

2600+
#[test]
2601+
fn parses_bech32_encoded_invoices() {
2602+
let invoices = [
2603+
"lni1qqsg7jpsyzz4hcsj0hu6rvjevwhmkceurq7sd5ez8ne3js4qt8acvxcgqgp7szsqzcss9w6ckhlv55zuwnkuqqxc9qhu24h9rggzflyw04l9d3hcslzu340jtqss9l7txvy6ukzg8zkxdnvzmg2at4stt004vdqrm0zedsez596nf5w55r7sr3qzhfe2d696205tjuddpjvz8952aaxh3n527f26ks7llqcq8jgzlwxsxhzwphk8y90zdqee8pesuhjst2nz2px6ska9wyr2g666ysz0e8vwqgptkk94lm99qhr5ahqqpkpg9lz4deg6zqj0erna0etvd7y8chydtusq9vqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz3afsfc3h8etwulthfjufa8c6lm8saelrud6h7xyeprcxnk4rd3sqqtqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq46w2nw3wjnazuhrtgvnq3edzh0f4uvazhj2k458hlcxqpujqhm35p4cnsda3eptcngxwfcwv89u5z65cjsfk59hft3q6jxkk3yqn7fmrszqw2gk576jl7lvaxqsae3tt9uepmp4gae5kptgwvc97a04jvljuss7qpdqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0vc5l00vl5rwqgc7cmxyrgtuz8dvv6yma5qs2609uvyfe7wvq2gxqpwqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz3rsqqqqqqsqqqraqqz5qqqqqqqqqqqvsqqqq8g6jj3qqqqqqqqqqqpqqqq86qq9gqqqqqqqqqqqeqqqqqw3499zqqqqq9yq35rr8sh4qsz52329g4z52329g4z52329g4z52329g4z52329g4z52329g4z5242qgp73tzaqqqzpcasc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4qqqpf67qrgen88wz7kzlkpyp480l5rgzecaz2qgqyza43d07efg9ca8dcqqds2p0c4tw2xssyn7gult72mr03p79er2l9vppq2a43d07efg9ca8dcqqds2p0c4tw2xssyn7gult72mr03p79er2l9uzq9wktr4p2qxgmdnpw8qvs05qr0zvam2h52lxt4zz7lah7yp6vmsczevlvqdgjxtwdlp84304uqcygvqcgzpj8p44smqjpzeua0xryrrc"
2604+
];
2605+
for encoded in invoices {
2606+
let decoded = match encoded.parse::<Bolt12Invoice>() {
2607+
Ok(decoded) => decoded,
2608+
Err(e) => panic!("Invalid invoice ({:?}): {}", e, encoded),
2609+
};
2610+
2611+
let reencoded = decoded.to_string();
2612+
assert_eq!(reencoded, encoded, "Re-encoded invoice does not match original");
2613+
}
2614+
}
2615+
25752616
#[test]
25762617
fn parses_invoice_with_payment_paths() {
25772618
let expanded_key = ExpandedKey::new([42; 32]);

lightning/src/offers/invoice_request.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
//! # }
6666
//! ```
6767
68+
use core::str::FromStr;
69+
6870
use crate::blinded_path::message::BlindedMessagePath;
6971
use crate::blinded_path::payment::BlindedPaymentPath;
7072
use crate::io;
@@ -79,7 +81,7 @@ use crate::offers::offer::{
7981
Amount, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, Offer, OfferContents,
8082
OfferId, OfferTlvStream, OfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES,
8183
};
82-
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
84+
use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
8385
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
8486
use crate::offers::signer::{Metadata, MetadataMaterial};
8587
use crate::onion_message::dns_resolution::HumanReadableName;
@@ -1284,6 +1286,30 @@ impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
12841286
}
12851287
}
12861288

1289+
impl AsRef<[u8]> for InvoiceRequest {
1290+
fn as_ref(&self) -> &[u8] {
1291+
&self.bytes
1292+
}
1293+
}
1294+
1295+
impl Bech32Encode for InvoiceRequest {
1296+
const BECH32_HRP: &'static str = "lnr";
1297+
}
1298+
1299+
impl FromStr for InvoiceRequest {
1300+
type Err = Bolt12ParseError;
1301+
1302+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
1303+
Self::from_bech32_str(s)
1304+
}
1305+
}
1306+
1307+
impl core::fmt::Display for InvoiceRequest {
1308+
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
1309+
self.fmt_bech32_str(f)
1310+
}
1311+
}
1312+
12871313
impl TryFrom<Vec<u8>> for InvoiceRequest {
12881314
type Error = Bolt12ParseError;
12891315

@@ -2219,6 +2245,22 @@ mod tests {
22192245
}
22202246
}
22212247

2248+
#[test]
2249+
fn parses_bech32_encoded_invoice_requests() {
2250+
let invoice_requests = [
2251+
"lnr1qqsg7jpsyzz4hcsj0hu6rvjevwhmkceurq7sd5ez8ne3js4qt8acvxcgqgp7szsqzsqpvggzhdvttlk22pw8fmwqqrvzst792mj35ypylj886ljkcmug03wg6he9yqs86ptqzqjcyypqk4jf95qryjcsqywr6kktzrf366ex4yp8cr5r8m32cre3kfea7w0sgzegrzqgucwd37cjyvkgg2lfae8j6wyyx7dj3aqe8j2ncrthhszl8r69lecma5cxclmft4kh8x39jaeqtdl2yy5gsfdqcpvxczf5x0sw"
2252+
];
2253+
for encoded in invoice_requests {
2254+
let decoded = match encoded.parse::<InvoiceRequest>() {
2255+
Ok(decoded) => decoded,
2256+
Err(e) => panic!("Invalid invoice request ({:?}): {}", e, encoded),
2257+
};
2258+
2259+
let reencoded = decoded.to_string();
2260+
assert_eq!(reencoded, encoded, "Re-encoded invoice does not match original");
2261+
}
2262+
}
2263+
22222264
#[test]
22232265
fn parses_invoice_request_with_metadata() {
22242266
let expanded_key = ExpandedKey::new([42; 32]);

lightning/src/offers/merkle.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,9 @@ mod tests {
285285

286286
use crate::ln::channelmanager::PaymentId;
287287
use crate::ln::inbound_payment::ExpandedKey;
288-
use crate::offers::invoice_request::{InvoiceRequest, UnsignedInvoiceRequest};
288+
use crate::offers::invoice_request::UnsignedInvoiceRequest;
289289
use crate::offers::nonce::Nonce;
290290
use crate::offers::offer::{Amount, OfferBuilder};
291-
use crate::offers::parse::Bech32Encode;
292291
use crate::offers::signer::Metadata;
293292
use crate::offers::test_utils::recipient_pubkey;
294293
use crate::util::ser::Writeable;
@@ -477,20 +476,4 @@ mod tests {
477476

478477
assert_eq!(tlv_stream, invoice_request.bytes);
479478
}
480-
481-
impl AsRef<[u8]> for InvoiceRequest {
482-
fn as_ref(&self) -> &[u8] {
483-
&self.bytes
484-
}
485-
}
486-
487-
impl Bech32Encode for InvoiceRequest {
488-
const BECH32_HRP: &'static str = "lnr";
489-
}
490-
491-
impl core::fmt::Display for InvoiceRequest {
492-
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
493-
self.fmt_bech32_str(f)
494-
}
495-
}
496479
}

0 commit comments

Comments
 (0)