Skip to main content

fiber_types/
invoice.rs

1//! Invoice-related types: status, currency, hash algorithm, script wrapper, signature.
2
3use crate::serde_utils::EntityHex;
4
5use crate::gen::invoice as gen_invoice;
6use arcode::bitbit::{BitReader, BitWriter, MSB};
7use arcode::{ArithmeticDecoder, ArithmeticEncoder, EOFKind, Model};
8use bech32::{encode, u5, FromBase32, ToBase32, Variant, WriteBase32};
9use ckb_hash::blake2b_256;
10use ckb_types::packed::Script as PackedScript;
11use ckb_types::prelude::{Pack, Unpack};
12use gen_invoice::{
13    Description, ExpiryTime, FallbackAddr, Feature, FinalHtlcMinimumExpiryDelta, FinalHtlcTimeout,
14    InvoiceAttr, InvoiceAttrUnion, InvoiceAttrsVec, PayeePublicKey, PaymentHash, PaymentSecret,
15    RawInvoiceDataBuilder, UdtScript,
16};
17use molecule::prelude::Byte;
18use molecule::prelude::{Builder, Entity};
19use nom::{branch::alt, combinator::opt};
20use nom::{
21    bytes::{complete::take_while1, streaming::tag},
22    IResult,
23};
24use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
25use serde::{Deserialize, Serialize};
26use serde_with::serde_as;
27use sha2::{Digest, Sha256};
28use std::cmp::Ordering;
29use std::fmt::Display;
30use std::io::{Cursor, Result as IoResult};
31use std::num::ParseIntError;
32use std::str::FromStr;
33use thiserror::Error;
34
35/// Wrapper for molecule verification errors.
36#[derive(Error, Debug)]
37pub struct VerificationError(pub molecule::error::VerificationError);
38
39impl PartialEq for VerificationError {
40    fn eq(&self, _other: &Self) -> bool {
41        false
42    }
43}
44
45impl Display for VerificationError {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        self.0.fmt(f)
48    }
49}
50
51/// Errors that can occur when parsing or validating an invoice.
52#[derive(Error, PartialEq, Debug)]
53pub enum InvoiceError {
54    /// Bech32 encoding/decoding error.
55    #[error("Bech32 error: {0}")]
56    Bech32Error(bech32::Error),
57    /// Molecule serialization error.
58    #[error("Molecule error: {0}")]
59    MoleculeError(VerificationError),
60    /// Failed to parse amount from HRP.
61    #[error("Failed to parse amount: {0}")]
62    ParseAmountError(ParseIntError),
63    /// Unknown currency in HRP.
64    #[error("Unknown currency: {0}")]
65    UnknownCurrency(String),
66    /// Unknown SI prefix in amount.
67    #[error("Unknown si prefix: {0}")]
68    UnknownSiPrefix(String),
69    /// Malformed HRP.
70    #[error("Parsing failed with malformed HRP: {0}")]
71    MalformedHRP(String),
72    /// Data part is too short.
73    #[error("Too short data part")]
74    TooShortDataPart,
75    /// Unexpected end of tagged fields.
76    #[error("Unexpected end of tagged fields")]
77    UnexpectedEndOfTaggedFields,
78    /// Integer overflow error.
79    #[error("Integer overflow error")]
80    IntegerOverflowError,
81    /// Invalid recovery ID in signature.
82    #[error("Invalid recovery id")]
83    InvalidRecoveryId,
84    /// Invalid slice length.
85    #[error("Invalid slice length: {0}")]
86    InvalidSliceLength(String),
87    /// Invalid signature.
88    #[error("Invalid signature")]
89    InvalidSignature,
90    /// Duplicated attribute key.
91    #[error("Duplicated attribute key: {0}")]
92    DuplicatedAttributeKey(String),
93    /// Payment secret is required for MPP payments.
94    #[error("Payment secret is required for MPP payments")]
95    PaymentSecretRequiredForMpp,
96    /// Both payment_hash and payment_preimage are set.
97    #[error("Both payment_hash and payment_preimage are set")]
98    BothPaymenthashAndPreimage,
99    /// Neither payment_hash nor payment_preimage is set.
100    #[error("Neither payment_hash nor payment_preimage is set")]
101    NeitherPaymenthashNorPreimage,
102    /// An error occurred during signing.
103    #[error("Sign error")]
104    SignError,
105    /// Hex decode error.
106    #[error("Hex decode error: {0}")]
107    HexDecodeError(#[from] hex::FromHexError),
108    /// Duplicated invoice found.
109    #[error("Duplicated invoice found: {0}")]
110    DuplicatedInvoice(String),
111    /// Description is too long.
112    #[error("Description with length of {0} is too long, max length is 639")]
113    DescriptionTooLong(usize),
114    /// Invoice not found.
115    #[error("Invoice not found")]
116    InvoiceNotFound,
117    /// Invoice already exists.
118    #[error("Invoice already exists")]
119    InvoiceAlreadyExists,
120    /// Deprecated attribute.
121    #[error("Deprecated attribute: {0}")]
122    DeprecatedAttribute(String),
123}
124
125/// Size of the signature in u5 encoding.
126pub const SIGNATURE_U5_SIZE: usize = 104;
127
128/// Maximum allowed length for an invoice description.
129pub const MAX_DESCRIPTION_LENGTH: usize = 639;
130
131/// Encodes bytes and returns the compressed form.
132/// This is used for encoding the invoice data, to make the final Invoice encoded address shorter.
133pub(crate) fn ar_encompress(data: &[u8]) -> IoResult<Vec<u8>> {
134    let mut model = Model::builder().num_bits(8).eof(EOFKind::EndAddOne).build();
135    let mut compressed_writer = BitWriter::new(Cursor::new(vec![]));
136    let mut encoder = ArithmeticEncoder::new(48);
137    for &sym in data {
138        encoder.encode(sym as u32, &model, &mut compressed_writer)?;
139        model.update_symbol(sym as u32);
140    }
141
142    encoder.encode(model.eof(), &model, &mut compressed_writer)?;
143    encoder.finish_encode(&mut compressed_writer)?;
144    compressed_writer.pad_to_byte()?;
145
146    Ok(compressed_writer.get_ref().get_ref().clone())
147}
148
149/// Decompresses the data.
150pub(crate) fn ar_decompress(data: &[u8]) -> IoResult<Vec<u8>> {
151    let mut model = Model::builder().num_bits(8).eof(EOFKind::EndAddOne).build();
152    let mut input_reader = BitReader::<_, MSB>::new(data);
153    let mut decoder = ArithmeticDecoder::new(48);
154    let mut decompressed_data = vec![];
155
156    while !decoder.finished() {
157        let sym = decoder.decode(&model, &mut input_reader)?;
158        model.update_symbol(sym);
159        decompressed_data.push(sym as u8);
160    }
161
162    decompressed_data.pop(); // remove the EOF
163    Ok(decompressed_data)
164}
165
166/// Construct the invoice's HRP and signatureless data into a preimage to be hashed.
167pub fn construct_invoice_preimage(hrp_bytes: &[u8], data_without_signature: &[u5]) -> Vec<u8> {
168    let mut preimage = Vec::<u8>::from(hrp_bytes);
169
170    let mut data_part = Vec::from(data_without_signature);
171    let overhang = (data_part.len() * 5) % 8;
172    if overhang > 0 {
173        // add padding if data does not end at a byte boundary
174        data_part.push(u5::try_from_u8(0).expect("u5 from u8"));
175
176        // if overhang is in (1..3) we need to add u5(0) padding two times
177        if overhang < 3 {
178            data_part.push(u5::try_from_u8(0).expect("u5 from u8"));
179        }
180    }
181
182    preimage.extend_from_slice(
183        &Vec::<u8>::from_base32(&data_part)
184            .expect("No padding error may occur due to appended zero above."),
185    );
186    preimage
187}
188
189fn nom_scan_hrp(input: &str) -> IResult<&str, (&str, Option<&str>)> {
190    let (input, currency) = alt((tag("fibb"), tag("fibt"), tag("fibd")))(input)?;
191    let (input, amount) = opt(take_while1(|c: char| c.is_numeric()))(input)?;
192    Ok((input, (currency, amount)))
193}
194
195/// Parse the human-readable part of an invoice.
196pub fn parse_hrp(input: &str) -> Result<(Currency, Option<u128>), InvoiceError> {
197    match nom_scan_hrp(input) {
198        Ok((left, (currency, amount))) => {
199            if !left.is_empty() {
200                return Err(InvoiceError::MalformedHRP(format!(
201                    "{}, unexpected ending `{}`",
202                    input, left
203                )));
204            }
205            let currency =
206                Currency::from_str(currency).map_err(|e| InvoiceError::UnknownCurrency(e.0))?;
207            let amount = amount
208                .map(|x| x.parse().map_err(InvoiceError::ParseAmountError))
209                .transpose()?;
210            Ok((currency, amount))
211        }
212        Err(_) => Err(InvoiceError::MalformedHRP(input.to_string())),
213    }
214}
215
216/// The currency of the invoice, can also used to represent the CKB network chain.
217#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
218pub enum CkbInvoiceStatus {
219    /// The invoice is open and can be paid.
220    Open,
221    /// The invoice is cancelled.
222    Cancelled,
223    /// The invoice is expired.
224    Expired,
225    /// The invoice is received, but not settled yet.
226    Received,
227    /// The invoice is paid.
228    Paid,
229}
230
231impl Display for CkbInvoiceStatus {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        match self {
234            CkbInvoiceStatus::Open => write!(f, "Open"),
235            CkbInvoiceStatus::Cancelled => write!(f, "Cancelled"),
236            CkbInvoiceStatus::Expired => write!(f, "Expired"),
237            CkbInvoiceStatus::Received => write!(f, "Received"),
238            CkbInvoiceStatus::Paid => write!(f, "Paid"),
239        }
240    }
241}
242
243/// The currency of the invoice, can also used to represent the CKB network chain.
244#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Default)]
245pub enum Currency {
246    /// The mainnet currency of CKB.
247    Fibb,
248    /// The testnet currency of the CKB network.
249    Fibt,
250    /// The devnet currency of the CKB network.
251    #[default]
252    Fibd,
253}
254
255impl Display for Currency {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        match self {
258            Currency::Fibb => write!(f, "fibb"),
259            Currency::Fibt => write!(f, "fibt"),
260            Currency::Fibd => write!(f, "fibd"),
261        }
262    }
263}
264
265/// Error for unknown currency
266#[derive(thiserror::Error, Debug)]
267#[error("Unknown currency: {0}")]
268pub struct UnknownCurrencyError(pub String);
269
270impl FromStr for Currency {
271    type Err = UnknownCurrencyError;
272
273    fn from_str(s: &str) -> Result<Self, Self::Err> {
274        match s {
275            "fibb" => Ok(Self::Fibb),
276            "fibt" => Ok(Self::Fibt),
277            "fibd" => Ok(Self::Fibd),
278            _ => Err(UnknownCurrencyError(s.to_string())),
279        }
280    }
281}
282
283impl TryFrom<u8> for Currency {
284    type Error = UnknownCurrencyError;
285
286    fn try_from(byte: u8) -> Result<Self, Self::Error> {
287        match byte {
288            0 => Ok(Self::Fibb),
289            1 => Ok(Self::Fibt),
290            2 => Ok(Self::Fibd),
291            _ => Err(UnknownCurrencyError(byte.to_string())),
292        }
293    }
294}
295
296/// HashAlgorithm is the hash algorithm used in the hash lock.
297#[repr(u8)]
298#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
299#[serde(rename_all = "snake_case")]
300pub enum HashAlgorithm {
301    /// The default hash algorithm, CkbHash
302    #[default]
303    CkbHash = 0,
304    /// The sha256 hash algorithm
305    Sha256 = 1,
306}
307
308/// Error for unknown hash algorithm
309#[derive(thiserror::Error, Debug)]
310#[error("Unknown Hash Algorithm: {0}")]
311pub struct UnknownHashAlgorithmError(pub u8);
312
313impl TryFrom<u8> for HashAlgorithm {
314    type Error = UnknownHashAlgorithmError;
315
316    fn try_from(value: u8) -> Result<Self, Self::Error> {
317        match value {
318            0 => Ok(HashAlgorithm::CkbHash),
319            1 => Ok(HashAlgorithm::Sha256),
320            _ => Err(UnknownHashAlgorithmError(value)),
321        }
322    }
323}
324
325impl HashAlgorithm {
326    pub fn supported_algorithms() -> Vec<HashAlgorithm> {
327        vec![HashAlgorithm::CkbHash, HashAlgorithm::Sha256]
328    }
329
330    pub fn hash<T: AsRef<[u8]>>(&self, s: T) -> [u8; 32] {
331        match self {
332            HashAlgorithm::CkbHash => blake2b_256(s),
333            HashAlgorithm::Sha256 => sha256(s),
334        }
335    }
336}
337
338/// SHA-256 hash helper function.
339pub fn sha256<T: AsRef<[u8]>>(s: T) -> [u8; 32] {
340    let mut hasher = Sha256::new();
341    hasher.update(s.as_ref());
342    hasher.finalize().into()
343}
344
345impl TryFrom<Byte> for HashAlgorithm {
346    type Error = UnknownHashAlgorithmError;
347
348    fn try_from(value: Byte) -> Result<Self, Self::Error> {
349        let value: u8 = value.into();
350        value.try_into()
351    }
352}
353
354/// A wrapper around `ckb_types::packed::Script` with hex serialization.
355#[serde_as]
356#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
357pub struct CkbScript(#[serde_as(as = "EntityHex")] pub PackedScript);
358
359/// Recoverable signature
360#[derive(Clone, Debug, Eq, PartialEq)]
361pub struct InvoiceSignature(pub RecoverableSignature);
362
363impl PartialOrd for InvoiceSignature {
364    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
365        Some(self.cmp(other))
366    }
367}
368
369impl Ord for InvoiceSignature {
370    fn cmp(&self, other: &Self) -> Ordering {
371        self.0
372            .serialize_compact()
373            .1
374            .cmp(&other.0.serialize_compact().1)
375    }
376}
377
378impl Serialize for InvoiceSignature {
379    fn serialize<S>(
380        &self,
381        serializer: S,
382    ) -> Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>
383    where
384        S: serde::Serializer,
385    {
386        let base32: Vec<u8> = self.to_base32().iter().map(|x| x.to_u8()).collect();
387        let hex_str = hex::encode(base32);
388        hex_str.serialize(serializer)
389    }
390}
391
392impl<'de> Deserialize<'de> for InvoiceSignature {
393    fn deserialize<D>(deserializer: D) -> Result<Self, <D as serde::Deserializer<'de>>::Error>
394    where
395        D: serde::Deserializer<'de>,
396    {
397        let signature_hex: String = String::deserialize(deserializer)?;
398        let signature_bytes = hex::decode(signature_hex).map_err(serde::de::Error::custom)?;
399        let base32_values = signature_bytes
400            .iter()
401            .map(|x| u5::try_from_u8(*x))
402            .collect::<Result<Vec<u5>, _>>()
403            .map_err(serde::de::Error::custom)?;
404        InvoiceSignature::from_base32(&base32_values).map_err(serde::de::Error::custom)
405    }
406}
407
408struct BytesToBase32<'a, W: WriteBase32 + 'a> {
409    writer: &'a mut W,
410    buffer: u8,
411    buffer_bits: u8,
412}
413
414impl<'a, W: WriteBase32> BytesToBase32<'a, W> {
415    fn new(writer: &'a mut W) -> Self {
416        BytesToBase32 {
417            writer,
418            buffer: 0,
419            buffer_bits: 0,
420        }
421    }
422
423    fn append(&mut self, byte: u8) -> Result<(), <W as WriteBase32>::Err> {
424        let mut bits_remaining = 8;
425        while bits_remaining > 0 {
426            let bits_to_take = std::cmp::min(5 - self.buffer_bits, bits_remaining);
427            self.buffer <<= bits_to_take;
428            self.buffer |= (byte >> (bits_remaining - bits_to_take)) & ((1 << bits_to_take) - 1);
429            self.buffer_bits += bits_to_take;
430            bits_remaining -= bits_to_take;
431
432            if self.buffer_bits == 5 {
433                self.writer
434                    .write_u5(u5::try_from_u8(self.buffer).expect("buffer is 5 bits"))?;
435                self.buffer = 0;
436                self.buffer_bits = 0;
437            }
438        }
439        Ok(())
440    }
441
442    fn finalize(mut self) -> Result<(), <W as WriteBase32>::Err> {
443        if self.buffer_bits > 0 {
444            self.buffer <<= 5 - self.buffer_bits;
445            self.writer
446                .write_u5(u5::try_from_u8(self.buffer).expect("buffer is at most 5 bits"))?;
447        }
448        Ok(())
449    }
450}
451
452impl ToBase32 for InvoiceSignature {
453    fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
454        let mut converter = BytesToBase32::new(writer);
455        let (recovery_id, signature) = self.0.serialize_compact();
456        for v in signature
457            .iter()
458            .chain(std::iter::once(&(i32::from(recovery_id) as u8)))
459        {
460            converter.append(*v)?;
461        }
462        converter.finalize()
463    }
464}
465
466impl FromBase32 for InvoiceSignature {
467    type Err = anyhow::Error;
468
469    fn from_base32(field_data: &[u5]) -> Result<InvoiceSignature, Self::Err> {
470        if field_data.len() < 104 {
471            return Err(anyhow::anyhow!(
472                "InvoiceSignature TryFrom<[u5]> failed: unexpected length {}",
473                field_data.len()
474            ));
475        }
476
477        let raw_bytes = Vec::<u8>::from_base32(field_data)?;
478        if raw_bytes.len() != 65 {
479            return Err(anyhow::anyhow!(
480                "InvoiceSignature TryFrom<[u5]> failed: unexpected byte length {}",
481                raw_bytes.len()
482            ));
483        }
484        let recovery_id = RecoveryId::try_from(raw_bytes[64] as i32)?;
485        let signature = RecoverableSignature::from_compact(&raw_bytes[0..64], recovery_id)?;
486        Ok(InvoiceSignature(signature))
487    }
488}
489
490impl InvoiceSignature {
491    /// Parse an `InvoiceSignature` from base32-encoded data, returning `InvoiceError` on failure.
492    pub fn from_base32_checked(signature: &[u5]) -> Result<Self, InvoiceError> {
493        if signature.len() != SIGNATURE_U5_SIZE {
494            return Err(InvoiceError::InvalidSliceLength(
495                "InvoiceSignature::from_base32_checked()".into(),
496            ));
497        }
498        let recoverable_signature_bytes =
499            Vec::<u8>::from_base32(signature).expect("bytes from base32");
500        let sig = &recoverable_signature_bytes[0..64];
501        let recovery_id = RecoveryId::try_from(recoverable_signature_bytes[64] as i32)
502            .expect("Recovery ID from i32");
503
504        Ok(InvoiceSignature(
505            RecoverableSignature::from_compact(sig, recovery_id).expect("signature from compact"),
506        ))
507    }
508}
509
510use crate::protocol::FeatureVector;
511use crate::serde_utils::{duration_hex, U128Hex, U64Hex};
512use crate::Hash256;
513use secp256k1::PublicKey;
514use std::time::Duration;
515
516/// The attributes of the invoice.
517#[serde_as]
518#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
519#[serde(rename_all = "snake_case")]
520pub enum Attribute {
521    /// This attribute is deprecated since v0.6.0. The final TLC timeout, in milliseconds.
522    #[serde(with = "U64Hex")]
523    FinalHtlcTimeout(u64),
524    /// The final TLC minimum expiry delta, in milliseconds. Default is 160 minutes.
525    #[serde(with = "U64Hex")]
526    FinalHtlcMinimumExpiryDelta(u64),
527    /// The expiry time of the invoice, in seconds.
528    #[serde(with = "duration_hex")]
529    ExpiryTime(Duration),
530    /// The description of the invoice.
531    Description(String),
532    /// The fallback address of the invoice.
533    FallbackAddr(String),
534    /// The UDT type script of the invoice.
535    UdtScript(CkbScript),
536    /// The payee public key of the invoice.
537    PayeePublicKey(PublicKey),
538    /// The hash algorithm of the invoice.
539    HashAlgorithm(HashAlgorithm),
540    /// The feature flags of the invoice.
541    Feature(FeatureVector),
542    /// The payment secret of the invoice.
543    PaymentSecret(Hash256),
544}
545
546/// The metadata of the invoice.
547#[serde_as]
548#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
549pub struct InvoiceData {
550    /// The timestamp of the invoice.
551    #[serde_as(as = "U128Hex")]
552    pub timestamp: u128,
553    /// The payment hash of the invoice.
554    pub payment_hash: Hash256,
555    /// The attributes of the invoice, e.g. description, expiry time, etc.
556    pub attrs: Vec<Attribute>,
557}
558
559/// Represents a syntactically and semantically correct Fiber invoice.
560///
561/// There are three ways to construct a `CkbInvoice`:
562///  1. using `CkbInvoiceBuilder`
563///  2. using `str::parse::<CkbInvoice>(&str)` (see `CkbInvoice::from_str`)
564#[serde_as]
565#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
566pub struct CkbInvoice {
567    /// The currency of the invoice.
568    pub currency: Currency,
569    /// The amount of the invoice.
570    #[serde_as(as = "Option<U128Hex>")]
571    pub amount: Option<u128>,
572    /// The signature of the invoice.
573    pub signature: Option<InvoiceSignature>,
574    /// The invoice data, including the payment hash, timestamp and other attributes.
575    pub data: InvoiceData,
576}
577
578impl CkbInvoice {
579    fn hrp_part(&self) -> String {
580        format!(
581            "{}{}",
582            self.currency,
583            self.amount
584                .map_or_else(|| "".to_string(), |x| x.to_string()),
585        )
586    }
587
588    // Use the lossless compression algorithm to compress the invoice data.
589    // To make sure the final encoded invoice address is shorter
590    fn data_part(&self) -> Vec<u5> {
591        let invoice_data = gen_invoice::RawInvoiceData::from(self.data.clone());
592        let compressed = ar_encompress(invoice_data.as_slice()).expect("compress invoice data");
593        let mut base32 = Vec::with_capacity(compressed.len());
594        compressed
595            .write_base32(&mut base32)
596            .expect("encode in base32");
597        base32
598    }
599
600    /// Check that the invoice is signed correctly and that key recovery works.
601    pub fn check_signature(&self) -> Result<(), InvoiceError> {
602        if self.signature.is_none() {
603            return Ok(());
604        }
605        match self.recover_payee_pub_key() {
606            Err(secp256k1::Error::InvalidRecoveryId) => {
607                return Err(InvoiceError::InvalidRecoveryId);
608            }
609            Err(secp256k1::Error::InvalidSignature) => return Err(InvoiceError::InvalidSignature),
610            Err(e) => panic!("no other error may occur, got {:?}", e),
611            Ok(_) => {}
612        }
613
614        if !self.validate_signature() {
615            return Err(InvoiceError::InvalidSignature);
616        }
617
618        Ok(())
619    }
620
621    fn validate_signature(&self) -> bool {
622        if self.signature.is_none() {
623            return true;
624        }
625        let signature = self.signature.as_ref().expect("expect signature");
626        let included_pub_key = self.payee_pub_key();
627
628        let mut recovered_pub_key = Option::None;
629        if included_pub_key.is_none() {
630            let recovered = match self.recover_payee_pub_key() {
631                Ok(pk) => pk,
632                Err(_) => return false,
633            };
634            recovered_pub_key = Some(recovered);
635        }
636
637        let pub_key = included_pub_key
638            .or(recovered_pub_key.as_ref())
639            .expect("One is always present");
640
641        let hash = secp256k1::Message::from_digest_slice(&self.hash()[..])
642            .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
643
644        let verification_result =
645            secp256k1::SECP256K1.verify_ecdsa(&hash, &signature.0.to_standard(), pub_key);
646        match verification_result {
647            Ok(()) => true,
648            Err(_) => false,
649        }
650    }
651
652    fn hash(&self) -> [u8; 32] {
653        let hrp = self.hrp_part();
654        let data = self.data_part();
655        let preimage = construct_invoice_preimage(hrp.as_bytes(), &data);
656        sha256(&preimage)
657    }
658
659    /// Recovers the public key used for signing the invoice from the recoverable signature.
660    pub fn recover_payee_pub_key(&self) -> Result<PublicKey, secp256k1::Error> {
661        let hash = secp256k1::Message::from_digest_slice(&self.hash()[..])
662            .expect("Hash is 32 bytes long, same as MESSAGE_SIZE");
663
664        secp256k1::SECP256K1.recover_ecdsa(
665            &hash,
666            &self
667                .signature
668                .as_ref()
669                .expect("signature must be present")
670                .0,
671        )
672    }
673
674    /// Returns the payee public key if set in the invoice attributes.
675    pub fn payee_pub_key(&self) -> Option<&PublicKey> {
676        self.data
677            .attrs
678            .iter()
679            .filter_map(|attr| match attr {
680                Attribute::PayeePublicKey(val) => Some(val),
681                _ => None,
682            })
683            .next()
684    }
685
686    /// Returns whether the invoice has a signature.
687    pub fn is_signed(&self) -> bool {
688        self.signature.is_some()
689    }
690
691    /// Returns the payment hash of the invoice.
692    pub fn payment_hash(&self) -> &Hash256 {
693        &self.data.payment_hash
694    }
695
696    /// Returns the amount of the invoice.
697    pub fn amount(&self) -> Option<u128> {
698        self.amount
699    }
700
701    /// Returns the UDT type script if set in the invoice attributes.
702    pub fn udt_type_script(&self) -> Option<&PackedScript> {
703        self.data
704            .attrs
705            .iter()
706            .filter_map(|attr| match attr {
707                Attribute::UdtScript(script) => Some(&script.0),
708                _ => None,
709            })
710            .next()
711    }
712
713    /// Returns the expiry time if set in the invoice attributes.
714    pub fn expiry_time(&self) -> Option<&Duration> {
715        self.data
716            .attrs
717            .iter()
718            .filter_map(|attr| match attr {
719                Attribute::ExpiryTime(val) => Some(val),
720                _ => None,
721            })
722            .next()
723    }
724
725    /// Returns the description if set in the invoice attributes.
726    pub fn description(&self) -> Option<&String> {
727        self.data
728            .attrs
729            .iter()
730            .filter_map(|attr| match attr {
731                Attribute::Description(val) => Some(val),
732                _ => None,
733            })
734            .next()
735    }
736
737    /// Returns the final TLC minimum expiry delta if set in the invoice attributes.
738    pub fn final_tlc_minimum_expiry_delta(&self) -> Option<&u64> {
739        self.data
740            .attrs
741            .iter()
742            .filter_map(|attr| match attr {
743                Attribute::FinalHtlcMinimumExpiryDelta(val) => Some(val),
744                _ => None,
745            })
746            .next()
747    }
748
749    /// Returns the fallback address if set in the invoice attributes.
750    pub fn fallback_address(&self) -> Option<&String> {
751        self.data
752            .attrs
753            .iter()
754            .filter_map(|attr| match attr {
755                Attribute::FallbackAddr(val) => Some(val),
756                _ => None,
757            })
758            .next()
759    }
760
761    /// Returns the hash algorithm if set in the invoice attributes.
762    pub fn hash_algorithm(&self) -> Option<&HashAlgorithm> {
763        self.data
764            .attrs
765            .iter()
766            .filter_map(|attr| match attr {
767                Attribute::HashAlgorithm(val) => Some(val),
768                _ => None,
769            })
770            .next()
771    }
772
773    /// Returns the payment secret if set in the invoice attributes.
774    pub fn payment_secret(&self) -> Option<&Hash256> {
775        self.data
776            .attrs
777            .iter()
778            .filter_map(|attr| match attr {
779                Attribute::PaymentSecret(val) => Some(val),
780                _ => None,
781            })
782            .next()
783    }
784
785    /// Returns whether the invoice allows MPP (multi-part payments).
786    pub fn allow_mpp(&self) -> bool {
787        self.data
788            .attrs
789            .iter()
790            .any(|attr| matches!(attr, Attribute::Feature(feature) if feature.supports_basic_mpp()))
791    }
792
793    /// Returns whether the invoice allows trampoline routing.
794    pub fn allow_trampoline_routing(&self) -> bool {
795        self.data
796            .attrs
797            .iter()
798            .any(|attr| matches!(attr, Attribute::Feature(feature) if feature.supports_trampoline_routing()))
799    }
800
801    /// Returns whether the invoice has expired based on the current time.
802    pub fn is_expired(&self) -> bool {
803        self.expiry_time().is_some_and(|expiry| {
804            self.data
805                .timestamp
806                .checked_add(expiry.as_millis())
807                .is_some_and(|expiry_time| {
808                    let now = crate::crate_time::UNIX_EPOCH
809                        .elapsed()
810                        .expect("Duration since unix epoch")
811                        .as_millis();
812                    expiry_time < now
813                })
814        })
815    }
816
817    /// Returns whether the TLC expiry is too soon for the given invoice.
818    pub fn is_tlc_expire_too_soon(&self, tlc_expiry: u64) -> bool {
819        let now = crate::crate_time::UNIX_EPOCH
820            .elapsed()
821            .expect("Duration since unix epoch")
822            .as_millis();
823        let required_expiry = now
824            + (self
825                .final_tlc_minimum_expiry_delta()
826                .cloned()
827                .unwrap_or_default() as u128);
828        (tlc_expiry as u128) < required_expiry
829    }
830
831    /// Updates the invoice signature using the provided signing function.
832    pub fn update_signature<F>(&mut self, sign_function: F) -> Result<(), InvoiceError>
833    where
834        F: FnOnce(&secp256k1::Message) -> RecoverableSignature,
835    {
836        let hash = self.hash();
837        let message =
838            secp256k1::Message::from_digest_slice(&hash).expect("message from digest slice");
839        let signature = sign_function(&message);
840        self.signature = Some(InvoiceSignature(signature));
841        self.check_signature()?;
842        Ok(())
843    }
844}
845
846impl Display for CkbInvoice {
847    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
848        let hrp = self.hrp_part();
849        let mut data = self.data_part();
850        data.insert(
851            0,
852            u5::try_from_u8(if self.signature.is_some() { 1 } else { 0 }).expect("u5 from u8"),
853        );
854        if let Some(signature) = &self.signature {
855            data.extend_from_slice(&signature.to_base32());
856        }
857        write!(
858            f,
859            "{}",
860            encode(&hrp, data, Variant::Bech32m).expect("encode invoice using Bech32m")
861        )
862    }
863}
864
865impl FromStr for CkbInvoice {
866    type Err = InvoiceError;
867
868    fn from_str(s: &str) -> Result<Self, Self::Err> {
869        let (hrp, data, var) = bech32::decode(s).map_err(InvoiceError::Bech32Error)?;
870
871        if var == bech32::Variant::Bech32 {
872            return Err(InvoiceError::Bech32Error(bech32::Error::InvalidChecksum));
873        }
874
875        if data.len() < SIGNATURE_U5_SIZE {
876            return Err(InvoiceError::TooShortDataPart);
877        }
878        let (currency, amount) = parse_hrp(&hrp)?;
879        let is_signed = data[0].to_u8() == 1;
880        let data_end = if is_signed {
881            data.len() - SIGNATURE_U5_SIZE
882        } else {
883            data.len()
884        };
885        let data_part =
886            Vec::<u8>::from_base32(&data[1..data_end]).map_err(InvoiceError::Bech32Error)?;
887        let data_part = ar_decompress(&data_part).expect("decompress invoice data");
888        let invoice_data = gen_invoice::RawInvoiceData::from_slice(&data_part)
889            .map_err(|err| InvoiceError::MoleculeError(VerificationError(err)))?;
890        let signature = if is_signed {
891            Some(InvoiceSignature::from_base32(
892                &data[data.len() - SIGNATURE_U5_SIZE..],
893            )?)
894        } else {
895            None
896        };
897
898        let invoice = CkbInvoice {
899            currency,
900            amount,
901            signature,
902            data: invoice_data.try_into().expect("pack invoice data"),
903        };
904        invoice.check_signature()?;
905        Ok(invoice)
906    }
907}
908
909/// Converts a `[u8]` slice to `[Byte; 32]`.
910fn u8_slice_to_bytes(slice: &[u8]) -> Result<[Byte; 32], &'static str> {
911    let vec: Vec<Byte> = slice.iter().map(|&x| Byte::new(x)).collect();
912    let boxed_slice = vec.into_boxed_slice();
913    let boxed_array: Box<[Byte; 32]> = match boxed_slice.try_into() {
914        Ok(ba) => ba,
915        Err(_) => return Err("Slice length doesn't match array length"),
916    };
917    Ok(*boxed_array)
918}
919
920/// Converts molecule bytes to `[u8; 32]`.
921fn bytes_to_u8_array(array: &molecule::bytes::Bytes) -> [u8; 32] {
922    let mut res = [0u8; 32];
923    res.copy_from_slice(array);
924    res
925}
926
927impl From<InvoiceData> for gen_invoice::RawInvoiceData {
928    fn from(data: InvoiceData) -> Self {
929        RawInvoiceDataBuilder::default()
930            .timestamp(data.timestamp.pack())
931            .payment_hash(
932                PaymentHash::new_builder()
933                    .set(
934                        u8_slice_to_bytes(data.payment_hash.as_ref()).expect("bytes from u8 slice"),
935                    )
936                    .build(),
937            )
938            .attrs(
939                InvoiceAttrsVec::new_builder()
940                    .set(
941                        data.attrs
942                            .iter()
943                            .map(|a| a.to_owned().into())
944                            .collect::<Vec<InvoiceAttr>>(),
945                    )
946                    .build(),
947            )
948            .build()
949    }
950}
951
952impl TryFrom<gen_invoice::RawInvoiceData> for InvoiceData {
953    type Error = VerificationError;
954
955    fn try_from(data: gen_invoice::RawInvoiceData) -> Result<Self, Self::Error> {
956        Ok(InvoiceData {
957            timestamp: data.timestamp().unpack(),
958            payment_hash: bytes_to_u8_array(&data.payment_hash().as_bytes()).into(),
959            attrs: data
960                .attrs()
961                .into_iter()
962                .map(|a| a.into())
963                .collect::<Vec<Attribute>>(),
964        })
965    }
966}
967
968impl From<Attribute> for InvoiceAttr {
969    fn from(attr: Attribute) -> Self {
970        let a = match attr {
971            Attribute::ExpiryTime(x) => {
972                let seconds = x.as_secs();
973                let value = ExpiryTime::new_builder().value(seconds.pack()).build();
974                InvoiceAttrUnion::ExpiryTime(value)
975            }
976            Attribute::Description(value) => InvoiceAttrUnion::Description(
977                Description::new_builder().value(value.pack()).build(),
978            ),
979            Attribute::FinalHtlcTimeout(value) => InvoiceAttrUnion::FinalHtlcTimeout(
980                FinalHtlcTimeout::new_builder().value(value.pack()).build(),
981            ),
982            Attribute::FinalHtlcMinimumExpiryDelta(value) => {
983                InvoiceAttrUnion::FinalHtlcMinimumExpiryDelta(
984                    FinalHtlcMinimumExpiryDelta::new_builder()
985                        .value(value.pack())
986                        .build(),
987                )
988            }
989            Attribute::FallbackAddr(value) => InvoiceAttrUnion::FallbackAddr(
990                FallbackAddr::new_builder().value(value.pack()).build(),
991            ),
992            Attribute::Feature(value) => InvoiceAttrUnion::Feature(
993                Feature::new_builder().value(value.bytes().pack()).build(),
994            ),
995            Attribute::UdtScript(script) => {
996                InvoiceAttrUnion::UdtScript(UdtScript::new_builder().value(script.0).build())
997            }
998            Attribute::PayeePublicKey(pubkey) => InvoiceAttrUnion::PayeePublicKey(
999                PayeePublicKey::new_builder()
1000                    .value(pubkey.serialize().pack())
1001                    .build(),
1002            ),
1003            Attribute::HashAlgorithm(hash_algorithm) => InvoiceAttrUnion::HashAlgorithm(
1004                gen_invoice::HashAlgorithm::new_builder()
1005                    .value(Byte::new(hash_algorithm as u8))
1006                    .build(),
1007            ),
1008            Attribute::PaymentSecret(payment_secret) => InvoiceAttrUnion::PaymentSecret(
1009                PaymentSecret::new_builder()
1010                    .value(payment_secret.into())
1011                    .build(),
1012            ),
1013        };
1014        InvoiceAttr::new_builder().set(a).build()
1015    }
1016}
1017
1018impl From<InvoiceAttr> for Attribute {
1019    fn from(attr: InvoiceAttr) -> Self {
1020        match attr.to_enum() {
1021            InvoiceAttrUnion::Description(x) => {
1022                let value: Vec<u8> = x.value().unpack();
1023                Attribute::Description(
1024                    String::from_utf8(value).expect("decode utf8 string from bytes"),
1025                )
1026            }
1027            InvoiceAttrUnion::ExpiryTime(x) => {
1028                let seconds: u64 = x.value().unpack();
1029                Attribute::ExpiryTime(Duration::from_secs(seconds))
1030            }
1031
1032            InvoiceAttrUnion::FinalHtlcTimeout(x) => {
1033                // This attribute is deprecated since v0.6.0, but we still keep it in molecule for consistency
1034                Attribute::FinalHtlcTimeout(x.value().unpack())
1035            }
1036            InvoiceAttrUnion::FinalHtlcMinimumExpiryDelta(x) => {
1037                Attribute::FinalHtlcMinimumExpiryDelta(x.value().unpack())
1038            }
1039            InvoiceAttrUnion::FallbackAddr(x) => {
1040                let value: Vec<u8> = x.value().unpack();
1041                Attribute::FallbackAddr(
1042                    String::from_utf8(value).expect("decode utf8 string from bytes"),
1043                )
1044            }
1045            InvoiceAttrUnion::Feature(x) => {
1046                Attribute::Feature(FeatureVector::from(x.value().unpack()))
1047            }
1048            InvoiceAttrUnion::UdtScript(x) => Attribute::UdtScript(CkbScript(x.value())),
1049            InvoiceAttrUnion::PayeePublicKey(x) => {
1050                let value: Vec<u8> = x.value().unpack();
1051                Attribute::PayeePublicKey(
1052                    PublicKey::from_slice(&value).expect("Public key from slice"),
1053                )
1054            }
1055            InvoiceAttrUnion::HashAlgorithm(x) => {
1056                let value = x.value();
1057                // Consider unknown algorithm as the default one.
1058                let hash_algorithm = value.try_into().unwrap_or_default();
1059                Attribute::HashAlgorithm(hash_algorithm)
1060            }
1061            InvoiceAttrUnion::PaymentSecret(x) => Attribute::PaymentSecret(x.value().into()),
1062        }
1063    }
1064}
1065
1066impl From<anyhow::Error> for InvoiceError {
1067    fn from(_err: anyhow::Error) -> Self {
1068        InvoiceError::InvalidSignature
1069    }
1070}
1071
1072impl TryFrom<gen_invoice::RawCkbInvoice> for CkbInvoice {
1073    type Error = InvoiceError;
1074
1075    fn try_from(invoice: gen_invoice::RawCkbInvoice) -> Result<Self, Self::Error> {
1076        Ok(CkbInvoice {
1077            currency: (u8::from(invoice.currency()))
1078                .try_into()
1079                .map_err(|e: UnknownCurrencyError| InvoiceError::UnknownCurrency(e.0))?,
1080            amount: invoice.amount().to_opt().map(|x| x.unpack()),
1081            signature: invoice.signature().to_opt().map(|x| {
1082                InvoiceSignature::from_base32_checked(
1083                    &x.as_bytes()
1084                        .into_iter()
1085                        .map(|x| u5::try_from_u8(x).expect("u5 from u8"))
1086                        .collect::<Vec<u5>>(),
1087                )
1088                .expect("signature must be present")
1089            }),
1090            data: InvoiceData::try_from(invoice.data()).map_err(InvoiceError::MoleculeError)?,
1091        })
1092    }
1093}
1094
1095impl From<CkbInvoice> for gen_invoice::RawCkbInvoice {
1096    fn from(invoice: CkbInvoice) -> Self {
1097        gen_invoice::RawCkbInvoiceBuilder::default()
1098            .currency((invoice.currency as u8).into())
1099            .amount(
1100                gen_invoice::AmountOpt::new_builder()
1101                    .set(invoice.amount.map(|x| x.pack()))
1102                    .build(),
1103            )
1104            .signature(
1105                gen_invoice::SignatureOpt::new_builder()
1106                    .set({
1107                        invoice.signature.map(|x| {
1108                            let bytes: [Byte; SIGNATURE_U5_SIZE] = x
1109                                .to_base32()
1110                                .iter()
1111                                .map(|x| Byte::new(x.to_u8()))
1112                                .collect::<Vec<_>>()
1113                                .as_slice()
1114                                .try_into()
1115                                .expect("[Byte; 104] from [Byte] slice");
1116                            gen_invoice::Signature::new_builder().set(bytes).build()
1117                        })
1118                    })
1119                    .build(),
1120            )
1121            .data(invoice.data.into())
1122            .build()
1123    }
1124}
1125
1126#[cfg(test)]
1127#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1128#[cfg_attr(not(target_arch = "wasm32"), test)]
1129fn test_parse_hrp() {
1130    use super::InvoiceError;
1131
1132    let res = parse_hrp("fibb1280");
1133    assert_eq!(res, Ok((Currency::Fibb, Some(1280))));
1134
1135    let res = parse_hrp("fibb");
1136    assert_eq!(res, Ok((Currency::Fibb, None)));
1137
1138    let res = parse_hrp("fibt1023");
1139    assert_eq!(res, Ok((Currency::Fibt, Some(1023))));
1140
1141    let res = parse_hrp("fibt10");
1142    assert_eq!(res, Ok((Currency::Fibt, Some(10))));
1143
1144    let res = parse_hrp("fibt");
1145    assert_eq!(res, Ok((Currency::Fibt, None)));
1146
1147    let res = parse_hrp("xnfibb");
1148    assert_eq!(res, Err(InvoiceError::MalformedHRP("xnfibb".to_string())));
1149
1150    let res = parse_hrp("lxfibt");
1151    assert_eq!(res, Err(InvoiceError::MalformedHRP("lxfibt".to_string())));
1152
1153    let res = parse_hrp("fibt");
1154    assert_eq!(res, Ok((Currency::Fibt, None)));
1155
1156    let res = parse_hrp("fixt");
1157    assert_eq!(res, Err(InvoiceError::MalformedHRP("fixt".to_string())));
1158
1159    let res = parse_hrp("fibtt");
1160    assert_eq!(
1161        res,
1162        Err(InvoiceError::MalformedHRP(
1163            "fibtt, unexpected ending `t`".to_string()
1164        ))
1165    );
1166
1167    let res = parse_hrp("fibt1x24");
1168    assert_eq!(
1169        res,
1170        Err(InvoiceError::MalformedHRP(
1171            "fibt1x24, unexpected ending `x24`".to_string()
1172        ))
1173    );
1174
1175    let res = parse_hrp("fibt000");
1176    assert_eq!(res, Ok((Currency::Fibt, Some(0))));
1177
1178    let res = parse_hrp("fibt1024444444444444444444444444444444444444444444444444444444444444");
1179    assert!(matches!(res, Err(InvoiceError::ParseAmountError(_))));
1180
1181    let res = parse_hrp("fibt0x");
1182    assert!(matches!(res, Err(InvoiceError::MalformedHRP(_))));
1183
1184    let res = parse_hrp("");
1185    assert!(matches!(res, Err(InvoiceError::MalformedHRP(_))));
1186}
1187
1188#[cfg(test)]
1189#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1190#[cfg_attr(not(target_arch = "wasm32"), test)]
1191fn test_compress() {
1192    let input = "hrp1gyqsqqq5qqqqq9gqqqqp6qqqqq0qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq2qqqqqqqqqqqyvqsqqqsqqqqqvqqqqq8";
1193    let bytes = input.as_bytes();
1194    let compressed = ar_encompress(input.as_bytes()).unwrap();
1195
1196    let decompressed = ar_decompress(&compressed).unwrap();
1197    let decompressed_str = std::str::from_utf8(&decompressed).unwrap();
1198    assert_eq!(input, decompressed_str);
1199    assert!(compressed.len() < bytes.len());
1200}