]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Upgrade bech32 dependency, bech32 serialization improvements
authoroptout <13562139+optout21@users.noreply.github.com>
Wed, 2 Oct 2024 19:21:07 +0000 (21:21 +0200)
committeroptout <13562139+optout21@users.noreply.github.com>
Wed, 2 Oct 2024 19:21:07 +0000 (21:21 +0200)
13 files changed:
fuzz/Cargo.toml
fuzz/src/bolt11_deser.rs
lightning-invoice/Cargo.toml
lightning-invoice/src/de.rs
lightning-invoice/src/lib.rs
lightning-invoice/src/ser.rs
lightning-invoice/src/test_ser_de.rs
lightning-invoice/tests/ser_de.rs
lightning-types/Cargo.toml
lightning-types/src/features.rs
lightning-types/src/payment.rs
lightning/Cargo.toml
lightning/src/offers/parse.rs

index 02b808f238339b0c5480b86748e3077670dfb48e..a0f748d2349e5c0a8abc9d91e801ed3b1e90500f 100644 (file)
@@ -21,7 +21,7 @@ stdin_fuzz = []
 lightning = { path = "../lightning", features = ["regex", "_test_utils"] }
 lightning-invoice = { path = "../lightning-invoice" }
 lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync" }
-bech32 = "0.9.1"
+bech32 = "0.11.0"
 bitcoin = { version = "0.32.2", features = ["secp-lowmemory"] }
 
 afl = { version = "0.12", optional = true }
index 63d869c8178495613f15cb08ebd1205f7e23d3fc..92dde80aa66d19096ec2959a3bae2a8c7b30fce7 100644 (file)
@@ -8,10 +8,11 @@
 // licenses.
 
 use crate::utils::test_logger;
-use bech32::{u5, FromBase32, ToBase32};
+use bech32::Fe32;
 use bitcoin::secp256k1::{Secp256k1, SecretKey};
 use lightning_invoice::{
-       Bolt11Invoice, RawBolt11Invoice, RawDataPart, RawHrp, RawTaggedField, TaggedField,
+       Base32Iterable, Bolt11Invoice, FromBase32, RawBolt11Invoice, RawDataPart, RawHrp,
+       RawTaggedField, TaggedField,
 };
 use std::str::FromStr;
 
@@ -25,19 +26,19 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
                        Err(_) => return,
                };
                let bech32 =
-                       data.iter().skip(hrp_len).map(|x| u5::try_from_u8(x % 32).unwrap()).collect::<Vec<_>>();
+                       data.iter().skip(hrp_len).map(|x| Fe32::try_from(x % 32).unwrap()).collect::<Vec<_>>();
                let invoice_data = match RawDataPart::from_base32(&bech32) {
                        Ok(invoice) => invoice,
                        Err(_) => return,
                };
 
+               let invoice_data_base32 = invoice_data.fe_iter().collect::<Vec<_>>();
                // Our data encoding is not worse than the input
-               assert!(invoice_data.to_base32().len() <= bech32.len());
+               assert!(invoice_data_base32.len() <= bech32.len());
 
                // Our data serialization is loss-less
                assert_eq!(
-                       RawDataPart::from_base32(&invoice_data.to_base32())
-                               .expect("faild parsing out own encoding"),
+                       RawDataPart::from_base32(&invoice_data_base32).expect("faild parsing out own encoding"),
                        invoice_data
                );
 
index 054113fe13ae45e83cd19d5c25646bcc0b690737..ee1d4aa53dc936e633e803d60e036110b914eaa0 100644 (file)
@@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "docsrs"]
 std = []
 
 [dependencies]
-bech32 = { version = "0.9.1", default-features = false }
+bech32 = { version = "0.11.0", default-features = false }
 lightning-types = { version = "0.1.0", path = "../lightning-types", default-features = false }
 serde = { version = "1.0.118", optional = true }
 bitcoin = { version = "0.32.2", default-features = false, features = ["secp-recovery"] }
index e5ba67cccf9ffc2cd2f2ccd4560d5a06417adcd9..7c425441f4ec1f3a338f75a2a1d9c1437ca791d3 100644 (file)
@@ -8,7 +8,8 @@ use core::num::ParseIntError;
 use core::str;
 use core::str::FromStr;
 
-use bech32::{u5, FromBase32};
+use bech32::primitives::decode::{CheckedHrpstring, CheckedHrpstringError};
+use bech32::{Bech32, Fe32, Fe32IterExt};
 
 use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
 use bitcoin::hashes::Hash;
@@ -26,6 +27,117 @@ use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDe
 
 use self::hrp_sm::parse_hrp;
 
+/// Trait for parsing/converting base32 slice.
+pub trait FromBase32: Sized {
+       /// The associated error which can be returned from parsing (e.g. because of bad padding).
+       type Err;
+
+       /// Convert a base32 slice to `Self`.
+       fn from_base32(b32: &[Fe32]) -> Result<Self, Self::Err>;
+}
+
+// FromBase32 implementations are here, because the trait is in this module.
+
+impl FromBase32 for Vec<u8> {
+       type Err = Bolt11ParseError;
+
+       fn from_base32(data: &[Fe32]) -> Result<Self, Self::Err> {
+               Ok(data.iter().copied().fes_to_bytes().collect::<Self>())
+       }
+}
+
+impl<const N: usize> FromBase32 for [u8; N] {
+       type Err = Bolt11ParseError;
+
+       fn from_base32(data: &[Fe32]) -> Result<Self, Self::Err> {
+               let mut res_arr = [0; N];
+               // Do in a for loop to place in the array directly, not using `collect`
+               let mut count = 0;
+               for elem in data.iter().copied().fes_to_bytes() {
+                       if count >= N {
+                               // too many elements
+                               count += 1;
+                               break;
+                       }
+                       res_arr[count] = elem;
+                       count += 1;
+               }
+               if count != N {
+                       return Err(Bolt11ParseError::InvalidSliceLength(
+                               count, N, "<[u8; N]>",
+                       ));
+               }
+               Ok(res_arr)
+       }
+}
+
+impl FromBase32 for PaymentSecret {
+       type Err = Bolt11ParseError;
+
+       fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
+               if field_data.len() != 52 {
+                       return Err(Bolt11ParseError::InvalidSliceLength(
+                               field_data.len(),
+                               52,
+                               "PaymentSecret",
+                       ));
+               }
+               let data_bytes = <[u8; 32]>::from_base32(field_data)?;
+               Ok(PaymentSecret(data_bytes))
+       }
+}
+
+impl FromBase32 for Bolt11InvoiceFeatures {
+       type Err = Bolt11ParseError;
+
+       /// Convert to byte values, by packing the 5-bit groups,
+       /// putting the 5-bit values from left to-right (reverse order),
+       /// starting from the rightmost bit,
+       /// and taking the resulting 8-bit values (right to left),
+       /// with the leading 0's skipped.
+       fn from_base32(field_data: &[Fe32]) -> Result<Self, Self::Err> {
+               // Fe32 conversion cannot be used, because this unpacks from right, right-to-left
+               // Carry bits, 0, 1, 2, 3, or 4 bits
+               let mut carry_bits = 0;
+               let mut carry = 0u8;
+               let expected_raw_length = (field_data.len() * 5 + 7) / 8;
+               let mut output = Vec::<u8>::with_capacity(expected_raw_length);
+
+               // Iterate over input in reverse
+               for curr_in in field_data.iter().rev() {
+                       let curr_in_as_u8 = curr_in.to_u8();
+                       if carry_bits >= 3 {
+                               // we have a new full byte -- 3, 4 or 5 carry bits, plus 5 new ones
+                               // For combining with carry '|', '^', or '+' can be used (disjoint bit positions)
+                               let next = carry + (curr_in_as_u8 << carry_bits);
+                               output.push(next);
+                               carry = curr_in_as_u8 >> (8 - carry_bits);
+                               carry_bits -= 3; // added 5, removed 8
+                       } else {
+                               // only 0, 1, or 2 carry bits,  plus 5 new ones
+                               carry += curr_in_as_u8 << carry_bits;
+                               carry_bits += 5;
+                       }
+               }
+
+               // No more inputs, output remaining (if any)
+               if carry_bits > 0 {
+                       output.push(carry);
+               }
+
+               // This is to double check the estimated length and
+               // satisfying mutation test on the capacity, which is mutatable
+               debug_assert_eq!(output.len(), expected_raw_length);
+
+               // Trim the highest feature bits
+               while !output.is_empty() && output[output.len() - 1] == 0 {
+                       output.pop();
+               }
+
+               Ok(Bolt11InvoiceFeatures::from_le_bytes(output))
+       }
+}
+
 /// State machine to parse the hrp
 mod hrp_sm {
        use core::ops::Range;
@@ -269,31 +381,29 @@ impl FromStr for SignedRawBolt11Invoice {
        type Err = Bolt11ParseError;
 
        fn from_str(s: &str) -> Result<Self, Self::Err> {
-               let (hrp, data, var) = bech32::decode(s)?;
-
-               if var == bech32::Variant::Bech32m {
-                       // Consider Bech32m addresses to be "Invalid Checksum", since that is what we'd get if
-                       // we didn't support Bech32m (which lightning does not use).
-                       return Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum));
-               }
-
-               if data.len() < 104 {
+               let parsed = CheckedHrpstring::new::<Bech32>(s)?;
+               let hrp = parsed.hrp();
+               // Access original non-packed 32 byte values (as Fe32s)
+               // Note: the type argument is needed due to the API peculiarities, but it's not used
+               let data: Vec<_> = parsed.fe32_iter::<&mut dyn Iterator<Item = u8>>().collect();
+
+               const SIGNATURE_LEN_5: usize = 104; // number of the 5-bit values (equals to 65 bytes)
+               if data.len() < SIGNATURE_LEN_5 {
                        return Err(Bolt11ParseError::TooShortDataPart);
                }
 
-               let raw_hrp: RawHrp = hrp.parse()?;
-               let data_part = RawDataPart::from_base32(&data[..data.len()-104])?;
+               let raw_hrp: RawHrp = hrp.to_string().to_lowercase().parse()?;
+               let data_part = RawDataPart::from_base32(&data[..data.len() - SIGNATURE_LEN_5])?;
+               let raw_invoice = RawBolt11Invoice {
+                       hrp: raw_hrp,
+                       data: data_part,
+               };
+               let hash = raw_invoice.signable_hash();
 
                Ok(SignedRawBolt11Invoice {
-                       raw_invoice: RawBolt11Invoice {
-                               hrp: raw_hrp,
-                               data: data_part,
-                       },
-                       hash: RawBolt11Invoice::hash_from_parts(
-                               hrp.as_bytes(),
-                               &data[..data.len()-104]
-                       ),
-                       signature: Bolt11InvoiceSignature::from_base32(&data[data.len()-104..])?,
+                       raw_invoice,
+                       hash,
+                       signature: Bolt11InvoiceSignature::from_base32(&data[data.len() - SIGNATURE_LEN_5..])?,
                })
        }
 }
