Qualify the BOLT 11 semantic error type

A previous commit qualified the BOLT 12 semantic error type. Qualify the
BOLT 11 semantic error type for consistency.
This commit is contained in:
Jeffrey Czyz 2023-07-13 20:56:30 -05:00
parent 6fb34d30b3
commit 62ca48f979
No known key found for this signature in database
GPG key ID: 3A4E08275D5E96D2
3 changed files with 46 additions and 46 deletions

View file

@ -24,7 +24,7 @@ use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
use secp256k1::PublicKey;
use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, InvoiceSignature, PositiveTimestamp,
SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
Bolt11SemanticError, PrivateRoute, Bolt11ParseError, ParseOrSemanticError, Description, RawTaggedField, Currency, RawHrp, SiPrefix, RawBolt11Invoice,
constants, SignedRawBolt11Invoice, RawDataPart, InvoiceFeatures};
use self::hrp_sm::parse_hrp;
@ -715,8 +715,8 @@ impl From<Bolt11ParseError> for ParseOrSemanticError {
}
}
impl From<crate::SemanticError> for ParseOrSemanticError {
fn from(e: SemanticError) -> Self {
impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
fn from(e: Bolt11SemanticError) -> Self {
ParseOrSemanticError::SemanticError(e)
}
}

View file

@ -139,7 +139,7 @@ pub enum ParseOrSemanticError {
ParseError(Bolt11ParseError),
/// The invoice could be decoded but violates the BOLT11 standard
SemanticError(crate::SemanticError),
SemanticError(crate::Bolt11SemanticError),
}
/// The number of bits used to represent timestamps as defined in BOLT 11.
@ -1154,16 +1154,16 @@ impl Bolt11Invoice {
}
/// Check that all mandatory fields are present
fn check_field_counts(&self) -> Result<(), SemanticError> {
fn check_field_counts(&self) -> Result<(), Bolt11SemanticError> {
// "A writer MUST include exactly one p field […]."
let payment_hash_cnt = self.tagged_fields().filter(|&tf| match *tf {
TaggedField::PaymentHash(_) => true,
_ => false,
}).count();
if payment_hash_cnt < 1 {
return Err(SemanticError::NoPaymentHash);
return Err(Bolt11SemanticError::NoPaymentHash);
} else if payment_hash_cnt > 1 {
return Err(SemanticError::MultiplePaymentHashes);
return Err(Bolt11SemanticError::MultiplePaymentHashes);
}
// "A writer MUST include either exactly one d or exactly one h field."
@ -1172,9 +1172,9 @@ impl Bolt11Invoice {
_ => false,
}).count();
if description_cnt < 1 {
return Err(SemanticError::NoDescription);
return Err(Bolt11SemanticError::NoDescription);
} else if description_cnt > 1 {
return Err(SemanticError::MultipleDescriptions);
return Err(Bolt11SemanticError::MultipleDescriptions);
}
self.check_payment_secret()?;
@ -1183,33 +1183,33 @@ impl Bolt11Invoice {
}
/// Checks that there is exactly one payment secret field
fn check_payment_secret(&self) -> Result<(), SemanticError> {
fn check_payment_secret(&self) -> Result<(), Bolt11SemanticError> {
// "A writer MUST include exactly one `s` field."
let payment_secret_count = self.tagged_fields().filter(|&tf| match *tf {
TaggedField::PaymentSecret(_) => true,
_ => false,
}).count();
if payment_secret_count < 1 {
return Err(SemanticError::NoPaymentSecret);
return Err(Bolt11SemanticError::NoPaymentSecret);
} else if payment_secret_count > 1 {
return Err(SemanticError::MultiplePaymentSecrets);
return Err(Bolt11SemanticError::MultiplePaymentSecrets);
}
Ok(())
}
/// Check that amount is a whole number of millisatoshis
fn check_amount(&self) -> Result<(), SemanticError> {
fn check_amount(&self) -> Result<(), Bolt11SemanticError> {
if let Some(amount_pico_btc) = self.amount_pico_btc() {
if amount_pico_btc % 10 != 0 {
return Err(SemanticError::ImpreciseAmount);
return Err(Bolt11SemanticError::ImpreciseAmount);
}
}
Ok(())
}
/// Check that feature bits are set as required
fn check_feature_bits(&self) -> Result<(), SemanticError> {
fn check_feature_bits(&self) -> Result<(), Bolt11SemanticError> {
self.check_payment_secret()?;
// "A writer MUST set an s field if and only if the payment_secret feature is set."
@ -1220,12 +1220,12 @@ impl Bolt11Invoice {
_ => false,
});
match features {
None => Err(SemanticError::InvalidFeatures),
None => Err(Bolt11SemanticError::InvalidFeatures),
Some(TaggedField::Features(features)) => {
if features.requires_unknown_bits() {
Err(SemanticError::InvalidFeatures)
Err(Bolt11SemanticError::InvalidFeatures)
} else if !features.supports_payment_secret() {
Err(SemanticError::InvalidFeatures)
Err(Bolt11SemanticError::InvalidFeatures)
} else {
Ok(())
}
@ -1235,18 +1235,18 @@ impl Bolt11Invoice {
}
/// Check that the invoice is signed correctly and that key recovery works
pub fn check_signature(&self) -> Result<(), SemanticError> {
pub fn check_signature(&self) -> Result<(), Bolt11SemanticError> {
match self.signed_invoice.recover_payee_pub_key() {
Err(secp256k1::Error::InvalidRecoveryId) =>
return Err(SemanticError::InvalidRecoveryId),
return Err(Bolt11SemanticError::InvalidRecoveryId),
Err(secp256k1::Error::InvalidSignature) =>
return Err(SemanticError::InvalidSignature),
return Err(Bolt11SemanticError::InvalidSignature),
Err(e) => panic!("no other error may occur, got {:?}", e),
Ok(_) => {},
}
if !self.signed_invoice.check_signature() {
return Err(SemanticError::InvalidSignature);
return Err(Bolt11SemanticError::InvalidSignature);
}
Ok(())
@ -1272,7 +1272,7 @@ impl Bolt11Invoice {
///
/// assert!(Bolt11Invoice::from_signed(signed).is_ok());
/// ```
pub fn from_signed(signed_invoice: SignedRawBolt11Invoice) -> Result<Self, SemanticError> {
pub fn from_signed(signed_invoice: SignedRawBolt11Invoice) -> Result<Self, Bolt11SemanticError> {
let invoice = Bolt11Invoice {
signed_invoice,
};
@ -1654,7 +1654,7 @@ impl std::error::Error for CreationError { }
/// Errors that may occur when converting a [`RawBolt11Invoice`] to a [`Bolt11Invoice`]. They relate to
/// the requirements sections in BOLT #11
#[derive(Eq, PartialEq, Debug, Clone)]
pub enum SemanticError {
pub enum Bolt11SemanticError {
/// The invoice is missing the mandatory payment hash
NoPaymentHash,
@ -1687,25 +1687,25 @@ pub enum SemanticError {
ImpreciseAmount,
}
impl Display for SemanticError {
impl Display for Bolt11SemanticError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SemanticError::NoPaymentHash => f.write_str("The invoice is missing the mandatory payment hash"),
SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
SemanticError::NoPaymentSecret => f.write_str("The invoice is missing the mandatory payment secret"),
SemanticError::MultiplePaymentSecrets => f.write_str("The invoice contains multiple payment secrets"),
SemanticError::InvalidFeatures => f.write_str("The invoice's features are invalid"),
SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
SemanticError::ImpreciseAmount => f.write_str("The invoice's amount was not a whole number of millisatoshis"),
Bolt11SemanticError::NoPaymentHash => f.write_str("The invoice is missing the mandatory payment hash"),
Bolt11SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
Bolt11SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
Bolt11SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
Bolt11SemanticError::NoPaymentSecret => f.write_str("The invoice is missing the mandatory payment secret"),
Bolt11SemanticError::MultiplePaymentSecrets => f.write_str("The invoice contains multiple payment secrets"),
Bolt11SemanticError::InvalidFeatures => f.write_str("The invoice's features are invalid"),
Bolt11SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
Bolt11SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
Bolt11SemanticError::ImpreciseAmount => f.write_str("The invoice's amount was not a whole number of millisatoshis"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SemanticError { }
impl std::error::Error for Bolt11SemanticError { }
/// When signing using a fallible method either an user-supplied `SignError` or a [`CreationError`]
/// may occur.
@ -1867,7 +1867,7 @@ mod test {
use secp256k1::Secp256k1;
use secp256k1::SecretKey;
use crate::{Bolt11Invoice, RawBolt11Invoice, RawHrp, RawDataPart, Currency, Sha256, PositiveTimestamp,
SemanticError};
Bolt11SemanticError};
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
let payment_secret = lightning::ln::PaymentSecret([21; 32]);
@ -1898,7 +1898,7 @@ mod test {
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(Bolt11SemanticError::InvalidFeatures));
// Missing feature bits
let invoice = {
@ -1907,7 +1907,7 @@ mod test {
invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(Bolt11SemanticError::InvalidFeatures));
let mut payment_secret_features = InvoiceFeatures::empty();
payment_secret_features.set_payment_secret_required();
@ -1926,7 +1926,7 @@ mod test {
let invoice = invoice_template.clone();
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(Bolt11SemanticError::NoPaymentSecret));
// No payment secret or feature bits
let invoice = {
@ -1934,7 +1934,7 @@ mod test {
invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(Bolt11SemanticError::NoPaymentSecret));
// Missing payment secret
let invoice = {
@ -1942,7 +1942,7 @@ mod test {
invoice.data.tagged_fields.push(Features(payment_secret_features).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(Bolt11SemanticError::NoPaymentSecret));
// Multiple payment secrets
let invoice = {
@ -1951,7 +1951,7 @@ mod test {
invoice.data.tagged_fields.push(PaymentSecret(payment_secret).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)))
}.unwrap();
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(SemanticError::MultiplePaymentSecrets));
assert_eq!(Bolt11Invoice::from_signed(invoice), Err(Bolt11SemanticError::MultiplePaymentSecrets));
}
#[test]

View file

@ -421,7 +421,7 @@ fn test_bolt_invalid_invoices() {
// Tests the BOLT 11 invalid invoice test vectors
assert_eq!(Bolt11Invoice::from_str(
"lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqtqyx5vggfcsll4wu246hz02kp85x4katwsk9639we5n5yngc3yhqkm35jnjw4len8vrnqnf5ejh0mzj9n3vz2px97evektfm2l6wqccp3y7372"
), Err(ParseOrSemanticError::SemanticError(SemanticError::InvalidFeatures)));
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidFeatures)));
assert_eq!(Bolt11Invoice::from_str(
"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt"
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum))));
@ -433,7 +433,7 @@ fn test_bolt_invalid_invoices() {
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MixedCase))));
assert_eq!(Bolt11Invoice::from_str(
"lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2"
), Err(ParseOrSemanticError::SemanticError(SemanticError::InvalidSignature)));
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidSignature)));
assert_eq!(Bolt11Invoice::from_str(
"lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh"
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::TooShortDataPart)));
@ -442,5 +442,5 @@ fn test_bolt_invalid_invoices() {
), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::UnknownSiPrefix)));
assert_eq!(Bolt11Invoice::from_str(
"lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x"
), Err(ParseOrSemanticError::SemanticError(SemanticError::ImpreciseAmount)));
), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::ImpreciseAmount)));
}