c
}
-/// Computes the checksum bytes of a descriptor
-pub fn get_checksum_bytes(desc: &str) -> Result<[u8; 8], DescriptorError> {
+/// Computes the checksum bytes of a descriptor.
+/// `exclude_hash = true` ignores all data after the first '#' (inclusive).
+pub fn get_checksum_bytes(mut desc: &str, exclude_hash: bool) -> Result<[u8; 8], DescriptorError> {
let mut c = 1;
let mut cls = 0;
let mut clscount = 0;
+ let mut original_checksum = None;
+ if exclude_hash {
+ 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()
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 {
+ return Err(DescriptorError::InvalidDescriptorChecksum);
+ }
+ }
+
Ok(checksum)
}
/// Compute the checksum of a descriptor
pub fn get_checksum(desc: &str) -> Result<String, DescriptorError> {
// unsafe is okay here as the checksum only uses bytes in `CHECKSUM_CHARSET`
- get_checksum_bytes(desc).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
+ get_checksum_bytes(desc, true).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
}
#[cfg(test)]
pub mod template;
pub use self::checksum::get_checksum;
+use self::checksum::get_checksum_bytes;
pub use self::derived::{AsDerived, DerivedDescriptorKey};
pub use self::error::Error as DescriptorError;
pub use self::policy::Policy;
secp: &SecpCtx,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
- let descriptor = if self.contains('#') {
- let parts: Vec<&str> = self.splitn(2, '#').collect();
- if !get_checksum(parts[0])
- .ok()
- .map(|computed| computed == parts[1])
- .unwrap_or(false)
- {
- return Err(DescriptorError::InvalidDescriptorChecksum);
+ let descriptor = match self.split_once('#') {
+ Some((desc, original_checksum)) => {
+ let checksum = get_checksum_bytes(desc, false)?;
+ if original_checksum.as_bytes() != &checksum {
+ return Err(DescriptorError::InvalidDescriptorChecksum);
+ }
+ desc
}
-
- parts[0]
- } else {
- self
+ None => self,
};
ExtendedDescriptor::parse_descriptor(secp, descriptor)?
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let checksum = wallet.descriptor_checksum(KeychainKind::External);
assert_eq!(checksum.len(), 8);
-
- let raw_descriptor = wallet
- .descriptor
- .to_string()
- .split_once('#')
- .unwrap()
- .0
- .to_string();
- assert_eq!(get_checksum(&raw_descriptor).unwrap(), checksum);
+ assert_eq!(
+ get_checksum(&wallet.descriptor.to_string()).unwrap(),
+ checksum
+ );
}
#[test]