#![deny(unused_mut)]
#![deny(broken_intra_doc_links)]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+
#![cfg_attr(feature = "strict", deny(warnings))]
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
pub mod payment;
pub mod utils;
+pub(crate) mod time_utils;
+
extern crate bech32;
extern crate bitcoin_hashes;
#[macro_use] extern crate lightning;
use lightning::routing::router::RouteHint;
use lightning::util::invoice::construct_invoice_preimage;
-use secp256k1::key::PublicKey;
+use secp256k1::PublicKey;
use secp256k1::{Message, Secp256k1};
-use secp256k1::recovery::RecoverableSignature;
+use secp256k1::ecdsa::RecoverableSignature;
use core::fmt::{Display, Formatter, self};
use core::iter::FilterMap;
+use core::num::ParseIntError;
use core::ops::Deref;
use core::slice::Iter;
use core::time::Duration;
+use core::str;
mod de;
mod ser;
#[cfg(not(feature = "std"))]
mod sync;
-pub use de::{ParseError, ParseOrSemanticError};
+/// Errors that indicate what is wrong with the invoice. They have some granularity for debug
+/// reasons, but should generally result in an "invalid BOLT11 invoice" message for the user.
+#[allow(missing_docs)]
+#[derive(PartialEq, Debug, Clone)]
+pub enum ParseError {
+ Bech32Error(bech32::Error),
+ ParseAmountError(ParseIntError),
+ MalformedSignature(secp256k1::Error),
+ BadPrefix,
+ UnknownCurrency,
+ UnknownSiPrefix,
+ MalformedHRP,
+ TooShortDataPart,
+ UnexpectedEndOfTaggedFields,
+ DescriptionDecodeError(str::Utf8Error),
+ PaddingError,
+ IntegerOverflowError,
+ InvalidSegWitProgramLength,
+ InvalidPubKeyHashLength,
+ InvalidScriptHashLength,
+ InvalidRecoveryId,
+ InvalidSliceLength(String),
+
+ /// Not an error, but used internally to signal that a part of the invoice should be ignored
+ /// according to BOLT11
+ Skip,
+}
+
+/// Indicates that something went wrong while parsing or validating the invoice. Parsing errors
+/// should be mostly seen as opaque and are only there for debugging reasons. Semantic errors
+/// like wrong signatures, missing fields etc. could mean that someone tampered with the invoice.
+#[derive(PartialEq, Debug, Clone)]
+pub enum ParseOrSemanticError {
+ /// The invoice couldn't be decoded
+ ParseError(ParseError),
+
+ /// The invoice could be decoded but violates the BOLT11 standard
+ SemanticError(::SemanticError),
+}
/// The number of bits used to represent timestamps as defined in BOLT 11.
const TIMESTAMP_BITS: usize = 35;
/// use bitcoin_hashes::sha256;
///
/// use secp256k1::Secp256k1;
-/// use secp256k1::key::SecretKey;
+/// use secp256k1::SecretKey;
///
/// use lightning::ln::PaymentSecret;
///
/// .current_timestamp()
/// .min_final_cltv_expiry(144)
/// .build_signed(|hash| {
-/// Secp256k1::new().sign_recoverable(hash, &private_key)
+/// Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)
/// })
/// .unwrap();
///
impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::False> {
/// Sets the payment secret and relevant features.
pub fn payment_secret(mut self, payment_secret: PaymentSecret) -> InvoiceBuilder<D, H, T, C, tb::True> {
- let features = InvoiceFeatures::empty()
- .set_variable_length_onion_required()
- .set_payment_secret_required();
+ let mut features = InvoiceFeatures::empty();
+ features.set_variable_length_onion_required();
+ features.set_payment_secret_required();
self.tagged_fields.push(TaggedField::PaymentSecret(payment_secret));
self.tagged_fields.push(TaggedField::Features(features));
self.set_flags()
impl<D: tb::Bool, H: tb::Bool, T: tb::Bool, C: tb::Bool> InvoiceBuilder<D, H, T, C, tb::True> {
/// Sets the `basic_mpp` feature as optional.
pub fn basic_mpp(mut self) -> Self {
- self.tagged_fields = self.tagged_fields
- .drain(..)
- .map(|field| match field {
- TaggedField::Features(f) => TaggedField::Features(f.set_basic_mpp_optional()),
- _ => field,
- })
- .collect();
+ for field in self.tagged_fields.iter_mut() {
+ if let TaggedField::Features(f) = field {
+ f.set_basic_mpp_optional();
+ }
+ }
self
}
}
let hash = Message::from_slice(&self.hash[..])
.expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
- Ok(PayeePubKey(Secp256k1::new().recover(
+ Ok(PayeePubKey(Secp256k1::new().recover_ecdsa(
&hash,
&self.signature
)?))
.expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
let secp_context = Secp256k1::new();
- let verification_result = secp_context.verify(
+ let verification_result = secp_context.verify_ecdsa(
&hash,
&self.signature.to_standard(),
pub_key
fn test_check_signature() {
use TaggedField::*;
use secp256k1::Secp256k1;
- use secp256k1::recovery::{RecoveryId, RecoverableSignature};
- use secp256k1::key::{SecretKey, PublicKey};
+ use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
+ use secp256k1::{SecretKey, PublicKey};
use {SignedRawInvoice, InvoiceSignature, RawInvoice, RawHrp, RawDataPart, Currency, Sha256,
PositiveTimestamp};
let (raw_invoice, _, _) = invoice.into_parts();
let new_signed = raw_invoice.sign::<_, ()>(|hash| {
- Ok(Secp256k1::new().sign_recoverable(hash, &private_key))
+ Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key))
}).unwrap();
assert!(new_signed.check_signature());
use TaggedField::*;
use lightning::ln::features::InvoiceFeatures;
use secp256k1::Secp256k1;
- use secp256k1::key::SecretKey;
+ use secp256k1::SecretKey;
use {RawInvoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp, Invoice,
SemanticError};
let invoice = {
let mut invoice = invoice_template.clone();
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
let mut invoice = invoice_template.clone();
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
let mut invoice = invoice_template.clone();
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert!(Invoice::from_signed(invoice).is_ok());
// No payment secret or features
let invoice = {
let invoice = invoice_template.clone();
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
let invoice = {
let mut invoice = invoice_template.clone();
invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
let invoice = {
let mut invoice = invoice_template.clone();
invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
let mut invoice = invoice_template.clone();
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
- invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
+ invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::MultiplePaymentSecrets));
}
use ::*;
use lightning::routing::router::RouteHintHop;
use std::iter::FromIterator;
- use secp256k1::key::PublicKey;
+ use secp256k1::PublicKey;
let builder = InvoiceBuilder::new(Currency::Bitcoin)
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
use ::*;
use lightning::routing::router::RouteHintHop;
use secp256k1::Secp256k1;
- use secp256k1::key::{SecretKey, PublicKey};
+ use secp256k1::{SecretKey, PublicKey};
use std::time::{UNIX_EPOCH, Duration};
let secp_ctx = Secp256k1::new();
.basic_mpp();
let invoice = builder.clone().build_signed(|hash| {
- secp_ctx.sign_recoverable(hash, &private_key)
+ secp_ctx.sign_ecdsa_recoverable(hash, &private_key)
}).unwrap();
assert!(invoice.check_signature().is_ok());
fn test_default_values() {
use ::*;
use secp256k1::Secp256k1;
- use secp256k1::key::SecretKey;
+ use secp256k1::SecretKey;
let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description("Test".into())
.sign::<_, ()>(|hash| {
let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
let secp_ctx = Secp256k1::new();
- Ok(secp_ctx.sign_recoverable(hash, &privkey))
+ Ok(secp_ctx.sign_ecdsa_recoverable(hash, &privkey))
})
.unwrap();
let invoice = Invoice::from_signed(signed_invoice).unwrap();
fn test_expiration() {
use ::*;
use secp256k1::Secp256k1;
- use secp256k1::key::SecretKey;
+ use secp256k1::SecretKey;
let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description("Test".into())
.sign::<_, ()>(|hash| {
let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
let secp_ctx = Secp256k1::new();
- Ok(secp_ctx.sign_recoverable(hash, &privkey))
+ Ok(secp_ctx.sign_ecdsa_recoverable(hash, &privkey))
})
.unwrap();
let invoice = Invoice::from_signed(signed_invoice).unwrap();