@@ -335,7 +445,7 @@ impl FromStr for RawHrp {
 impl FromBase32 for RawDataPart {
        type Err = Bolt11ParseError;
 
-       fn from_base32(data: &[u5]) -> Result<Self, Self::Err> {
+       fn from_base32(data: &[Fe32]) -> Result<Self, Self::Err> {
                if data.len() < 7 { // timestamp length
                        return Err(Bolt11ParseError::TooShortDataPart);
                }
@@ -353,9 +463,13 @@ impl FromBase32 for RawDataPart {
 impl FromBase32 for PositiveTimestamp {
        type Err = Bolt11ParseError;
 
-       fn from_base32(b32: &[u5]) -> Result<Self, Self::Err> {
+       fn from_base32(b32: &[Fe32]) -> Result<Self, Self::Err> {
                if b32.len() != 7 {
-                       return Err(Bolt11ParseError::InvalidSliceLength("PositiveTimestamp::from_base32()".into()));
+                       return Err(Bolt11ParseError::InvalidSliceLength(
+                               b32.len(),
+                               7,
+                               "PositiveTimestamp",
+                       ));
                }
                let timestamp: u64 = parse_u64_be(b32)
                        .expect("7*5bit < 64bit, no overflow possible");
@@ -368,11 +482,15 @@ impl FromBase32 for PositiveTimestamp {
 
 impl FromBase32 for Bolt11InvoiceSignature {
        type Err = Bolt11ParseError;
-       fn from_base32(signature: &[u5]) -> Result<Self, Self::Err> {
+       fn from_base32(signature: &[Fe32]) -> Result<Self, Self::Err> {
                if signature.len() != 104 {
-                       return Err(Bolt11ParseError::InvalidSliceLength("Bolt11InvoiceSignature::from_base32()".into()));
+                       return Err(Bolt11ParseError::InvalidSliceLength(
+                               signature.len(),
+                               104,
+                               "Bolt11InvoiceSignature",
+                       ));
                }
-               let recoverable_signature_bytes = Vec::<u8>::from_base32(signature)?;
+               let recoverable_signature_bytes = <[u8; 65]>::from_base32(signature)?;
                let signature = &recoverable_signature_bytes[0..64];
                let recovery_id = RecoveryId::from_i32(recoverable_signature_bytes[64] as i32)?;
 
@@ -384,7 +502,7 @@ impl FromBase32 for Bolt11InvoiceSignature {
 }
 
 macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
-       fn $name(digits: &[u5]) -> Option<$ty> {
+       fn $name(digits: &[Fe32]) -> Option<$ty> {
                digits.iter().fold(Some(Default::default()), |acc, b|
                        acc
                                .and_then(|x| x.checked_mul(32))
@@ -395,7 +513,7 @@ macro_rules! define_parse_int_be { ($name: ident, $ty: ty) => {
 define_parse_int_be!(parse_u16_be, u16);
 define_parse_int_be!(parse_u64_be, u64);
 
-fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
+fn parse_tagged_parts(data: &[Fe32]) -> Result<Vec<RawTaggedField>, Bolt11ParseError> {
        let mut parts = Vec::<RawTaggedField>::new();
        let mut data = data;
 
@@ -423,7 +541,9 @@ fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseErr
                        Ok(field) => {
                                parts.push(RawTaggedField::KnownSemantics(field))
                        },
-                       Err(Bolt11ParseError::Skip)|Err(Bolt11ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
+                       Err(Bolt11ParseError::Skip)
+                       | Err(Bolt11ParseError::InvalidSliceLength(_, _, _))
+                       | Err(Bolt11ParseError::Bech32Error(_)) => {
                                parts.push(RawTaggedField::UnknownSemantics(field.into()))
                        },
                        Err(e) => {return Err(e)}
@@ -435,7 +555,7 @@ fn parse_tagged_parts(data: &[u5]) -> Result<Vec<RawTaggedField>, Bolt11ParseErr
 impl FromBase32 for TaggedField {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field: &[u5]) -> Result<TaggedField, Bolt11ParseError> {
+       fn from_base32(field: &[Fe32]) -> Result<TaggedField, Bolt11ParseError> {
                if field.len() < 3 {
                        return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
                }
@@ -477,12 +597,12 @@ impl FromBase32 for TaggedField {
 impl FromBase32 for Sha256 {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<Sha256, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<Sha256, Bolt11ParseError> {
                if field_data.len() != 52 {
                        // "A reader MUST skip over […] a p, [or] h […] field that does not have data_length 52 […]."
                        Err(Bolt11ParseError::Skip)
                } else {
-                       Ok(Sha256(sha256::Hash::from_slice(&Vec::<u8>::from_base32(field_data)?)
+                       Ok(Sha256(sha256::Hash::from_slice(&<[u8; 32]>::from_base32(field_data)?)
                                .expect("length was checked before (52 u5 -> 32 u8)")))
                }
        }
@@ -491,7 +611,7 @@ impl FromBase32 for Sha256 {
 impl FromBase32 for Description {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<Description, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<Description, Bolt11ParseError> {
                let bytes = Vec::<u8>::from_base32(field_data)?;
                let description = String::from(str::from_utf8(&bytes)?);
                Ok(Description::new(description).expect(
@@ -503,12 +623,12 @@ impl FromBase32 for Description {
 impl FromBase32 for PayeePubKey {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<PayeePubKey, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<PayeePubKey, Bolt11ParseError> {
                if field_data.len() != 53 {
                        // "A reader MUST skip over […] a n […] field that does not have data_length 53 […]."
                        Err(Bolt11ParseError::Skip)
                } else {
-                       let data_bytes = Vec::<u8>::from_base32(field_data)?;
+                       let data_bytes = <[u8; 33]>::from_base32(field_data)?;
                        let pub_key = PublicKey::from_slice(&data_bytes)?;
                        Ok(pub_key.into())
                }
@@ -518,7 +638,7 @@ impl FromBase32 for PayeePubKey {
 impl FromBase32 for ExpiryTime {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<ExpiryTime, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<ExpiryTime, Bolt11ParseError> {
                match parse_u64_be(field_data)
                        .map(ExpiryTime::from_seconds)
                {
@@ -531,7 +651,7 @@ impl FromBase32 for ExpiryTime {
 impl FromBase32 for MinFinalCltvExpiryDelta {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<MinFinalCltvExpiryDelta, Bolt11ParseError> {
                let expiry = parse_u64_be(field_data);
                if let Some(expiry) = expiry {
                        Ok(MinFinalCltvExpiryDelta(expiry))
@@ -544,7 +664,7 @@ impl FromBase32 for MinFinalCltvExpiryDelta {
 impl FromBase32 for Fallback {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<Fallback, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<Fallback, Bolt11ParseError> {
                if field_data.is_empty() {
                        return Err(Bolt11ParseError::UnexpectedEndOfTaggedFields);
                }
@@ -585,7 +705,7 @@ impl FromBase32 for Fallback {
 impl FromBase32 for PrivateRoute {
        type Err = Bolt11ParseError;
 
-       fn from_base32(field_data: &[u5]) -> Result<PrivateRoute, Bolt11ParseError> {
+       fn from_base32(field_data: &[Fe32]) -> Result<PrivateRoute, Bolt11ParseError> {
                let bytes = Vec::<u8>::from_base32(field_data)?;
 
                if bytes.len() % 51 != 0 {
@@ -637,9 +757,13 @@ impl Display for Bolt11ParseError {
                        Bolt11ParseError::DescriptionDecodeError(ref e) => {
                                write!(f, "Description is not a valid utf-8 string: {}", e)
                        }
-                       Bolt11ParseError::InvalidSliceLength(ref function) => {
-                               write!(f, "Slice in function {} had the wrong length", function)
-                       }
+                       Bolt11ParseError::InvalidSliceLength(ref len, ref expected, ref elemen) => {
+                               write!(
+                                       f,
+                                       "Slice had length {} instead of {} for element {}",
+                                       len, expected, elemen
+                               )
+                       },
                        Bolt11ParseError::BadPrefix => f.write_str("did not begin with 'ln'"),
                        Bolt11ParseError::UnknownCurrency => f.write_str("currency code unknown"),
                        Bolt11ParseError::UnknownSiPrefix => f.write_str("unknown SI prefix"),
@@ -702,12 +826,9 @@ from_error!(Bolt11ParseError::MalformedSignature, bitcoin::secp256k1::Error);
 from_error!(Bolt11ParseError::ParseAmountError, ParseIntError);
 from_error!(Bolt11ParseError::DescriptionDecodeError, str::Utf8Error);
 
-impl From<bech32::Error> for Bolt11ParseError {
-       fn from(e: bech32::Error) -> Self {
-               match e {
-                       bech32::Error::InvalidPadding => Bolt11ParseError::PaddingError,
-                       _ => Bolt11ParseError::Bech32Error(e)
-               }
+impl From<CheckedHrpstringError> for Bolt11ParseError {
+       fn from(e: CheckedHrpstringError) -> Self {
+               Self::Bech32Error(e)
        }
 }
 
@@ -725,9 +846,10 @@ impl From<crate::Bolt11SemanticError> for ParseOrSemanticError {
 
 #[cfg(test)]
 mod test {
+       use super::FromBase32;
        use crate::de::Bolt11ParseError;
        use bitcoin::secp256k1::PublicKey;
-       use bech32::u5;
+       use bech32::Fe32;
        use bitcoin::hashes::sha256;
        use std::str::FromStr;
 
@@ -742,10 +864,10 @@ mod test {
                1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
        ];
 
-       fn from_bech32(bytes_5b: &[u8]) -> Vec<u5> {
+       fn from_bech32(bytes_5b: &[u8]) -> Vec<Fe32> {
                bytes_5b
                        .iter()
-                       .map(|c| u5::try_from_u8(CHARSET_REV[*c as usize] as u8).unwrap())
+                       .map(|c| Fe32::try_from(CHARSET_REV[*c as usize] as u8).unwrap())
                        .collect()
        }
 
@@ -766,19 +888,18 @@ mod test {
                use crate::de::parse_u16_be;
 
                assert_eq!(parse_u16_be(&[
-                               u5::try_from_u8(1).unwrap(), u5::try_from_u8(2).unwrap(),
-                               u5::try_from_u8(3).unwrap(), u5::try_from_u8(4).unwrap()]
-                       ), Some(34916));
+                       Fe32::try_from(1).unwrap(), Fe32::try_from(2).unwrap(),
+                       Fe32::try_from(3).unwrap(), Fe32::try_from(4).unwrap(),
+               ]), Some(34916));
                assert_eq!(parse_u16_be(&[
-                               u5::try_from_u8(2).unwrap(), u5::try_from_u8(0).unwrap(),
-                               u5::try_from_u8(0).unwrap(), u5::try_from_u8(0).unwrap()]
-                       ), None);
+                       Fe32::try_from(2).unwrap(), Fe32::try_from(0).unwrap(),
+                       Fe32::try_from(0).unwrap(), Fe32::try_from(0).unwrap(),
+               ]), None);
        }
 
        #[test]
        fn test_parse_sha256_hash() {
                use crate::Sha256;
-               use bech32::FromBase32;
 
                let input = from_bech32(
                        "qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq".as_bytes()
@@ -801,7 +922,6 @@ mod test {
        #[test]
        fn test_parse_description() {
                use crate::Description;
-               use bech32::FromBase32;
 
                let input = from_bech32("xysxxatsyp3k7enxv4js".as_bytes());
                let expected = Ok(Description::new("1 cup coffee".to_owned()).unwrap());
@@ -811,7 +931,6 @@ mod test {
        #[test]
        fn test_parse_payee_pub_key() {
                use crate::PayeePubKey;
-               use bech32::FromBase32;
 
                let input = from_bech32("q0n326hr8v9zprg8gsvezcch06gfaqqhde2aj730yg0durunfhv66".as_bytes());
                let pk_bytes = [
@@ -835,7 +954,6 @@ mod test {
        #[test]
        fn test_parse_expiry_time() {
                use crate::ExpiryTime;
-               use bech32::FromBase32;
 
                let input = from_bech32("pu".as_bytes());
                let expected = Ok(ExpiryTime::from_seconds(60));
@@ -848,7 +966,6 @@ mod test {
        #[test]
        fn test_parse_min_final_cltv_expiry_delta() {
                use crate::MinFinalCltvExpiryDelta;
-               use bech32::FromBase32;
 
                let input = from_bech32("pr".as_bytes());
                let expected = Ok(MinFinalCltvExpiryDelta(35));
@@ -859,7 +976,6 @@ mod test {
        #[test]
        fn test_parse_fallback() {
                use crate::Fallback;
-               use bech32::FromBase32;
                use bitcoin::{PubkeyHash, ScriptHash, WitnessVersion};
                use bitcoin::hashes::Hash;
 
@@ -889,7 +1005,7 @@ mod test {
                                })
                        ),
                        (
-                               vec![u5::try_from_u8(21).unwrap(); 41],
+                               vec![Fe32::try_from(21).unwrap(); 41],
                                Err(Bolt11ParseError::Skip)
                        ),
                        (
@@ -897,15 +1013,15 @@ mod test {
                                Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
                        ),
                        (
-                               vec![u5::try_from_u8(1).unwrap(); 81],
+                               vec![Fe32::try_from(1).unwrap(); 81],
                                Err(Bolt11ParseError::InvalidSegWitProgramLength)
                        ),
                        (
-                               vec![u5::try_from_u8(17).unwrap(); 1],
+                               vec![Fe32::try_from(17).unwrap(); 1],
                                Err(Bolt11ParseError::InvalidPubKeyHashLength)
                        ),
                        (
-                               vec![u5::try_from_u8(18).unwrap(); 1],
+                               vec![Fe32::try_from(18).unwrap(); 1],
                                Err(Bolt11ParseError::InvalidScriptHashLength)
                        )
                ];
@@ -919,7 +1035,6 @@ mod test {
        fn test_parse_route() {
                use lightning_types::routing::{RoutingFees, RouteHint, RouteHintHop};
                use crate::PrivateRoute;
-               use bech32::FromBase32;
 
                let input = from_bech32(
                        "q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
@@ -965,7 +1080,7 @@ mod test {
                assert_eq!(PrivateRoute::from_base32(&input), Ok(PrivateRoute(RouteHint(expected))));
 
                assert_eq!(
-                       PrivateRoute::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
+                       PrivateRoute::from_base32(&[Fe32::try_from(0).unwrap(); 40][..]),
                        Err(Bolt11ParseError::UnexpectedEndOfTaggedFields)
                );
        }
index 08b670f41fef8cbadb649a9df3aca913add9c146..36370896052f33989ed5a853495f6bcc5a15080d 100644 (file)
@@ -33,7 +33,8 @@ extern crate serde;
 #[cfg(feature = "std")]
 use std::time::SystemTime;
 
-use bech32::{FromBase32, u5};
+use bech32::primitives::decode::CheckedHrpstringError;
+use bech32::Fe32;
 use bitcoin::{Address, Network, PubkeyHash, ScriptHash, WitnessProgram, WitnessVersion};
 use bitcoin::hashes::{Hash, sha256};
 use lightning_types::features::Bolt11InvoiceFeatures;
@@ -42,6 +43,7 @@ use bitcoin::secp256k1::PublicKey;
 use bitcoin::secp256k1::{Message, Secp256k1};
 use bitcoin::secp256k1::ecdsa::RecoverableSignature;
 
+use alloc::boxed::Box;
 use core::cmp::Ordering;
 use core::fmt::{Display, Formatter, self};
 use core::iter::FilterMap;
@@ -76,12 +78,18 @@ mod prelude {
 
 use crate::prelude::*;
 
+/// Re-export serialization traits
+#[cfg(fuzzing)]
+pub use crate::de::FromBase32;
+#[cfg(fuzzing)]
+pub use crate::ser::Base32Iterable;
+
 /// 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, Eq, Debug, Clone)]
 pub enum Bolt11ParseError {
-       Bech32Error(bech32::Error),
+       Bech32Error(CheckedHrpstringError),
        ParseAmountError(ParseIntError),
        MalformedSignature(bitcoin::secp256k1::Error),
        BadPrefix,
@@ -97,7 +105,8 @@ pub enum Bolt11ParseError {
        InvalidPubKeyHashLength,
        InvalidScriptHashLength,
        InvalidRecoveryId,
-       InvalidSliceLength(String),
+       // Invalid length, with actual length, expected length, and name of the element
+       InvalidSliceLength(usize, usize, &'static str),
 
        /// Not an error, but used internally to signal that a part of the invoice should be ignored
        /// according to BOLT11
@@ -299,6 +308,15 @@ pub struct RawHrp {
        pub si_prefix: Option<SiPrefix>,
 }
 
+impl RawHrp {
+       /// Convert to bech32::Hrp
+       pub fn to_hrp(&self) -> bech32::Hrp {
+               let hrp_str = self.to_string();
+               let s = core::str::from_utf8(&hrp_str.as_bytes()).expect("HRP bytes should be ASCII");
+               bech32::Hrp::parse_unchecked(s)
+       }
+}
+
 /// Data of the [`RawBolt11Invoice`] that is encoded in the data part
 #[derive(Eq, PartialEq, Debug, Clone, Hash, Ord, PartialOrd)]
 pub struct RawDataPart {
@@ -404,12 +422,38 @@ impl From<Currency> for Network {
 /// Tagged field which may have an unknown tag
 ///
 /// This is not exported to bindings users as we don't currently support TaggedField
-#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
 pub enum RawTaggedField {
        /// Parsed tagged field with known tag
        KnownSemantics(TaggedField),
        /// tagged field which was not parsed due to an unknown tag or undefined field semantics
-       UnknownSemantics(Vec<u5>),
+       UnknownSemantics(Vec<Fe32>),
+}
+
+impl PartialOrd for RawTaggedField {
+       fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+               Some(self.cmp(other))
+       }
+}
+
+/// Note: `Ord `cannot be simply derived because of `Fe32`.
+impl Ord for RawTaggedField {
+       fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+               match (self, other) {
+                       (RawTaggedField::KnownSemantics(ref a), RawTaggedField::KnownSemantics(ref b)) => {
+                               a.cmp(b)
+                       },
+                       (RawTaggedField::UnknownSemantics(ref a), RawTaggedField::UnknownSemantics(ref b)) => {
+                               a.iter().map(|a| a.to_u8()).cmp(b.iter().map(|b| b.to_u8()))
+                       },
+                       (RawTaggedField::KnownSemantics(..), RawTaggedField::UnknownSemantics(..)) => {
+                               core::cmp::Ordering::Less
+                       },
+                       (RawTaggedField::UnknownSemantics(..), RawTaggedField::KnownSemantics(..)) => {
+                               core::cmp::Ordering::Greater
+                       },
+               }
+       }
 }
 
 /// Tagged field with known tag
@@ -956,37 +1000,46 @@ macro_rules! find_all_extract {
 
 #[allow(missing_docs)]
 impl RawBolt11Invoice {
-       /// Hash the HRP as bytes and signatureless data part.
-       fn hash_from_parts(hrp_bytes: &[u8], data_without_signature: &[u5]) -> [u8; 32] {
-               let mut preimage = Vec::<u8>::from(hrp_bytes);
+       /// Hash the HRP (as bytes) and signatureless data part (as Fe32 iterator)
+       fn hash_from_parts<'s>(hrp_bytes: &[u8], data_without_signature: Box<dyn Iterator<Item = Fe32> + 's>) -> [u8; 32] {
+               use crate::bech32::Fe32IterExt;
+               use bitcoin::hashes::HashEngine;
+
+               let mut data_part = data_without_signature.collect::<Vec<Fe32>>();
 
-               let mut data_part = Vec::from(data_without_signature);
+               // Need to pad before from_base32 conversion
                let overhang = (data_part.len() * 5) % 8;
                if overhang > 0 {
                        // add padding if data does not end at a byte boundary
-                       data_part.push(u5::try_from_u8(0).unwrap());
+                       data_part.push(Fe32::try_from(0).unwrap());
 
-                       // if overhang is in (1..3) we need to add u5(0) padding two times
+                       // if overhang is in (1..3) we need to add Fe32(0) padding two times
                        if overhang < 3 {
-                               data_part.push(u5::try_from_u8(0).unwrap());
+                               data_part.push(Fe32::try_from(0).unwrap());
                        }
                }
 
-               preimage.extend_from_slice(&Vec::<u8>::from_base32(&data_part)
-                       .expect("No padding error may occur due to appended zero above."));
+               // Hash bytes and data part sequentially
+               let mut engine = sha256::Hash::engine();
+               engine.input(hrp_bytes);
+               // Iterate over data
+               // Note: if it was not for padding, this could go on the supplied original iterator
+               //  (see https://github.com/rust-bitcoin/rust-bech32/issues/198)
+               data_part.into_iter().fes_to_bytes().for_each(|v| { engine.input(&[v])});
+               let raw_hash = sha256::Hash::from_engine(engine);
 
                let mut hash: [u8; 32] = Default::default();
-               hash.copy_from_slice(&sha256::Hash::hash(&preimage)[..]);
+               hash.copy_from_slice(raw_hash.as_ref());
                hash
        }
 
        /// Calculate the hash of the encoded `RawBolt11Invoice` which should be signed.
        pub fn signable_hash(&self) -> [u8; 32] {
-               use bech32::ToBase32;
+               use crate::ser::Base32Iterable;
 
-               RawBolt11Invoice::hash_from_parts(
+               Self::hash_from_parts(
                        self.hrp.to_string().as_bytes(),
-                       &self.data.to_base32()
+                       self.data.fe_iter(),
                )
        }
 
@@ -1493,7 +1546,7 @@ impl From<TaggedField> for RawTaggedField {
 
 impl TaggedField {
        /// Numeric representation of the field's tag
-       pub fn tag(&self) -> u5 {
+       pub fn tag(&self) -> Fe32 {
                let tag = match *self {
                        TaggedField::PaymentHash(_) => constants::TAG_PAYMENT_HASH,
                        TaggedField::Description(_) => constants::TAG_DESCRIPTION,
@@ -1508,7 +1561,7 @@ impl TaggedField {
                        TaggedField::Features(_) => constants::TAG_FEATURES,
                };
 
-               u5::try_from_u8(tag).expect("all tags defined are <32")
+               Fe32::try_from(tag).expect("all tags defined are <32")
        }
 }
 
@@ -2243,4 +2296,32 @@ mod test {
                assert_eq!(invoice_str, deserialized_invoice.to_string().as_str());
                assert_eq!(invoice_str, serialized_invoice.as_str().trim_matches('\"'));
        }
+
+       #[test]
+       fn raw_tagged_field_ordering() {
+               use crate::{Description, Fe32, RawTaggedField, TaggedField, Sha256, sha256, UntrustedString};
+
+               let field10 = RawTaggedField::KnownSemantics(
+                       TaggedField::PaymentHash(Sha256(sha256::Hash::from_str(
+                               "0001020304050607080900010203040506070809000102030405060708090102"
+                       ).unwrap()))
+               );
+               let field11 = RawTaggedField::KnownSemantics(
+                       TaggedField::Description(Description(UntrustedString("Description".to_string())))
+               );
+               let field20 = RawTaggedField::UnknownSemantics(vec![Fe32::Q]);
+               let field21 = RawTaggedField::UnknownSemantics(vec![Fe32::R]);
+
+               assert!(field10 < field20);
+               assert!(field20 > field10);
+               assert_eq!(field10.cmp(&field20), std::cmp::Ordering::Less);
+               assert_eq!(field20.cmp(&field10), std::cmp::Ordering::Greater);
+               assert_eq!(field10.cmp(&field10), std::cmp::Ordering::Equal);
+               assert_eq!(field20.cmp(&field20), std::cmp::Ordering::Equal);
+               assert_eq!(field10.partial_cmp(&field20).unwrap(), std::cmp::Ordering::Less);
+               assert_eq!(field20.partial_cmp(&field10).unwrap(), std::cmp::Ordering::Greater);
+
+               assert_eq!(field10.partial_cmp(&field11).unwrap(), std::cmp::Ordering::Less);
+               assert_eq!(field20.partial_cmp(&field21).unwrap(), std::cmp::Ordering::Less);
+       }
 }
index 69ef3693c6133aadea2a19560f80ce683c5fbe71..4000241f5575cf88cd1c1667d371986d80422e1d 100644 (file)
 use core::fmt;
 use core::fmt::{Display, Formatter};
-use bech32::{ToBase32, u5, WriteBase32, Base32Len};
+use core::{array, iter};
+use alloc::boxed::Box;
+
+use bech32::{ByteIterExt, Fe32, Fe32IterExt};
 use crate::prelude::*;
 
-use super::{Bolt11Invoice, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PositiveTimestamp,
-       PrivateRoute, Description, RawTaggedField, Currency, RawHrp, SiPrefix, constants, SignedRawBolt11Invoice, RawDataPart};
-
-/// Converts a stream of bytes written to it to base32. On finalization the according padding will
-/// be applied. That means the results of writing two data blocks with one or two `BytesToBase32`
-/// converters will differ.
-struct BytesToBase32<'a, W: WriteBase32 + 'a> {
-       /// Target for writing the resulting `u5`s resulting from the written bytes
-       writer: &'a mut W,
-       /// Holds all unwritten bits left over from last round. The bits are stored beginning from
-       /// the most significant bit. E.g. if buffer_bits=3, then the byte with bits a, b and c will
-       /// look as follows: [a, b, c, 0, 0, 0, 0, 0]
-       buffer: u8,
-       /// Amount of bits left over from last round, stored in buffer.
-       buffer_bits: u8,
-}
-
-impl<'a, W: WriteBase32> BytesToBase32<'a, W> {
-       /// Create a new bytes-to-base32 converter with `writer` as  a sink for the resulting base32
-       /// data.
-       pub fn new(writer: &'a mut W) -> BytesToBase32<'a, W> {
-               BytesToBase32 {
-                       writer,
-                       buffer: 0,
-                       buffer_bits: 0,
-               }
-       }
+use super::{Bolt11Invoice, Bolt11InvoiceFeatures, Sha256, TaggedField, ExpiryTime, MinFinalCltvExpiryDelta, Fallback, PayeePubKey, Bolt11InvoiceSignature, PaymentSecret, PositiveTimestamp,
+       PrivateRoute, Description, RawTaggedField, Currency, RawHrp, SiPrefix, constants, SignedRawBolt11Invoice, RawDataPart, RouteHintHop};
 
-       /// Add more bytes to the current conversion unit
-       pub fn append(&mut self, bytes: &[u8]) -> Result<(), W::Err> {
-               for b in bytes {
-                       self.append_u8(*b)?;
-               }
-               Ok(())
+/// Objects that can be encoded to base32 (bech32).
+///
+/// Private to this crate to avoid polluting the API.
+pub trait Base32Iterable {
+       /// apoelstra: In future we want to replace this Box<dyn Iterator> with an explicit
+       /// associated type, to avoid the allocation. But we cannot do this until
+       /// Rust 1.65 and GATs since the iterator may contain a reference to self.
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's>;
+}
+
+/// Interface to calculate the length of the base32 representation before actually serializing
+pub(crate) trait Base32Len: Base32Iterable {
+       /// Calculate the bech32 serialized length
+       fn base32_len(&self) -> usize;
+}
+
+// Base32Iterable & Base32Len implementations are here, because the traits are in this module.
+
+impl<const N: usize> Base32Iterable for [u8; N] {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new((*self).into_iter().bytes_to_fes())
        }
+}
 
-       pub fn append_u8(&mut self, byte: u8) -> Result<(), W::Err> {
-               // Write first u5 if we have to write two u5s this round. That only happens if the
-               // buffer holds too many bits, so we don't have to combine buffer bits with new bits
-               // from this rounds byte.
-               if self.buffer_bits >= 5 {
-                       self.writer.write_u5(
-                               u5::try_from_u8((self.buffer & 0b11111000) >> 3 ).expect("<32")
-                       )?;
-                       self.buffer <<= 5;
-                       self.buffer_bits -= 5;
-               }
+impl<const N: usize> Base32Len for [u8; N] {
+       /// Calculate the base32 serialized length
+       fn base32_len(&self) -> usize {
+               bytes_size_to_base32_size(N)
+       }
+}
 
-               // Combine all bits from buffer with enough bits from this rounds byte so that they fill
-               // a u5. Save remaining bits from byte to buffer.
-               let from_buffer = self.buffer >> 3;
-               let from_byte = byte >> (3 + self.buffer_bits); // buffer_bits <= 4
+impl Base32Iterable for [u8] {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(self.iter().copied().bytes_to_fes())
+       }
+}
 
-               self.writer.write_u5(u5::try_from_u8(from_buffer | from_byte).expect("<32"))?;
-               self.buffer = byte << (5 - self.buffer_bits);
-               self.buffer_bits += 3;
+impl Base32Len for [u8] {
+       /// Calculate the base32 serialized length
+       fn base32_len(&self) -> usize {
+               bytes_size_to_base32_size(self.len())
+       }
+}
 
-               Ok(())
+impl Base32Iterable for Vec<u8> {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(self.iter().copied().bytes_to_fes())
        }
+}
 
-       pub fn finalize(mut self) ->  Result<(), W::Err> {
-               self.inner_finalize()?;
-               core::mem::forget(self);
-               Ok(())
+impl Base32Len for Vec<u8> {
+       /// Calculate the base32 serialized length
+       fn base32_len(&self) -> usize {
+               bytes_size_to_base32_size(self.len())
        }
+}
 
-       fn inner_finalize(&mut self) -> Result<(), W::Err>{
-               // There can be at most two u5s left in the buffer after processing all bytes, write them.
-               if self.buffer_bits >= 5 {
-                       self.writer.write_u5(
-                               u5::try_from_u8((self.buffer & 0b11111000) >> 3).expect("<32")
-                       )?;
-                       self.buffer <<= 5;
-                       self.buffer_bits -= 5;
-               }
+impl Base32Iterable for PaymentSecret {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(self.0[..].fe_iter())
+       }
+}
 
-               if self.buffer_bits != 0 {
-                       self.writer.write_u5(u5::try_from_u8(self.buffer >> 3).expect("<32"))?;
+impl Base32Len for PaymentSecret {
+       fn base32_len(&self) -> usize {
+               52
+       }
+}
+
+impl Base32Iterable for Bolt11InvoiceFeatures {
+       /// Convert to 5-bit values, by unpacking the 5 bit groups,
+       /// putting the bytes from right-to-left,
+       /// starting from the rightmost bit,
+       /// and taking the resulting 5-bit values in reverse (left-to-right),
+       /// with the leading 0's skipped.
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               // Fe32 conversion cannot be used, because this packs from right, right-to-left
+               let mut input_iter = self.le_flags().iter();
+               // Carry bits, 0..7 bits
+               let mut carry = 0u8;
+               let mut carry_bits = 0;
+               let mut output = Vec::<Fe32>::new();
+
+               loop {
+                       let next_out8 = if carry_bits >= 5 {
+                               // We have enough carry bits for an output, no need to read the input
+                               let next_out8 = carry;
+                               carry >>= 5;
+                               carry_bits -= 5;
+                               next_out8
+                       } else {
+                               // take next byte
+                               if let Some(curr_in) = input_iter.next() {
+                                       // we have at least one Fe32 to output (maybe two)
+                                       // For combining with carry '|', '^', or '+' can be used (disjoint bit positions)
+                                       let next_out8 = carry + (curr_in << carry_bits);
+                                       carry = curr_in >> (5 - carry_bits);
+                                       carry_bits += 3; // added 8, removed 5
+                                       next_out8
+                               } else {
+                                       // No more inputs, output remaining (if any)
+                                       if carry_bits > 0 {
+                                               carry_bits = 0;
+                                               carry
+                                       } else {
+                                               break;
+                                       }
+                               }
+                       };
+                       // Isolate the 5 right bits
+                       output.push(Fe32::try_from(next_out8 & 31u8).expect("<32"))
                }
-
-               Ok(())
+               // Take result in reverse order, and skip leading 0s
+               Box::new(output.into_iter().rev().skip_while(|e| *e == Fe32::Q))
        }
 }
 
-impl<'a, W: WriteBase32> Drop for BytesToBase32<'a, W> {
-       fn drop(&mut self) {
-               self.inner_finalize()
-                       .expect("Unhandled error when finalizing conversion on drop. User finalize to handle.")
+impl Base32Len for Bolt11InvoiceFeatures {
+       fn base32_len(&self) -> usize {
+               // Here we perform the real conversion, due to trimming it's hard to estimate
+               self.fe_iter().count()
        }
 }
 
@@ -107,26 +146,31 @@ fn bytes_size_to_base32_size(byte_size: usize) -> usize {
 }
 
 impl Display for Bolt11Invoice {
-       fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+       fn fmt(&self, f: &mut Formatter) -> fmt::Result {
                self.signed_invoice.fmt(f)
        }
 }
 
 impl Display for SignedRawBolt11Invoice {
-       fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
-               let hrp = self.raw_invoice.hrp.to_string();
-               let mut data  = self.raw_invoice.data.to_base32();
-               data.extend_from_slice(&self.signature.to_base32());
-
-               bech32::encode_to_fmt(f, &hrp, data, bech32::Variant::Bech32).expect("HRP is valid")?;
-
+       fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+               let hrp = self.raw_invoice.hrp.to_hrp();
+               for ch in self
+                       .raw_invoice
+                       .data
+                       .fe_iter()
+                       .chain(self.signature.fe_iter())
+                       .with_checksum::<bech32::Bech32>(&hrp)
+                       .chars()
+               {
+                       write!(f, "{}", ch)?;
+               }
                Ok(())
        }
 }
 
 /// This is not exported to bindings users
 impl Display for RawHrp {
-       fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+       fn fmt(&self, f: &mut Formatter) -> fmt::Result {
                let amount = match self.raw_amount {
                        Some(ref amt) => amt.to_string(),
                        None => String::new(),
@@ -148,7 +192,7 @@ impl Display for RawHrp {
 }
 
 impl Display for Currency {
-       fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+       fn fmt(&self, f: &mut Formatter) -> fmt::Result {
                let currency_code = match *self {
                        Currency::Bitcoin => "bc",
                        Currency::BitcoinTestnet => "tb",
@@ -161,7 +205,7 @@ impl Display for Currency {
 }
 
 impl Display for SiPrefix {
-       fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+       fn fmt(&self, f: &mut Formatter) -> fmt::Result {
                write!(f, "{}",
                        match *self {
                                SiPrefix::Milli => "m",
@@ -173,14 +217,15 @@ impl Display for SiPrefix {
        }
 }
 
-fn encode_int_be_base32(int: u64) -> Vec<u5> {
+/// Encode an integer to base32, big endian, without leading zeros
+fn encode_int_be_base32(int: u64) -> Vec<Fe32> {
        let base = 32u64;
 
-       let mut out_vec = Vec::<u5>::new();
-
+       // (64 + 4) / 5 == 13
+       let mut out_vec = Vec::<Fe32>::with_capacity(13);
        let mut rem_int = int;
        while rem_int != 0 {
-               out_vec.push(u5::try_from_u8((rem_int % base) as u8).expect("always <32"));
+               out_vec.push(Fe32::try_from((rem_int % base) as u8).expect("always <32"));
                rem_int /= base;
        }
 
@@ -188,98 +233,55 @@ fn encode_int_be_base32(int: u64) -> Vec<u5> {
        out_vec
 }
 
+/// The length of the output of `encode_int_be_base32`.
 fn encoded_int_be_base32_size(int: u64) -> usize {
-       for pos in (0..13).rev() {
-               if int & (0x1f << (5 * pos)) != 0 {
-                       return (pos + 1) as usize;
-               }
-       }
-       0usize
+       let bit_len = 64 - int.leading_zeros() as usize; // cast ok as value is in 0..=64.
+       (bit_len + 4) / 5
 }
 
-fn encode_int_be_base256<T: Into<u64>>(int: T) -> Vec<u8> {
-       let base = 256u64;
-
-       let mut out_vec = Vec::<u8>::new();
-
-       let mut rem_int: u64 = int.into();
-       while rem_int != 0 {
-               out_vec.push((rem_int % base) as u8);
-               rem_int /= base;
+impl Base32Iterable for RawDataPart {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               let ts_iter = self.timestamp.fe_iter();
+               let fields_iter = self.tagged_fields.iter().map(RawTaggedField::fe_iter).flatten();
+               Box::new(ts_iter.chain(fields_iter))
        }
-
-       out_vec.reverse();
-       out_vec
 }
 
-/// Appends the default value of `T` to the front of the `in_vec` till it reaches the length
-/// `target_length`. If `in_vec` already is too lang `None` is returned.
-fn try_stretch<T>(mut in_vec: Vec<T>, target_len: usize) -> Option<Vec<T>>
-       where T: Default + Copy
-{
-       if in_vec.len() > target_len {
-               None
-       } else if in_vec.len() == target_len {
-               Some(in_vec)
-       } else {
-               let mut out_vec = Vec::<T>::with_capacity(target_len);
-               out_vec.append(&mut vec![T::default(); target_len - in_vec.len()]);
-               out_vec.append(&mut in_vec);
-               Some(out_vec)
-       }
-}
-
-impl ToBase32 for RawDataPart {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               // encode timestamp
-               self.timestamp.write_base32(writer)?;
-
-               // encode tagged fields
-               for tagged_field in self.tagged_fields.iter() {
-                       tagged_field.write_base32(writer)?;
-               }
-
-               Ok(())
-       }
-}
-
-impl ToBase32 for PositiveTimestamp {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               // FIXME: use writer for int encoding
-               writer.write(
-                       &try_stretch(encode_int_be_base32(self.as_unix_timestamp()), 7)
-                               .expect("Can't be longer due than 7 u5s due to timestamp bounds")
-               )
+impl Base32Iterable for PositiveTimestamp {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               let fes = encode_int_be_base32(self.as_unix_timestamp());
+               debug_assert!(fes.len() <= 7, "Invalid timestamp length");
+               let to_pad = 7 - fes.len();
+               Box::new(core::iter::repeat(Fe32::Q).take(to_pad).chain(fes.into_iter()))
        }
 }
 
-impl ToBase32 for RawTaggedField {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
+impl Base32Iterable for RawTaggedField {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               // Annoyingly, when we move to explicit types, we will need an
+               // explicit enum holding the two iterator variants.
                match *self {
-                       RawTaggedField::UnknownSemantics(ref content) => {
-                               writer.write(content)
-                       },
-                       RawTaggedField::KnownSemantics(ref tagged_field) => {
-                               tagged_field.write_base32(writer)
-                       }
+                       RawTaggedField::UnknownSemantics(ref content) => Box::new(content.iter().copied()),
+                       RawTaggedField::KnownSemantics(ref tagged_field) => tagged_field.fe_iter(),
                }
        }
 }
 
-impl ToBase32 for Sha256 {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               (&self.0[..]).write_base32(writer)
+impl Base32Iterable for Sha256 {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(self.0[..].fe_iter())
        }
 }
+
 impl Base32Len for Sha256 {
        fn base32_len(&self) -> usize {
-               (&self.0[..]).base32_len()
+               self.0[..].base32_len()
        }
 }
 
-impl ToBase32 for Description {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               self.0.0.as_bytes().write_base32(writer)
+impl Base32Iterable for Description {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(self.0.0.as_bytes().fe_iter())
        }
 }
 
@@ -289,9 +291,9 @@ impl Base32Len for Description {
        }
 }
 
-impl ToBase32 for PayeePubKey {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               (&self.serialize()[..]).write_base32(writer)
+impl Base32Iterable for PayeePubKey {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(self.serialize().into_iter().bytes_to_fes())
        }
 }
 
@@ -301,9 +303,9 @@ impl Base32Len for PayeePubKey {
        }
 }
 
-impl ToBase32 for ExpiryTime {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               writer.write(&encode_int_be_base32(self.as_seconds()))
+impl Base32Iterable for ExpiryTime {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(encode_int_be_base32(self.as_seconds()).into_iter())
        }
 }
 
@@ -313,9 +315,9 @@ impl Base32Len for ExpiryTime {
        }
 }
 
-impl ToBase32 for MinFinalCltvExpiryDelta {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               writer.write(&encode_int_be_base32(self.0))
+impl Base32Iterable for MinFinalCltvExpiryDelta {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(encode_int_be_base32(self.0).into_iter())
        }
 }
 
@@ -325,71 +327,60 @@ impl Base32Len for MinFinalCltvExpiryDelta {
        }
 }
 
-impl ToBase32 for Fallback {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               match *self {
-                       Fallback::SegWitProgram {version: v, program: ref p} => {
-                               writer.write_u5(u5::try_from_u8(v.to_num()).expect("witness version <= 16"))?;
-                               p.write_base32(writer)
+impl Base32Iterable for Fallback {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               Box::new(match *self {
+                       Fallback::SegWitProgram { version: v, program: ref p } => {
+                               let v = Fe32::try_from(v.to_num()).expect("valid version");
+                               core::iter::once(v).chain(p[..].fe_iter())
                        },
                        Fallback::PubKeyHash(ref hash) => {
-                               writer.write_u5(u5::try_from_u8(17).expect("17 < 32"))?;
-                               (&hash[..]).write_base32(writer)
+                               // 17 '3'
+                               core::iter::once(Fe32::_3).chain(hash[..].fe_iter())
                        },
                        Fallback::ScriptHash(ref hash) => {
-                               writer.write_u5(u5::try_from_u8(18).expect("18 < 32"))?;
-                               (&hash[..]).write_base32(writer)
-                       }
-               }
+                               // 18 'J'
+                               core::iter::once(Fe32::J).chain(hash[..].fe_iter())
+                       },
+               })
        }
 }
 
 impl Base32Len for Fallback {
        fn base32_len(&self) -> usize {
                match *self {
-                       Fallback::SegWitProgram {program: ref p, ..} => {
+                       Fallback::SegWitProgram { program: ref p, .. } => {
                                bytes_size_to_base32_size(p.len()) + 1
                        },
-                       Fallback::PubKeyHash(_) | Fallback::ScriptHash(_) => {
-                               33
-                       },
+                       Fallback::PubKeyHash(_) | Fallback::ScriptHash(_) => 33,
                }
        }
 }
 
-impl ToBase32 for PrivateRoute {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               let mut converter = BytesToBase32::new(writer);
-
-               for hop in (self.0).0.iter() {
-                       converter.append(&hop.src_node_id.serialize()[..])?;
-                       let short_channel_id = try_stretch(
-                               encode_int_be_base256(hop.short_channel_id),
-                               8
-                       ).expect("sizeof(u64) == 8");
-                       converter.append(&short_channel_id)?;
-
-                       let fee_base_msat = try_stretch(
-                               encode_int_be_base256(hop.fees.base_msat),
-                               4
-                       ).expect("sizeof(u32) == 4");
-                       converter.append(&fee_base_msat)?;
-
-                       let fee_proportional_millionths = try_stretch(
-                               encode_int_be_base256(hop.fees.proportional_millionths),
-                               4
-                       ).expect("sizeof(u32) == 4");
-                       converter.append(&fee_proportional_millionths)?;
-
-                       let cltv_expiry_delta = try_stretch(
-                               encode_int_be_base256(hop.cltv_expiry_delta),
-                               2
-                       ).expect("sizeof(u16) == 2");
-                       converter.append(&cltv_expiry_delta)?;
+// Shorthand type
+type RouteHintHopIter = iter::Chain<
+       iter::Chain<
+               iter::Chain<
+                       iter::Chain<array::IntoIter<u8, 33>, array::IntoIter<u8, 8>>,
+                       array::IntoIter<u8, 4>,
+               >,
+               array::IntoIter<u8, 4>,
+       >,
+       array::IntoIter<u8, 2>,
+>;
+
+impl Base32Iterable for PrivateRoute {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
+               fn serialize_to_iter(hop: &RouteHintHop) -> RouteHintHopIter {
+                       let i1 = hop.src_node_id.serialize().into_iter();
+                       let i2 = u64::to_be_bytes(hop.short_channel_id).into_iter();
+                       let i3 = u32::to_be_bytes(hop.fees.base_msat).into_iter();
+                       let i4 = u32::to_be_bytes(hop.fees.proportional_millionths).into_iter();
+                       let i5 = u16::to_be_bytes(hop.cltv_expiry_delta).into_iter();
+                       i1.chain(i2).chain(i3).chain(i4).chain(i5)
                }
 
-               converter.finalize()?;
-               Ok(())
+               Box::new(self.0.0.iter().map(serialize_to_iter).flatten().bytes_to_fes())
        }
 }
 
@@ -399,77 +390,84 @@ impl Base32Len for PrivateRoute {
        }
 }
 
-impl ToBase32 for TaggedField {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
+// Shorthand type
+type TaggedFieldIter<I> = core::iter::Chain<core::array::IntoIter<Fe32, 3>, I>;
+
+impl Base32Iterable for TaggedField {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
                /// Writes a tagged field: tag, length and data. `tag` should be in `0..32` otherwise the
                /// function will panic.
-               fn write_tagged_field<W, P>(writer: &mut W, tag: u8, payload: &P) -> Result<(), W::Err>
-                       where W: WriteBase32,
-                                 P: ToBase32 + Base32Len,
+               fn write_tagged_field<'s, P>(
+                       tag: u8, payload: &'s P,
+               ) -> TaggedFieldIter<Box<dyn Iterator<Item = Fe32> + 's>>
+               where
+                       P: Base32Iterable + Base32Len + ?Sized,
                {
                        let len = payload.base32_len();
                        assert!(len < 1024, "Every tagged field data can be at most 1023 bytes long.");
 
-                       writer.write_u5(u5::try_from_u8(tag).expect("invalid tag, not in 0..32"))?;
-                       writer.write(&try_stretch(
-                               encode_int_be_base32(len as u64),
-                               2
-                       ).expect("Can't be longer than 2, see assert above."))?;
-                       payload.write_base32(writer)
+                       [
+                               Fe32::try_from(tag).expect("invalid tag, not in 0..32"),
+                               Fe32::try_from((len / 32) as u8).expect("< 32"),
+                               Fe32::try_from((len % 32) as u8).expect("< 32"),
+                       ]
+                       .into_iter()
+                       .chain(payload.fe_iter())
                }
 
-               match *self {
+               // we will also need a giant enum for this
+               Box::new(match *self {
                        TaggedField::PaymentHash(ref hash) => {
-                               write_tagged_field(writer, constants::TAG_PAYMENT_HASH, hash)
+                               write_tagged_field(constants::TAG_PAYMENT_HASH, hash)
                        },
                        TaggedField::Description(ref description) => {
-                               write_tagged_field(writer, constants::TAG_DESCRIPTION, description)
+                               write_tagged_field(constants::TAG_DESCRIPTION, description)
                        },
                        TaggedField::PayeePubKey(ref pub_key) => {
-                               write_tagged_field(writer, constants::TAG_PAYEE_PUB_KEY, pub_key)
+                               write_tagged_field(constants::TAG_PAYEE_PUB_KEY, pub_key)
                        },
                        TaggedField::DescriptionHash(ref hash) => {
-                               write_tagged_field(writer, constants::TAG_DESCRIPTION_HASH, hash)
+                               write_tagged_field(constants::TAG_DESCRIPTION_HASH, hash)
                        },
                        TaggedField::ExpiryTime(ref duration) => {
-                               write_tagged_field(writer, constants::TAG_EXPIRY_TIME, duration)
+                               write_tagged_field(constants::TAG_EXPIRY_TIME, duration)
                        },
                        TaggedField::MinFinalCltvExpiryDelta(ref expiry) => {
-                               write_tagged_field(writer, constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA, expiry)
+                               write_tagged_field(constants::TAG_MIN_FINAL_CLTV_EXPIRY_DELTA, expiry)
                        },
                        TaggedField::Fallback(ref fallback_address) => {
-                               write_tagged_field(writer, constants::TAG_FALLBACK, fallback_address)
+                               write_tagged_field(constants::TAG_FALLBACK, fallback_address)
                        },
                        TaggedField::PrivateRoute(ref route_hops) => {
-                               write_tagged_field(writer, constants::TAG_PRIVATE_ROUTE, route_hops)
+                               write_tagged_field(constants::TAG_PRIVATE_ROUTE, route_hops)
                        },
                        TaggedField::PaymentSecret(ref payment_secret) => {
-                                 write_tagged_field(writer, constants::TAG_PAYMENT_SECRET, payment_secret)
+                               write_tagged_field(constants::TAG_PAYMENT_SECRET, payment_secret)
                        },
                        TaggedField::PaymentMetadata(ref payment_metadata) => {
-                                 write_tagged_field(writer, constants::TAG_PAYMENT_METADATA, payment_metadata)
+                               write_tagged_field(constants::TAG_PAYMENT_METADATA, payment_metadata)
                        },
                        TaggedField::Features(ref features) => {
-                               write_tagged_field(writer, constants::TAG_FEATURES, features)
+                               write_tagged_field(constants::TAG_FEATURES, features)
                        },
-               }
+               })
        }
 }
 
-impl ToBase32 for Bolt11InvoiceSignature {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               let mut converter = BytesToBase32::new(writer);
+impl Base32Iterable for Bolt11InvoiceSignature {
+       fn fe_iter<'s>(&'s self) -> Box<dyn Iterator<Item = Fe32> + 's> {
                let (recovery_id, signature) = self.0.serialize_compact();
-               converter.append(&signature[..])?;
-               converter.append_u8(recovery_id.to_i32() as u8)?;
-               converter.finalize()
+               Box::new(
+                       signature
+                               .into_iter()
+                               .chain(core::iter::once(recovery_id.to_i32() as u8))
+                               .bytes_to_fes(),
+               )
        }
 }
 
 #[cfg(test)]
 mod test {
-       use bech32::CheckBase32;
-
        #[test]
        fn test_currency_code() {
                use crate::Currency;
@@ -497,20 +495,15 @@ mod test {
        #[test]
        fn test_encode_int_be_base32() {
                use crate::ser::encode_int_be_base32;
+               use bech32::Fe32;
 
                let input: u64 = 33764;
-               let expected_out = CheckBase32::check_base32(&[1, 0, 31, 4]).unwrap();
+               let expected_out = [1, 0, 31, 4]
+                       .iter()
+                       .copied()
+                       .map(|v| Fe32::try_from(v).expect("<= 31"))
+                       .collect::<Vec<Fe32>>();
 
                assert_eq!(expected_out, encode_int_be_base32(input));
        }
-
-       #[test]
-       fn test_encode_int_be_base256() {
-               use crate::ser::encode_int_be_base256;
-
-               let input: u64 = 16842530;
-               let expected_out = vec![1, 0, 255, 34];
-
-               assert_eq!(expected_out, encode_int_be_base256(input));
-       }
 }
index 7051924ee3c306b1e7a1859f454588ae2b687024..92b6c778d6c3aa61d4de3965eb5ddd28e1e72352 100644 (file)
@@ -1,18 +1,17 @@
-use crate::{
-       sha256, FromBase32, PayeePubKey, PaymentSecret, PositiveTimestamp, RawDataPart, Sha256,
-};
-use bech32::{u5, Base32Len, ToBase32};
-
+use crate::de::FromBase32;
+use crate::ser::{Base32Iterable, Base32Len};
+use crate::{sha256, PayeePubKey, PaymentSecret, PositiveTimestamp, RawDataPart, Sha256};
+use bech32::Fe32;
 use core::fmt::Debug;
 use std::str::FromStr;
 
 /// Test base32 encode and decode
 fn ser_de_test<T>(o: T, expected_str: &str)
 where
-       T: ToBase32 + FromBase32 + Eq + Debug,
+       T: Base32Iterable + FromBase32 + Eq + Debug,
        T::Err: Debug,
 {
-       let serialized_32 = o.to_base32();
+       let serialized_32 = o.fe_iter().collect::<Vec<Fe32>>();
        let serialized_str = serialized_32.iter().map(|f| f.to_char()).collect::<String>();
        assert_eq!(serialized_str, expected_str, "Mismatch for {:?}", o);
 
@@ -24,7 +23,7 @@ where
 /// Test base32 encode and decode, and also length hint
 fn ser_de_test_len<T>(o: T, expected_str: &str)
 where
-       T: ToBase32 + FromBase32 + Base32Len + Eq + Debug,
+       T: Base32Iterable + Base32Len + FromBase32 + Eq + Debug,
        T::Err: Debug,
 {
        assert_eq!(o.base32_len(), expected_str.len(), "Mismatch for {} {:?}", expected_str, o);
@@ -49,6 +48,50 @@ fn vec_u8() {
        ser_de_test_len(vec![255, 254, 253, 252, 251, 250, 249, 248, 247, 246], "lll0ml8mltul3alk");
 }
 
+#[test]
+fn array_u8() {
+       ser_de_test_len([0], "qq");
+       ser_de_test_len([255], "lu");
+       ser_de_test_len([0, 1], "qqqs");
+       ser_de_test_len([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "qqqsyqcyq5rqwzqf");
+       ser_de_test_len([255, 254, 253, 252, 251, 250, 249, 248, 247, 246], "lll0ml8mltul3alk");
+}
+
+#[test]
+fn array_u8_error_invalid_length() {
+       // correct len -- 5 fe32 -> 3 bytes
+       assert_eq!(
+               <[u8; 3]>::from_base32(
+                       &"pqqql".to_string().chars().map(|c| Fe32::from_char(c).unwrap()).collect::<Vec<_>>()[..]
+               )
+               .unwrap(),
+               [8, 0, 15]
+       );
+
+       // input too short
+       assert_eq!(
+               <[u8; 3]>::from_base32(
+                       &"pqql".to_string().chars().map(|c| Fe32::from_char(c).unwrap()).collect::<Vec<_>>()[..]
+               )
+               .err()
+               .unwrap()
+               .to_string(),
+               "Slice had length 2 instead of 3 for element <[u8; N]>"
+       );
+
+       // input too long
+       assert_eq!(
+               <[u8; 3]>::from_base32(
+                       &"pqqqqql".to_string().chars().map(|c| Fe32::from_char(c).unwrap()).collect::<Vec<_>>()
+                               [..]
+               )
+               .err()
+               .unwrap()
+               .to_string(),
+               "Slice had length 4 instead of 3 for element <[u8; N]>"
+       );
+}
+
 #[test]
 fn payment_secret() {
        let payment_secret = PaymentSecret([7; 32]);
@@ -111,8 +154,8 @@ fn bolt11_invoice_features() {
        // To test skipping 0's in deserialization, we have to start with deserialization
        assert_eq!(
                Bolt11InvoiceFeatures::from_base32(
-                       &vec![0, 0, 0, 0, 0, 3, 4]
-                               .iter().copied().map(|f| u5::try_from_u8(f).unwrap()).collect::<Vec<_>>()[..]
+                       &"qqqqqry".to_string().chars().map(|c| Fe32::from_char(c).unwrap()).collect::<Vec<_>>()
+                               [..]
                )
                .unwrap()
                .le_flags(),
@@ -120,8 +163,7 @@ fn bolt11_invoice_features() {
        );
        assert_eq!(
                Bolt11InvoiceFeatures::from_base32(
-                       &vec![3, 4]
-                               .iter().copied().map(|f| u5::try_from_u8(f).unwrap()).collect::<Vec<_>>()[..]
+                       &"ry".to_string().chars().map(|c| Fe32::from_char(c).unwrap()).collect::<Vec<_>>()[..]
                )
                .unwrap()
                .le_flags(),
index 68404817ff67965cbd68d45daf46a3db28ef1813..290b8ce660768a84c57639d2928d24c2a3677a5b 100644 (file)
@@ -410,19 +410,21 @@ fn invoice_deserialize() {
 
 #[test]
 fn test_bolt_invalid_invoices() {
+       use bech32::primitives::decode::{CharError, ChecksumError, CheckedHrpstringError, UncheckedHrpstringError};
+
        // Tests the BOLT 11 invalid invoice test vectors
        assert_eq!(Bolt11Invoice::from_str(
                "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqtqyx5vggfcsll4wu246hz02kp85x4katwsk9639we5n5yngc3yhqkm35jnjw4len8vrnqnf5ejh0mzj9n3vz2px97evektfm2l6wqccp3y7372"
                ), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidFeatures)));
        assert_eq!(Bolt11Invoice::from_str(
                "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt"
-               ), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::InvalidChecksum))));
+               ), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(CheckedHrpstringError::Checksum(ChecksumError::InvalidResidue)))));
        assert_eq!(Bolt11Invoice::from_str(
                "pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
-               ), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MissingSeparator))));
+               ), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::MissingSeparator))))));
        assert_eq!(Bolt11Invoice::from_str(
                "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
-               ), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(bech32::Error::MixedCase))));
+               ), Err(ParseOrSemanticError::ParseError(Bolt11ParseError::Bech32Error(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::MixedCase))))));
        assert_eq!(Bolt11Invoice::from_str(
                "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2"
                ), Err(ParseOrSemanticError::SemanticError(Bolt11SemanticError::InvalidSignature)));
index f3eeec0f1a9aa1396487cb13f808ade3b2df7f22..768b8fb3d90bc41591a28a2670da575e8a1b2783 100644 (file)
@@ -17,7 +17,6 @@ _test_utils = []
 
 [dependencies]
 bitcoin = { version = "0.32.2", default-features = false }
-bech32 = { version = "0.9", default-features = false }
 
 [lints]
 workspace = true
index 7ce87e0263ec30cb71f1eeb699d80a273308b581..418644593bf3115c06ae3a7fb1143731d2bf14ea 100644 (file)
@@ -82,11 +82,8 @@ use core::hash::{Hash, Hasher};
 use core::marker::PhantomData;
 use core::{cmp, fmt};
 
-use alloc::vec;
 use alloc::vec::Vec;
 
-use bech32::{u5, Base32Len, FromBase32, ToBase32, WriteBase32};
-
 mod sealed {
        use super::Features;
 
@@ -785,73 +782,6 @@ impl ChannelTypeFeatures {
        }
 }
 
-impl ToBase32 for Bolt11InvoiceFeatures {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               // Explanation for the "4": the normal way to round up when dividing is to add the divisor
-               // minus one before dividing
-               let length_u5s = (self.flags.len() * 8 + 4) / 5 as usize;
-               let mut res_u5s: Vec<u5> = vec![u5::try_from_u8(0).unwrap(); length_u5s];
-               for (byte_idx, byte) in self.flags.iter().enumerate() {
-                       let bit_pos_from_left_0_indexed = byte_idx * 8;
-                       let new_u5_idx = length_u5s - (bit_pos_from_left_0_indexed / 5) as usize - 1;
-                       let new_bit_pos = bit_pos_from_left_0_indexed % 5;
-                       let shifted_chunk_u16 = (*byte as u16) << new_bit_pos;
-                       let curr_u5_as_u8 = res_u5s[new_u5_idx].to_u8();
-                       res_u5s[new_u5_idx] =
-                               u5::try_from_u8(curr_u5_as_u8 | ((shifted_chunk_u16 & 0x001f) as u8)).unwrap();
-                       if new_u5_idx > 0 {
-                               let curr_u5_as_u8 = res_u5s[new_u5_idx - 1].to_u8();
-                               res_u5s[new_u5_idx - 1] =
-                                       u5::try_from_u8(curr_u5_as_u8 | (((shifted_chunk_u16 >> 5) & 0x001f) as u8))
-                                               .unwrap();
-                       }
-                       if new_u5_idx > 1 {
-                               let curr_u5_as_u8 = res_u5s[new_u5_idx - 2].to_u8();
-                               res_u5s[new_u5_idx - 2] =
-                                       u5::try_from_u8(curr_u5_as_u8 | (((shifted_chunk_u16 >> 10) & 0x001f) as u8))
-                                               .unwrap();
-                       }
-               }
-               // Trim the highest feature bits.
-               while !res_u5s.is_empty() && res_u5s[0] == u5::try_from_u8(0).unwrap() {
-                       res_u5s.remove(0);
-               }
-               writer.write(&res_u5s)
-       }
-}
-
-impl Base32Len for Bolt11InvoiceFeatures {
-       fn base32_len(&self) -> usize {
-               self.to_base32().len()
-       }
-}
-
-impl FromBase32 for Bolt11InvoiceFeatures {
-       type Err = bech32::Error;
-
-       fn from_base32(field_data: &[u5]) -> Result<Bolt11InvoiceFeatures, bech32::Error> {
-               // Explanation for the "7": the normal way to round up when dividing is to add the divisor
-               // minus one before dividing
-               let length_bytes = (field_data.len() * 5 + 7) / 8 as usize;
-               let mut res_bytes: Vec<u8> = vec![0; length_bytes];
-               for (u5_idx, chunk) in field_data.iter().enumerate() {
-                       let bit_pos_from_right_0_indexed = (field_data.len() - u5_idx - 1) * 5;
-                       let new_byte_idx = (bit_pos_from_right_0_indexed / 8) as usize;
-                       let new_bit_pos = bit_pos_from_right_0_indexed % 8;
-                       let chunk_u16 = chunk.to_u8() as u16;
-                       res_bytes[new_byte_idx] |= ((chunk_u16 << new_bit_pos) & 0xff) as u8;
-                       if new_byte_idx != length_bytes - 1 {
-                               res_bytes[new_byte_idx + 1] |= ((chunk_u16 >> (8 - new_bit_pos)) & 0xff) as u8;
-                       }
-               }
-               // Trim the highest feature bits.
-               while !res_bytes.is_empty() && res_bytes[res_bytes.len() - 1] == 0 {
-                       res_bytes.pop();
-               }
-               Ok(Bolt11InvoiceFeatures::from_le_bytes(res_bytes))
-       }
-}
-
 impl<T: sealed::Context> Features<T> {
        /// Create a blank Features with no features set
        pub fn empty() -> Self {
@@ -1303,37 +1233,6 @@ mod tests {
                assert_eq!(features.flags[32], 0b00000101);
        }
 
-       #[test]
-       fn invoice_features_encoding() {
-               let features_as_u5s = vec![
-                       u5::try_from_u8(6).unwrap(),
-                       u5::try_from_u8(10).unwrap(),
-                       u5::try_from_u8(25).unwrap(),
-                       u5::try_from_u8(1).unwrap(),
-                       u5::try_from_u8(10).unwrap(),
-                       u5::try_from_u8(0).unwrap(),
-                       u5::try_from_u8(20).unwrap(),
-                       u5::try_from_u8(2).unwrap(),
-                       u5::try_from_u8(0).unwrap(),
-                       u5::try_from_u8(6).unwrap(),
-                       u5::try_from_u8(0).unwrap(),
-                       u5::try_from_u8(16).unwrap(),
-                       u5::try_from_u8(1).unwrap(),
-               ];
-               let features = Bolt11InvoiceFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]);
-
-               // Test length calculation.
-               assert_eq!(features.base32_len(), 13);
-
-               // Test serialization.
-               let features_serialized = features.to_base32();
-               assert_eq!(features_as_u5s, features_serialized);
-
-               // Test deserialization.
-               let features_deserialized = Bolt11InvoiceFeatures::from_base32(&features_as_u5s).unwrap();
-               assert_eq!(features, features_deserialized);
-       }
-
        #[test]
        fn test_channel_type_mapping() {
                // If we map an Bolt11InvoiceFeatures with StaticRemoteKey optional, it should map into a
index a2832c6b559adf0222589abb1d192c734e9225d3..0f0fcf7b51686a3aedc0494b8d0d6a6a546101a5 100644 (file)
@@ -9,8 +9,6 @@
 
 //! Types which describe payments in lightning.
 
-use alloc::vec::Vec;
-
 use core::borrow::Borrow;
 
 use bitcoin::hashes::{sha256::Hash as Sha256, Hash as _};
@@ -79,32 +77,3 @@ impl_fmt_traits! {
                const LENGTH: usize = 32;
        }
 }
-
-use bech32::{u5, Base32Len, FromBase32, ToBase32, WriteBase32};
-
-impl FromBase32 for PaymentSecret {
-       type Err = bech32::Error;
-
-       fn from_base32(field_data: &[u5]) -> Result<PaymentSecret, bech32::Error> {
-               if field_data.len() != 52 {
-                       return Err(bech32::Error::InvalidLength);
-               } else {
-                       let data_bytes = Vec::<u8>::from_base32(field_data)?;
-                       let mut payment_secret = [0; 32];
-                       payment_secret.copy_from_slice(&data_bytes);
-                       Ok(PaymentSecret(payment_secret))
-               }
-       }
-}
-
-impl ToBase32 for PaymentSecret {
-       fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
-               (&self.0[..]).write_base32(writer)
-       }
-}
-
-impl Base32Len for PaymentSecret {
-       fn base32_len(&self) -> usize {
-               52
-       }
-}
index 3a1939733a6bca92b6f9dd2a6d59bcf349516741..e58ea74cbdddd6cce44a2d754c94e3e1f113f675 100644 (file)
@@ -40,7 +40,7 @@ default = ["std", "grind_signatures"]
 lightning-types = { version = "0.1.0", path = "../lightning-types", default-features = false }
 lightning-invoice = { version = "0.32.0", path = "../lightning-invoice", default-features = false }
 
-bech32 = { version = "0.9.1", default-features = false }
+bech32 = { version = "0.11.0", default-features = false }
 bitcoin = { version = "0.32.2", default-features = false, features = ["secp-recovery"] }
 
 hashbrown = { version = "0.13", default-features = false }
index 24a7848072401d4ce14c9ab4fe086bcaf18492fc..a46e90de6be5f71f88f637820ff91c2187ae6400 100644 (file)
@@ -13,6 +13,7 @@ use bitcoin::secp256k1;
 use crate::io;
 use crate::ln::msgs::DecodeError;
 use crate::util::ser::CursorReadable;
+use bech32::primitives::decode::CheckedHrpstringError;
 
 #[allow(unused_imports)]
 use crate::prelude::*;
@@ -24,7 +25,8 @@ pub(super) use sealed::Bech32Encode;
 pub use sealed::Bech32Encode;
 
 mod sealed {
-       use bech32::{FromBase32, ToBase32};
+       use bech32::{EncodeError, Hrp, NoChecksum, encode_to_fmt};
+       use bech32::primitives::decode::CheckedHrpstring;
        use core::fmt;
        use super::Bolt12ParseError;
 
@@ -54,22 +56,23 @@ mod sealed {
                                None => Bech32String::Borrowed(s),
                        };
 
-                       let (hrp, data) = bech32::decode_without_checksum(encoded.as_ref())?;
-
-                       if hrp != Self::BECH32_HRP {
+                       let parsed = CheckedHrpstring::new::<NoChecksum>(encoded.as_ref())?;
+                       let hrp = parsed.hrp();
+                       if hrp.to_string() != Self::BECH32_HRP {
                                return Err(Bolt12ParseError::InvalidBech32Hrp);
                        }
 
-                       let data = Vec::<u8>::from_base32(&data)?;
+                       let data = parsed.byte_iter().collect::<Vec<u8>>();
                        Self::try_from(data)
                }
 
                /// Formats the message using bech32-encoding.
                fn fmt_bech32_str(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-                       bech32::encode_without_checksum_to_fmt(f, Self::BECH32_HRP, self.as_ref().to_base32())
-                               .expect("HRP is invalid").unwrap();
-
-                       Ok(())
+                       encode_to_fmt::<NoChecksum, _>(f, Hrp::parse(Self::BECH32_HRP).unwrap(), self.as_ref())
+                               .map_err(|e| match e {
+                                       EncodeError::Fmt(e) => e,
+                                       _ => fmt::Error {},
+                               })
                }
        }
 
@@ -123,7 +126,7 @@ pub enum Bolt12ParseError {
        /// being parsed.
        InvalidBech32Hrp,
        /// The string could not be bech32 decoded.
-       Bech32(bech32::Error),
+       Bech32(CheckedHrpstringError),
        /// The bech32 decoded string could not be decoded as the expected message type.
        Decode(DecodeError),
        /// The parsed message has invalid semantics.
@@ -197,8 +200,8 @@ pub enum Bolt12SemanticError {
        MissingSignature,
 }
 
-impl From<bech32::Error> for Bolt12ParseError {
-       fn from(error: bech32::Error) -> Self {
+impl From<CheckedHrpstringError> for Bolt12ParseError {
+       fn from(error: CheckedHrpstringError) -> Self {
                Self::Bech32(error)
        }
 }
@@ -281,6 +284,7 @@ mod tests {
        use super::Bolt12ParseError;
        use crate::ln::msgs::DecodeError;
        use crate::offers::offer::Offer;
+       use bech32::primitives::decode::{CharError, CheckedHrpstringError, UncheckedHrpstringError};
 
        #[test]
        fn fails_parsing_bech32_encoded_offer_with_invalid_hrp() {
@@ -296,7 +300,7 @@ mod tests {
                let encoded_offer = "lno1pqps7sjqpgtyzm3qv4uxzmtsd3jjqer9wd3hy6tsw35k7msjzfpy7nz5yqcnygrfdej82um5wf5k2uckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxo";
                match encoded_offer.parse::<Offer>() {
                        Ok(_) => panic!("Valid offer: {}", encoded_offer),
-                       Err(e) => assert_eq!(e, Bolt12ParseError::Bech32(bech32::Error::InvalidChar('o'))),
+                       Err(e) => assert_eq!(e, Bolt12ParseError::Bech32(CheckedHrpstringError::Parse(UncheckedHrpstringError::Char(CharError::InvalidChar('o'))))),
                }
        }