use bitcoin::constants::ChainHash;
pub use lightning::offers::invoice::Bolt12Invoice;
pub use lightning_invoice::Bolt11Invoice;
pub use lightning::offers::offer::{Amount as OfferAmount, Offer};
use std::fmt;
use std::borrow::Borrow;
use std::str::FromStr;
use bitcoin::{Amount, Network};
use bitcoin::bech32::{encode_to_fmt, EncodeError, Hrp, NoChecksum, primitives::decode::CheckedHrpstring};
use bitcoin::hashes::{sha256, Hash};
use bitcoin::secp256k1::Message;
use lightning::offers::parse::Bolt12ParseError;
use lightning::util::ser::Writeable;
use bitcoin_ext::{AmountExt, P2TR_DUST};
use crate::SECP;
const BECH32_BOLT12_INVOICE_HRP: &str = "lni";
pub const HTLC_MIN_FEE: Amount = P2TR_DUST;
pub const PREIMAGE_SIZE: usize = 32;
pub const PAYMENT_HASH_SIZE: usize = 32;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Preimage([u8; PREIMAGE_SIZE]);
impl_byte_newtype!(Preimage, PREIMAGE_SIZE);
impl Preimage {
pub fn random() -> Preimage {
Preimage(rand::random())
}
pub fn compute_payment_hash(&self) -> PaymentHash {
sha256::Hash::hash(self.as_ref()).into()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct PaymentHash([u8; PAYMENT_HASH_SIZE]);
impl_byte_newtype!(PaymentHash, PAYMENT_HASH_SIZE);
impl From<sha256::Hash> for PaymentHash {
fn from(hash: sha256::Hash) -> Self {
PaymentHash(hash.to_byte_array())
}
}
impl From<Preimage> for PaymentHash {
fn from(preimage: Preimage) -> Self {
preimage.compute_payment_hash()
}
}
impl From<lightning::types::payment::PaymentHash> for PaymentHash {
fn from(hash: lightning::types::payment::PaymentHash) -> Self {
PaymentHash(hash.0)
}
}
impl<'a> From<&'a Bolt11Invoice> for PaymentHash {
fn from(i: &'a Bolt11Invoice) -> Self {
(*i.payment_hash()).into()
}
}
impl From<Bolt11Invoice> for PaymentHash {
fn from(i: Bolt11Invoice) -> Self {
(&i).into()
}
}
impl PaymentHash {
pub fn to_sha256_hash(&self) -> sha256::Hash {
sha256::Hash::from_byte_array(self.0)
}
}
#[derive(Debug, Clone)]
pub enum PaymentStatus {
Pending,
Success(Preimage),
Failed,
}
impl fmt::Display for PaymentStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Invoice {
Bolt11(Bolt11Invoice),
Bolt12(Bolt12Invoice),
}
#[derive(Debug, thiserror::Error)]
#[error("cannot parse invoice")]
pub struct InvoiceParseError;
impl FromStr for Invoice {
type Err = InvoiceParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(bolt11) = Bolt11Invoice::from_str(s) {
Ok(Invoice::Bolt11(bolt11))
} else if let Ok(bolt12) = Bolt12Invoice::from_str(s) {
Ok(Invoice::Bolt12(bolt12))
} else {
Err(InvoiceParseError)
}
}
}
impl From<Bolt11Invoice> for Invoice {
fn from(invoice: Bolt11Invoice) -> Self {
Invoice::Bolt11(invoice)
}
}
impl From<Bolt12Invoice> for Invoice {
fn from(invoice: Bolt12Invoice) -> Self {
Invoice::Bolt12(invoice)
}
}
impl<'a> TryFrom<&'a str> for Invoice {
type Error = <Invoice as FromStr>::Err;
fn try_from(invoice: &'a str) -> Result<Self, Self::Error> {
FromStr::from_str(invoice)
}
}
impl TryFrom<String> for Invoice {
type Error = <Invoice as FromStr>::Err;
fn try_from(invoice: String) -> Result<Self, Self::Error> {
FromStr::from_str(&invoice)
}
}
impl serde::Serialize for Invoice {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.collect_str(self)
}
}
impl<'de> serde::Deserialize<'de> for Invoice {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Invoice;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "a lightning invoice")
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
Invoice::from_str(v).map_err(serde::de::Error::custom)
}
}
d.deserialize_str(Visitor)
}
}
#[derive(Debug, thiserror::Error)]
#[error("invoice amount mismatch: invoice={invoice}, user={user}")]
pub enum CheckAmountError {
#[error("invalid user amount: invoice={invoice}, user={user}")]
InvalidUserAmount { invoice: Amount, user: Amount },
#[error("offer currency is not supported: {amount:?}")]
UnsupportedCurrency { amount: OfferAmount },
#[error("user amount required")]
UserAmountRequired,
}
#[derive(Debug, thiserror::Error)]
#[error("invalid invoice signature: {0}")]
pub struct CheckSignatureError(pub String);
impl Invoice {
pub fn into_bolt11(self) -> Option<Bolt11Invoice> {
match self {
Invoice::Bolt11(invoice) => Some(invoice),
Invoice::Bolt12(_) => None
}
}
pub fn payment_hash(&self) -> PaymentHash {
match self {
Invoice::Bolt11(invoice) => PaymentHash::from(*invoice.payment_hash().as_byte_array()),
Invoice::Bolt12(invoice) => PaymentHash::from(invoice.payment_hash()),
}
}
pub fn network(&self) -> Network {
match self {
Invoice::Bolt11(invoice) => invoice.network(),
Invoice::Bolt12(invoice) => match invoice.chain() {
ChainHash::BITCOIN => Network::Bitcoin,
ChainHash::TESTNET3 => Network::Testnet,
ChainHash::TESTNET4 => Network::Testnet4,
ChainHash::SIGNET => Network::Signet,
ChainHash::REGTEST => Network::Regtest,
_ => panic!("unsupported network"),
},
}
}
pub fn get_final_amount(
&self,
user_amount: Option<Amount>,
) -> Result<Amount, CheckAmountError> {
match self {
Invoice::Bolt11(invoice) => invoice.get_final_amount(user_amount),
Invoice::Bolt12(invoice) => invoice.get_final_amount(user_amount),
}
}
pub fn amount_msat(&self) -> Option<u64> {
match self {
Invoice::Bolt11(invoice) => invoice.amount_milli_satoshis(),
Invoice::Bolt12(invoice) => Some(invoice.amount_msats()),
}
}
pub fn check_signature(&self) -> Result<(), CheckSignatureError> {
match self {
Invoice::Bolt11(invoice) => invoice
.check_signature()
.map_err(|e| CheckSignatureError(e.to_string())),
Invoice::Bolt12(invoice) => invoice.check_signature(),
}
}
}
impl fmt::Display for Invoice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Invoice::Bolt11(invoice) => write!(f, "{}", invoice.to_string()),
Invoice::Bolt12(invoice) => encode_to_fmt::<NoChecksum, _>(
f,
Hrp::parse("lni").unwrap(),
&invoice.bytes(),
)
.map_err(|e| match e {
EncodeError::Fmt(e) => e,
_ => fmt::Error {},
}),
}
}
}
fn get_invoice_final_amount(invoice_amount: Option<Amount>, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
match (invoice_amount, user_amount) {
(Some(invoice_amount), Some(user_amount)) => {
if user_amount >= invoice_amount && user_amount <= invoice_amount * 2 {
return Ok(user_amount);
}
return Err(CheckAmountError::InvalidUserAmount {
invoice: invoice_amount,
user: user_amount,
});
}
(Some(invoice_amount), None) => {
return Ok(invoice_amount);
}
(None, Some(user_amount)) => {
return Ok(user_amount);
}
(None, None) => {
return Err(CheckAmountError::UserAmountRequired);
}
}
}
pub trait Bolt11InvoiceExt: Borrow<Bolt11Invoice> {
fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
let invoice_amount = self.borrow().amount_milli_satoshis()
.map(Amount::from_msat_ceil);
get_invoice_final_amount(invoice_amount, user_amount)
}
}
impl Bolt11InvoiceExt for Bolt11Invoice {}
pub trait Bolt12InvoiceExt: Borrow<Bolt12Invoice> {
fn payment_hash(&self) -> PaymentHash { PaymentHash::from(self.borrow().payment_hash()) }
fn get_final_amount(&self, user_amount: Option<Amount>) -> Result<Amount, CheckAmountError> {
let invoice_amount = Amount::from_msat_ceil(self.borrow().amount_msats());
get_invoice_final_amount(Some(invoice_amount), user_amount)
}
fn check_signature(&self) -> Result<(), CheckSignatureError> {
let message = Message::from_digest(self.borrow().signable_hash());
let signature = self.borrow().signature();
if let Some(pubkey) = self.borrow().issuer_signing_pubkey() {
Ok(SECP.verify_schnorr(&signature, &message, &pubkey.into())
.map_err(|_| CheckSignatureError("invalid signature".to_string()))?)
} else {
Err(CheckSignatureError("no pubkey on offer, cannot verify signature".to_string()))
}
}
fn bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
self.borrow().write(&mut bytes).expect("Writing into a Vec is infallible");
bytes
}
fn from_bytes(bytes: &[u8]) -> Result<Bolt12Invoice, Bolt12ParseError> {
Bolt12Invoice::try_from(bytes.to_vec())
}
fn validate_issuance(&self, offer: &Offer) -> Result<(), CheckSignatureError> {
if self.borrow().issuer_signing_pubkey() != offer.issuer_signing_pubkey() {
Err(CheckSignatureError("public keys mismatch".to_string()))
} else {
Ok(())
}
}
fn from_str(s: &str) -> Result<Bolt12Invoice, Bolt12ParseError> {
let dec = CheckedHrpstring::new::<NoChecksum>(&s)?;
if dec.hrp().to_lowercase() != BECH32_BOLT12_INVOICE_HRP {
return Err(Bolt12ParseError::InvalidBech32Hrp);
}
let data = dec.byte_iter().collect::<Vec<_>>();
Bolt12Invoice::try_from(data)
}
}
impl Bolt12InvoiceExt for Bolt12Invoice {}