]> Untitled Git - bdk/commitdiff
bdk_wallet: don't reimplement checksum algorithm, use rust-miniscript
authorAntoine Poinsot <darosior@protonmail.com>
Thu, 25 Jul 2024 14:47:03 +0000 (16:47 +0200)
committerAntoine Poinsot <darosior@protonmail.com>
Sat, 27 Jul 2024 15:56:02 +0000 (17:56 +0200)
crates/wallet/src/descriptor/checksum.rs

index 3a16c3e88cea7ac64b2832b12f0203f073cdd363..7b770910c7a750188bc2fa2c113d0586890e1214 100644 (file)
 use crate::descriptor::DescriptorError;
 use alloc::string::String;
 
-const INPUT_CHARSET: &[u8] = b"0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
-const CHECKSUM_CHARSET: &[u8] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
+use miniscript::descriptor::checksum::desc_checksum;
 
-fn poly_mod(mut c: u64, val: u64) -> u64 {
-    let c0 = c >> 35;
-    c = ((c & 0x7ffffffff) << 5) ^ val;
-    if c0 & 1 > 0 {
-        c ^= 0xf5dee51989
-    };
-    if c0 & 2 > 0 {
-        c ^= 0xa9fdca3312
-    };
-    if c0 & 4 > 0 {
-        c ^= 0x1bab10e32d
-    };
-    if c0 & 8 > 0 {
-        c ^= 0x3706b1677a
-    };
-    if c0 & 16 > 0 {
-        c ^= 0x644d626ffd
-    };
-
-    c
-}
-
-/// Compute the checksum bytes of a descriptor, excludes any existing checksum in the descriptor string from the calculation
-fn calc_checksum_bytes(mut desc: &str) -> Result<[u8; 8], DescriptorError> {
-    let mut c = 1;
-    let mut cls = 0;
-    let mut clscount = 0;
-
-    let mut original_checksum = None;
+/// Compute the checksum of a descriptor, excludes any existing checksum in the descriptor string from the calculation
+pub fn calc_checksum(desc: &str) -> Result<String, DescriptorError> {
     if let Some(split) = desc.split_once('#') {
-        desc = split.0;
-        original_checksum = Some(split.1);
-    }
-
-    for ch in desc.as_bytes() {
-        let pos = INPUT_CHARSET
-            .iter()
-            .position(|b| b == ch)
-            .ok_or(DescriptorError::InvalidDescriptorCharacter(*ch))? as u64;
-        c = poly_mod(c, pos & 31);
-        cls = cls * 3 + (pos >> 5);
-        clscount += 1;
-        if clscount == 3 {
-            c = poly_mod(c, cls);
-            cls = 0;
-            clscount = 0;
-        }
-    }
-    if clscount > 0 {
-        c = poly_mod(c, cls);
-    }
-    (0..8).for_each(|_| c = poly_mod(c, 0));
-    c ^= 1;
-
-    let mut checksum = [0_u8; 8];
-    for j in 0..8 {
-        checksum[j] = CHECKSUM_CHARSET[((c >> (5 * (7 - j))) & 31) as usize];
-    }
-
-    // if input data already had a checksum, check calculated checksum against original checksum
-    if let Some(original_checksum) = original_checksum {
-        if original_checksum.as_bytes() != checksum {
+        let og_checksum = split.1;
+        let checksum = desc_checksum(split.0)?;
+        if og_checksum != checksum {
             return Err(DescriptorError::InvalidDescriptorChecksum);
         }
+        Ok(checksum)
+    } else {
+        Ok(desc_checksum(desc)?)
     }
-
-    Ok(checksum)
-}
-
-/// Compute the checksum of a descriptor, excludes any existing checksum in the descriptor string from the calculation
-pub fn calc_checksum(desc: &str) -> Result<String, DescriptorError> {
-    // unsafe is okay here as the checksum only uses bytes in `CHECKSUM_CHARSET`
-    calc_checksum_bytes(desc).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
 }
 
 #[cfg(test)]
@@ -141,7 +79,7 @@ mod test {
 
         assert_matches!(
             calc_checksum(&invalid_desc),
-            Err(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart.as_bytes()[0]
+            Err(DescriptorError::Miniscript(miniscript::Error::BadDescriptor(e))) if e == format!("Invalid character in checksum: '{}'", sparkle_heart)
         );
     }
 }