Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 10 additions & 72 deletions crates/wallet/src/descriptor/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,82 +17,20 @@
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
pub 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who is the original gansta we are referring to here? 😆

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, i meant "original" of course. :)

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)]
Expand Down Expand Up @@ -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)
);
}
}
5 changes: 2 additions & 3 deletions crates/wallet/src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ pub mod policy;
pub mod template;

pub use self::checksum::calc_checksum;
use self::checksum::calc_checksum_bytes;
pub use self::error::Error as DescriptorError;
pub use self::policy::Policy;
use self::template::DescriptorTemplateOut;
Expand Down Expand Up @@ -88,8 +87,8 @@ impl IntoWalletDescriptor for &str {
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
let descriptor = match self.split_once('#') {
Some((desc, original_checksum)) => {
let checksum = calc_checksum_bytes(desc)?;
if original_checksum.as_bytes() != checksum {
let checksum = calc_checksum(desc)?;
if original_checksum != checksum {
return Err(DescriptorError::InvalidDescriptorChecksum);
}
desc
Expand Down
8 changes: 3 additions & 5 deletions crates/wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ use utils::{check_nsequence_rbf, After, Older, SecpCtx};

use crate::descriptor::policy::BuildSatisfaction;
use crate::descriptor::{
self, calc_checksum, DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy,
self, DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy,
IntoWalletDescriptor, Policy, XKeyUtils,
};
use crate::psbt::PsbtUtils;
Expand Down Expand Up @@ -2360,15 +2360,13 @@ where
.into_wallet_descriptor(secp, network)?
.0
.to_string();
let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
let mut wallet_name = descriptor.split_once('#').unwrap().1.to_string();
if let Some(change_descriptor) = change_descriptor {
let change_descriptor = change_descriptor
.into_wallet_descriptor(secp, network)?
.0
.to_string();
wallet_name.push_str(
calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
);
wallet_name.push_str(change_descriptor.split_once('#').unwrap().1);
}

Ok(wallet_name)
Expand Down