Skip to content

Commit 6123778

Browse files
committed
Merge #1523: bdk_wallet: Don't reimplement descriptor checksum, use the one from rust-miniscript
70c7d6b bdk_wallet: remove unnecessary calls to calc_checksum (Antoine Poinsot) 8245fb7 bdk_wallet: don't reimplement checksum algorithm, use rust-miniscript (Antoine Poinsot) a11ace2 bdk_wallet: descriptor: remove unnecessary call to calc_checksum_bytes (Antoine Poinsot) Pull request description: Skimming through the BDK code today i noticed the descriptor checksum algorithm is re-implemented instead of using the implementation from rust-miniscript. Re-implementing it here is more code to review and maintain, more room for mistakes and overall less eyes over the code than centralizing a single implementation over at `rust-miniscript`. While doing this i noticed that one of the variants was completely unnecessary (`calc_checksum_bytes`), so i removed it. I realise it's breaking the API, so if we want to avoid that i can remove this part. I also added a commit to remove redundant calls to `calc_checsum`. Overall this is just a quick PoC as i noticed it i figured i'd send a PR, my bad if i missed anything! ACKs for top commit: storopoli: utACK 70c7d6b evanlinjin: ACK 70c7d6b Tree-SHA512: 89d0e582979c8260a97646abc3821a9ec1fd84b5ff1e9236efa87654bd31d5da654c9725f4e1c9871a28c9312c0cfe6e095e50742abe5fbd0490fc9a2e41c86b
2 parents 785371e + 70c7d6b commit 6123778

File tree

3 files changed

+15
-80
lines changed

3 files changed

+15
-80
lines changed

crates/wallet/src/descriptor/checksum.rs

Lines changed: 10 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -17,82 +17,20 @@
1717
use crate::descriptor::DescriptorError;
1818
use alloc::string::String;
1919

20-
const INPUT_CHARSET: &[u8] = b"0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
21-
const CHECKSUM_CHARSET: &[u8] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
20+
use miniscript::descriptor::checksum::desc_checksum;
2221

