Skip to content

Commit a5ecb85

Browse files
authored
Merge pull request #2162 from jkczyz/2023-04-invoice-hash
2 parents 568a20b + 39befa1 commit a5ecb85

File tree

3 files changed

+54
-34
lines changed

3 files changed

+54
-34
lines changed

lightning-invoice/src/lib.rs

+43-33
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
//! invoices and functions to create, encode and decode these. If you just want to use the standard
1919
//! en-/decoding functionality this should get you started:
2020
//!
21-
//! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
22-
//! * For constructing invoices use the `InvoiceBuilder`
23-
//! * For serializing invoices use the `Display`/`ToString` traits
21+
//! * For parsing use `str::parse::<Invoice>(&self)` (see [`Invoice::from_str`])
22+
//! * For constructing invoices use the [`InvoiceBuilder`]
23+
//! * For serializing invoices use the [`Display`]/[`ToString`] traits
24+
//!
25+
//! [`Invoice::from_str`]: crate::Invoice#impl-FromStr
2426
2527
#[cfg(not(any(feature = "std", feature = "no-std")))]
2628
compile_error!("at least one of the `std` or `no-std` features must be enabled");
@@ -160,7 +162,7 @@ pub const DEFAULT_EXPIRY_TIME: u64 = 3600;
160162
/// [`MIN_FINAL_CLTV_EXPIRY_DELTA`]: lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA
161163
pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
162164

