Skip to content

Commit a35d71a

Browse files
committed
WIP: invoice_request with reflected offer
1 parent 4fabc31 commit a35d71a

File tree

5 files changed

+136
-1
lines changed

5 files changed

+136
-1
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Data structures and encoding for `invoice_request` messages.
11+
12+
use bitcoin::hash_types::BlockHash;
13+
use bitcoin::secp256k1::PublicKey;
14+
use bitcoin::secp256k1::schnorr::Signature;
15+
use core::convert::TryFrom;
16+
use core::str::FromStr;
17+
use ln::features::OfferFeatures;
18+
use offers::{Offer, PayerTlvStream};
19+
use offers::merkle::SignatureTlvStream;
20+
use offers::offer::{OfferTlvStream, ParsedOffer};
21+
use offers::parse::{Bech32Encode, ParseError, SemanticError};
22+
23+
///
24+
pub struct InvoiceRequest {
25+
bytes: Vec<u8>,
26+
offer: Offer,
27+
chain: Option<BlockHash>,
28+
amount_msat: Option<u64>,
29+
features: Option<OfferFeatures>,
30+
quantity: Option<u64>,
31+
payer_id: Option<PublicKey>,
32+
payer_note: Option<String>,
33+
payer_info: Option<Vec<u8>>,
34+
signature: Option<Signature>,
35+
}
36+
37+
impl AsRef<[u8]> for InvoiceRequest {
38+
fn as_ref(&self) -> &[u8] {
39+
&self.bytes
40+
}
41+
}
42+
43+
tlv_stream!(struct InvoiceRequestTlvStream {
44+
(80, chain: BlockHash),
45+
(82, amount: u64),
46+
(84, features: OfferFeatures),
47+
(86, quantity: u64),
48+
(88, payer_id: PublicKey),
49+
(89, payer_note: String),
50+
});
51+
52+
impl Bech32Encode for InvoiceRequest {
53+
type TlvStream = FullInvoiceRequestTlvStream;
54+
55+
const BECH32_HRP: &'static str = "lnr";
56+
}
57+
58+
type FullInvoiceRequestTlvStream =
59+
(PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream);
60+
61+
impl FromStr for InvoiceRequest {
62+
type Err = ParseError;
63+
64+
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
65+
let (tlv_stream, bytes) = InvoiceRequest::from_bech32_str(s)?;
66+
Ok(InvoiceRequest::try_from((tlv_stream, bytes))?)
67+
}
68+
}
69+
70+
type ParsedInvoiceRequest = (FullInvoiceRequestTlvStream, Vec<u8>);
71+
72+
impl TryFrom<ParsedInvoiceRequest> for InvoiceRequest {
73+
type Error = SemanticError;
74+
75+
fn try_from(invoice_request: ParsedInvoiceRequest) -> Result<Self, Self::Error> {
76+
let ((
77+
PayerTlvStream { payer_info },
78+
offer_tlv_stream,
79+
InvoiceRequestTlvStream { chain, amount, features, quantity, payer_id, payer_note },
80+
SignatureTlvStream { signature },
81+
), bytes) = invoice_request;
82+
83+
let offer = Offer::try_from(ParsedOffer(offer_tlv_stream, vec![]))?;
84+
85+
let chain = match chain {
86+
None => None,
87+
Some(chain) if chain == offer.chain() => Some(chain),
88+
Some(_) => return Err(SemanticError::UnsupportedChain),
89+
};
90+
91+
// TODO: Check remaining fields against the reflected offer
92+
let amount_msat = amount.map(Into::into);
93+
94+
let quantity = quantity.map(Into::into);
95+
96+
let payer_note = payer_note.map(Into::into);
97+
98+
let payer_info = payer_info.map(Into::into);
99+
100+
Ok(InvoiceRequest {
101+
bytes, offer, chain, amount_msat, features, quantity, payer_id, payer_note, payer_info,
102+
signature,
103+
})
104+
}
105+
}
106+
107+
impl core::fmt::Display for InvoiceRequest {
108+
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
109+
self.fmt_bech32_str(f)
110+
}
111+
}

lightning/src/offers/merkle.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@
1010
//! Tagged hashes for use in signature calculation and verification.
1111
1212
use bitcoin::hashes::{Hash, HashEngine, sha256};
13+
use bitcoin::secp256k1::schnorr::Signature;
1314
use util::ser::{BigSize, Readable};
1415

1516
const SIGNATURE_TYPES: core::ops::RangeInclusive<u64> = 240..=1000;
1617

18+
tlv_stream!(struct SignatureTlvStream {
19+
(240, signature: Signature),
20+
});
21+
1722
pub(super) fn root_hash(data: &[u8]) -> sha256::Hash {
1823
let mut tlv_stream = TlvStream::new(&data[..]).peekable();
1924
let nonce_tag = tagged_hash_engine(sha256::Hash::from_engine({

lightning/src/offers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! Implementation of Lightning Offers
1111
//! ([BOLT 12](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md)).
1212
13+
mod invoice_request;
1314
mod merkle;
1415
mod offer;
1516
mod parse;

lightning/src/util/ser.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,24 @@ impl<A: Writeable, B: Writeable, C: Writeable> Writeable for (A, B, C) {
10431043
}
10441044
}
10451045

1046+
impl<A: Readable, B: Readable, C: Readable, D: Readable> Readable for (A, B, C, D) {
1047+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
1048+
let a: A = Readable::read(r)?;
1049+
let b: B = Readable::read(r)?;
1050+
let c: C = Readable::read(r)?;
1051+
let d: D = Readable::read(r)?;
1052+
Ok((a, b, c, d))
1053+
}
1054+
}
1055+
impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable> Writeable for (A, B, C, D) {
1056+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
1057+
self.0.write(w)?;
1058+
self.1.write(w)?;
1059+
self.2.write(w)?;
1060+
self.3.write(w)
1061+
}
1062+
}
1063+
10461064
impl Writeable for () {
10471065
fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
10481066
Ok(())

lightning/src/util/ser_macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ macro_rules! tlv_stream {
437437
#[derive(Debug)]
438438
pub(crate) struct $name {
439439
$(
440-
$field: Option<tlv_record_type!($fieldty$(<$gen>)?)>,
440+
pub(crate) $field: Option<tlv_record_type!($fieldty$(<$gen>)?)>,
441441
)*
442442
}
443443

0 commit comments

Comments
 (0)