23-
fn poly_mod(mut c: u64, val: u64) -> u64 {
24-
let c0 = c >> 35;
25-
c = ((c & 0x7ffffffff) << 5) ^ val;
26-
if c0 & 1 > 0 {
27-
c ^= 0xf5dee51989
28-
};
29-
if c0 & 2 > 0 {
30-
c ^= 0xa9fdca3312
31-
};
32-
if c0 & 4 > 0 {
33-
c ^= 0x1bab10e32d
34-
};
35-
if c0 & 8 > 0 {
36-
c ^= 0x3706b1677a
37-
};
38-
if c0 & 16 > 0 {
39-
c ^= 0x644d626ffd
40-
};
41-
42-
c
43-
}
44-
45-
/// Compute the checksum bytes of a descriptor, excludes any existing checksum in the descriptor string from the calculation
46-
pub fn calc_checksum_bytes(mut desc: &str) -> Result<[u8; 8], DescriptorError> {
47-
let mut c = 1;
48-
let mut cls = 0;
49-
let mut clscount = 0;
50-
51-
let mut original_checksum = None;
22+
/// Compute the checksum of a descriptor, excludes any existing checksum in the descriptor string from the calculation
23+
pub fn calc_checksum(desc: &str) -> Result<String, DescriptorError> {
5224
if let Some(split) = desc.split_once('#') {
53-
desc = split.0;
54-
original_checksum = Some(split.1);
55-
}
56-
57-
for ch in desc.as_bytes() {
58-
let pos = INPUT_CHARSET
59-
.iter()
60-
.position(|b| b == ch)
61-
.ok_or(DescriptorError::InvalidDescriptorCharacter(*ch))? as u64;
62-
c = poly_mod(c, pos & 31);
63-
cls = cls * 3 + (pos >> 5);
64-
clscount += 1;
65-
if clscount == 3 {
66-
c = poly_mod(c, cls);
67-
cls = 0;
68-
clscount = 0;
69-
}
70-
}
71-
if clscount > 0 {
72-
c = poly_mod(c, cls);
73-
}
74-
(0..8).for_each(|_| c = poly_mod(c, 0));
75-
c ^= 1;
76-
77-
let mut checksum = [0_u8; 8];
78-
for j in 0..8 {
79-
checksum[j] = CHECKSUM_CHARSET[((c >> (5 * (7 - j))) & 31) as usize];
80-
}
81-
82-
// if input data already had a checksum, check calculated checksum against original checksum
83-
if let Some(original_checksum) = original_checksum {
84-
if original_checksum.as_bytes() != checksum {
25+
let og_checksum = split.1;
26+
let checksum = desc_checksum(split.0)?;
27+
if og_checksum != checksum {
8528
return Err(DescriptorError::InvalidDescriptorChecksum);
8629
}
30+
Ok(checksum)
31+
} else {
32+
Ok(desc_checksum(desc)?)
8733
}
88-
89-
Ok(checksum)
90-
}
91-
92-
/// Compute the checksum of a descriptor, excludes any existing checksum in the descriptor string from the calculation
93-
pub fn calc_checksum(desc: &str) -> Result<String, DescriptorError> {
94-
// unsafe is okay here as the checksum only uses bytes in `CHECKSUM_CHARSET`
95-
calc_checksum_bytes(desc).map(|b| unsafe { String::from_utf8_unchecked(b.to_vec()) })
9634
}
9735

9836
#[cfg(test)]
@@ -141,7 +79,7 @@ mod test {
14179

14280
assert_matches!(
14381
calc_checksum(&invalid_desc),
144-
Err(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart.as_bytes()[0]
82+
Err(DescriptorError::Miniscript(miniscript::Error::BadDescriptor(e))) if e == format!("Invalid character in checksum: '{}'", sparkle_heart)
14583
);
14684
}
14785
}

crates/wallet/src/descriptor/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub mod policy;
4242
pub mod template;
4343

4444
pub use self::checksum::calc_checksum;
45-
use self::checksum::calc_checksum_bytes;
4645
pub use self::error::Error as DescriptorError;
4746
pub use self::policy::Policy;
4847
use self::template::DescriptorTemplateOut;
@@ -88,8 +87,8 @@ impl IntoWalletDescriptor for &str {
8887
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
8988
let descriptor = match self.split_once('#') {
9089
Some((desc, original_checksum)) => {
91-
let checksum = calc_checksum_bytes(desc)?;
92-
if original_checksum.as_bytes() != checksum {
90+
let checksum = calc_checksum(desc)?;
91+
if original_checksum != checksum {
9392
return Err(DescriptorError::InvalidDescriptorChecksum);
9493
}
9594
desc

crates/wallet/src/wallet/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ use utils::{check_nsequence_rbf, After, Older, SecpCtx};
7979

8080
use crate::descriptor::policy::BuildSatisfaction;
8181
use crate::descriptor::{
82-
self, calc_checksum, DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy,
82+
self, DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy,
8383
IntoWalletDescriptor, Policy, XKeyUtils,
8484
};
8585
use crate::psbt::PsbtUtils;
@@ -2360,15 +2360,13 @@ where
23602360
.into_wallet_descriptor(secp, network)?
23612361
.0
23622362
.to_string();
2363-
let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
2363+
let mut wallet_name = descriptor.split_once('#').unwrap().1.to_string();
23642364
if let Some(change_descriptor) = change_descriptor {
23652365
let change_descriptor = change_descriptor
23662366
.into_wallet_descriptor(secp, network)?
23672367
.0
23682368
.to_string();
2369-
wallet_name.push_str(
2370-
calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
2371-
);
2369+
wallet_name.push_str(change_descriptor.split_once('#').unwrap().1);
23722370
}
23732371

23742372
Ok(wallet_name)

0 commit comments

Comments
 (0)