163-
/// Builder for `Invoice`s. It's the most convenient and advised way to use this library. It ensures
165+
/// Builder for [`Invoice`]s. It's the most convenient and advised way to use this library. It ensures
164166
/// that only a semantically and syntactically correct Invoice can be built using it.
165167
///
166168
/// ```
@@ -212,8 +214,8 @@ pub const DEFAULT_MIN_FINAL_CLTV_EXPIRY_DELTA: u64 = 18;
212214
/// # Type parameters
213215
/// The two parameters `D` and `H` signal if the builder already contains the correct amount of the
214216
/// given field:
215-
/// * `D`: exactly one `Description` or `DescriptionHash`
216-
/// * `H`: exactly one `PaymentHash`
217+
/// * `D`: exactly one [`TaggedField::Description`] or [`TaggedField::DescriptionHash`]
218+
/// * `H`: exactly one [`TaggedField::PaymentHash`]
217219
/// * `T`: the timestamp is set
218220
///
219221
/// This is not exported to bindings users as we likely need to manually select one set of boolean type parameters.
@@ -236,9 +238,11 @@ pub struct InvoiceBuilder<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S:
236238
/// Represents a syntactically and semantically correct lightning BOLT11 invoice.
237239
///
238240
/// There are three ways to construct an `Invoice`:
239-
/// 1. using `InvoiceBuilder`
240-
/// 2. using `Invoice::from_signed(SignedRawInvoice)`
241-
/// 3. using `str::parse::<Invoice>(&str)`
241+
/// 1. using [`InvoiceBuilder`]
242+
/// 2. using [`Invoice::from_signed`]
243+
/// 3. using `str::parse::<Invoice>(&str)` (see [`Invoice::from_str`])
244+
///
245+
/// [`Invoice::from_str`]: crate::Invoice#impl-FromStr
242246
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
243247
pub struct Invoice {
244248
signed_invoice: SignedRawInvoice,
@@ -258,34 +262,34 @@ pub enum InvoiceDescription<'f> {
258262
Hash(&'f Sha256),
259263
}
260264

261-
/// Represents a signed `RawInvoice` with cached hash. The signature is not checked and may be
265+
/// Represents a signed [`RawInvoice`] with cached hash. The signature is not checked and may be
262266
/// invalid.
263267
///
264268
/// # Invariants
265-
/// The hash has to be either from the deserialized invoice or from the serialized `raw_invoice`.
269+
/// The hash has to be either from the deserialized invoice or from the serialized [`RawInvoice`].
266270
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
267271
pub struct SignedRawInvoice {
268272
/// The rawInvoice that the signature belongs to
269273
raw_invoice: RawInvoice,
270274

271-
/// Hash of the `RawInvoice` that will be used to check the signature.
275+
/// Hash of the [`RawInvoice`] that will be used to check the signature.
272276
///
273277
/// * if the `SignedRawInvoice` was deserialized the hash is of from the original encoded form,
274278
/// since it's not guaranteed that encoding it again will lead to the same result since integers
275279
/// could have been encoded with leading zeroes etc.
276280
/// * if the `SignedRawInvoice` was constructed manually the hash will be the calculated hash
277-
/// from the `RawInvoice`
281+
/// from the [`RawInvoice`]
278282
hash: [u8; 32],
279283

280284
/// signature of the payment request
281285
signature: InvoiceSignature,
282286
}
283287

284-
/// Represents an syntactically correct Invoice for a payment on the lightning network,
288+
/// Represents an syntactically correct [`Invoice`] for a payment on the lightning network,
285289
/// but without the signature information.
286-
/// De- and encoding should not lead to information loss but may lead to different hashes.
290+
/// Decoding and encoding should not lead to information loss but may lead to different hashes.
287291
///
288-
/// For methods without docs see the corresponding methods in `Invoice`.
292+
/// For methods without docs see the corresponding methods in [`Invoice`].
289293
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
290294
pub struct RawInvoice {
291295
/// human readable part
@@ -295,7 +299,7 @@ pub struct RawInvoice {
295299
pub data: RawDataPart,
296300
}
297301

298-
/// Data of the `RawInvoice` that is encoded in the human readable part
302+
/// Data of the [`RawInvoice`] that is encoded in the human readable part.
299303
///
300304
/// This is not exported to bindings users as we don't yet support `Option<Enum>`
301305
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
@@ -310,7 +314,7 @@ pub struct RawHrp {
310314
pub si_prefix: Option<SiPrefix>,
311315
}
312316

313-
/// Data of the `RawInvoice` that is encoded in the data part
317+
/// Data of the [`RawInvoice`] that is encoded in the data part
314318
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
315319
pub struct RawDataPart {
316320
/// generation time of the invoice
@@ -564,7 +568,8 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBui
564568
}
565569

566570
impl<D: tb::Bool, H: tb::Bool, C: tb::Bool, S: tb::Bool> InvoiceBuilder<D, H, tb::True, C, S> {
567-
/// Builds a `RawInvoice` if no `CreationError` occurred while construction any of the fields.
571+
/// Builds a [`RawInvoice`] if no [`CreationError`] occurred while construction any of the
572+
/// fields.
568573
pub fn build_raw(self) -> Result<RawInvoice, CreationError> {
569574

570575
// If an error occurred at any time before, return it now
@@ -753,17 +758,17 @@ impl SignedRawInvoice {
753758
(self.raw_invoice, self.hash, self.signature)
754759
}
755760

756-
/// The `RawInvoice` which was signed.
761+
/// The [`RawInvoice`] which was signed.
757762
pub fn raw_invoice(&self) -> &RawInvoice {
758763
&self.raw_invoice
759764
}
760765

761-
/// The hash of the `RawInvoice` that was signed.
766+
/// The hash of the [`RawInvoice`] that was signed.
762767
pub fn signable_hash(&self) -> &[u8; 32] {
763768
&self.hash
764769
}
765770

766-
/// InvoiceSignature for the invoice.
771+
/// Signature for the invoice.
767772
pub fn signature(&self) -> &InvoiceSignature {
768773
&self.signature
769774
}
@@ -881,8 +886,8 @@ impl RawInvoice {
881886
)
882887
}
883888

884-
/// Signs the invoice using the supplied `sign_function`. This function MAY fail with an error
885-
/// of type `E`. Since the signature of a `SignedRawInvoice` is not required to be valid there
889+
/// Signs the invoice using the supplied `sign_method`. This function MAY fail with an error of
890+
/// type `E`. Since the signature of a [`SignedRawInvoice`] is not required to be valid there
886891
/// are no constraints regarding the validity of the produced signature.
887892
///
888893
/// This is not exported to bindings users as we don't currently support passing function pointers into methods
@@ -1033,6 +1038,11 @@ impl From<PositiveTimestamp> for SystemTime {
10331038
}
10341039

10351040
impl Invoice {
1041+
/// The hash of the [`RawInvoice`] that was signed.
1042+
pub fn signable_hash(&self) -> [u8; 32] {
1043+
self.signed_invoice.hash
1044+
}
1045+
10361046
/// Transform the `Invoice` into it's unchecked version
10371047
pub fn into_signed_raw(self) -> SignedRawInvoice {
10381048
self.signed_invoice
@@ -1137,7 +1147,7 @@ impl Invoice {
11371147
Ok(())
11381148
}
11391149

1140-
/// Constructs an `Invoice` from a `SignedRawInvoice` by checking all its invariants.
1150+
/// Constructs an `Invoice` from a [`SignedRawInvoice`] by checking all its invariants.
11411151
/// ```
11421152
/// use lightning_invoice::*;
11431153
///
@@ -1347,7 +1357,7 @@ impl TaggedField {
13471357
impl Description {
13481358

13491359
/// Creates a new `Description` if `description` is at most 1023 __bytes__ long,
1350-
/// returns `CreationError::DescriptionTooLong` otherwise
1360+
/// returns [`CreationError::DescriptionTooLong`] otherwise
13511361
///
13521362
/// Please note that single characters may use more than one byte due to UTF8 encoding.
13531363
pub fn new(description: String) -> Result<Description, CreationError> {
@@ -1358,7 +1368,7 @@ impl Description {
13581368
}
13591369
}
13601370

1361-
/// Returns the underlying description `String`
1371+
/// Returns the underlying description [`String`]
13621372
pub fn into_inner(self) -> String {
13631373
self.0
13641374
}
@@ -1398,7 +1408,7 @@ impl ExpiryTime {
13981408
ExpiryTime(Duration::from_secs(seconds))
13991409
}
14001410

1401-
/// Construct an `ExpiryTime` from a `Duration`, dropping the sub-second part.
1411+
/// Construct an `ExpiryTime` from a [`Duration`], dropping the sub-second part.
14021412
pub fn from_duration(duration: Duration) -> ExpiryTime {
14031413
Self::from_seconds(duration.as_secs())
14041414
}
@@ -1408,7 +1418,7 @@ impl ExpiryTime {
14081418
self.0.as_secs()
14091419
}
14101420

1411-
/// Returns a reference to the underlying `Duration` (=expiry time)
1421+
/// Returns a reference to the underlying [`Duration`] (=expiry time)
14121422
pub fn as_duration(&self) -> &Duration {
14131423
&self.0
14141424
}
@@ -1460,10 +1470,10 @@ impl Deref for SignedRawInvoice {
14601470
}
14611471
}
14621472

1463-
/// Errors that may occur when constructing a new `RawInvoice` or `Invoice`
1473+
/// Errors that may occur when constructing a new [`RawInvoice`] or [`Invoice`]
14641474
#[derive(Eq, PartialEq, Debug, Clone)]
14651475
pub enum CreationError {
1466-
/// The supplied description string was longer than 639 __bytes__ (see [`Description::new(…)`](./struct.Description.html#method.new))
1476+
/// The supplied description string was longer than 639 __bytes__ (see [`Description::new`])
14671477
DescriptionTooLong,
14681478

14691479
/// The specified route has too many hops and can't be encoded
@@ -1504,7 +1514,7 @@ impl Display for CreationError {
15041514
#[cfg(feature = "std")]
15051515
impl std::error::Error for CreationError { }
15061516

1507-
/// Errors that may occur when converting a `RawInvoice` to an `Invoice`. They relate to the
1517+
/// Errors that may occur when converting a [`RawInvoice`] to an [`Invoice`]. They relate to the
15081518
/// requirements sections in BOLT #11
15091519
#[derive(Eq, PartialEq, Debug, Clone)]
15101520
pub enum SemanticError {
@@ -1560,7 +1570,7 @@ impl Display for SemanticError {
15601570
#[cfg(feature = "std")]
15611571
impl std::error::Error for SemanticError { }
15621572

1563-
/// When signing using a fallible method either an user-supplied `SignError` or a `CreationError`
1573+
/// When signing using a fallible method either an user-supplied `SignError` or a [`CreationError`]
15641574
/// may occur.
15651575
#[derive(Eq, PartialEq, Debug, Clone)]
15661576
pub enum SignOrCreationError<S = ()> {

lightning/src/offers/invoice.rs

+10
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,11 @@ impl Invoice {
469469
self.signature
470470
}
471471

472+
/// Hash that was used for signing the invoice.
473+
pub fn signable_hash(&self) -> [u8; 32] {
474+
merkle::message_digest(SIGNATURE_TAG, &self.bytes).as_ref().clone()
475+
}
476+
472477
#[cfg(test)]
473478
fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
474479
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) =
@@ -937,6 +942,11 @@ mod tests {
937942
).is_ok()
938943
);
939944

945+
let digest = Message::from_slice(&invoice.signable_hash()).unwrap();
946+
let pubkey = recipient_pubkey().into();
947+
let secp_ctx = Secp256k1::verification_only();
948+
assert!(secp_ctx.verify_schnorr(&invoice.signature, &digest, &pubkey).is_ok());
949+
940950
assert_eq!(
941951
invoice.as_tlv_stream(),
942952
(

lightning/src/offers/merkle.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub(super) fn verify_signature(
6666
secp_ctx.verify_schnorr(signature, &digest, &pubkey)
6767
}
6868

69-
fn message_digest(tag: &str, bytes: &[u8]) -> Message {
69+
pub(super) fn message_digest(tag: &str, bytes: &[u8]) -> Message {
7070
let tag = sha256::Hash::hash(tag.as_bytes());
7171
let merkle_root = root_hash(bytes);
7272
Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap()

0 commit comments

Comments
 (0)