From c6775d7ecaa1f9dda0fabac6ffc2bf0bc010d1a7 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 15 Nov 2024 19:24:10 +0800 Subject: [PATCH] add length limits for invoice description --- src/invoice/errors.rs | 2 ++ src/invoice/invoice_impl.rs | 13 +++++++++++-- src/invoice/tests/invoice_impl.rs | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/invoice/errors.rs b/src/invoice/errors.rs index 1c16bdf1..b8aacacd 100644 --- a/src/invoice/errors.rs +++ b/src/invoice/errors.rs @@ -55,6 +55,8 @@ pub enum InvoiceError { HexDecodeError(#[from] hex::FromHexError), #[error("Duplicated inovice found: {0}")] DuplicatedInvoice(String), + #[error("Description with length of {0} is too long, max length is 639")] + DescriptionTooLong(usize), #[error("Invoice not found")] InvoiceNotFound, } diff --git a/src/invoice/invoice_impl.rs b/src/invoice/invoice_impl.rs index 91ed128d..5075259f 100644 --- a/src/invoice/invoice_impl.rs +++ b/src/invoice/invoice_impl.rs @@ -26,6 +26,7 @@ use serde_with::serde_as; use std::{cmp::Ordering, str::FromStr}; pub(crate) const SIGNATURE_U5_SIZE: usize = 104; +pub(crate) const MAX_DESCRIPTION_LENGTH: usize = 639; /// The currency of the invoice, can also used to represent the CKB network chain. #[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] @@ -652,7 +653,7 @@ impl InvoiceBuilder { rand_sha256_hash() }; - self.check_duplicated_attrs()?; + self.check_attrs_valid()?; let timestamp = std::time::UNIX_EPOCH .elapsed() .expect("Duration since unix epoch") @@ -678,7 +679,7 @@ impl InvoiceBuilder { Ok(invoice) } - fn check_duplicated_attrs(&self) -> Result<(), InvoiceError> { + fn check_attrs_valid(&self) -> Result<(), InvoiceError> { // check is there any duplicate attribute key set for (i, attr) in self.attrs.iter().enumerate() { for other in self.attrs.iter().skip(i + 1) { @@ -687,6 +688,14 @@ impl InvoiceBuilder { } } } + + if let Some(len) = self.attrs.iter().find_map(|attr| match attr { + Attribute::Description(desc) if desc.len() > MAX_DESCRIPTION_LENGTH => Some(desc.len()), + _ => None, + }) { + return Err(InvoiceError::DescriptionTooLong(len)); + } + Ok(()) } } diff --git a/src/invoice/tests/invoice_impl.rs b/src/invoice/tests/invoice_impl.rs index 3f5db057..55c85026 100644 --- a/src/invoice/tests/invoice_impl.rs +++ b/src/invoice/tests/invoice_impl.rs @@ -316,6 +316,26 @@ fn test_invoice_builder_duplicated_attr() { ); } +#[test] +fn test_invoice_check_description_length() { + let gen_payment_hash = rand_sha256_hash(); + let private_key = gen_rand_private_key(); + const MAX_DESCRIPTION_LEN: usize = 639; + let invoice = InvoiceBuilder::new(Currency::Fibb) + .amount(Some(1280)) + .payment_hash(gen_payment_hash) + .description("a".repeat(MAX_DESCRIPTION_LEN + 1)) + .add_attr(Attribute::FinalHtlcTimeout(5)) + .build_with_sign(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)); + + assert!(invoice.is_err()); + let message = invoice.err().unwrap().to_string(); + assert_eq!( + message, + "Description with length of 640 is too long, max length is 639" + ); +} + #[test] fn test_invoice_builder_missing() { let private_key = gen_rand_private_key();