Skip to content

Commit f1349b1

Browse files
committed
Merge commit '72dab64af741383d502cb2aad8c78d3a749e6d50' into update_2022_10
72dab64 (HEAD -> master, upstream/master, origin/master, origin/HEAD) Merge rust-bitcoin/rust-miniscript#478: allow disabling the checksum with alternate `Display` Also had to fix a bunch of test cases with bare descriptors without el prefix. Add a small API to get the nested segwit descriptors without the el prefix
2 parents 8b53a6c + 72dab64 commit f1349b1

File tree

8 files changed

+276
-111
lines changed

8 files changed

+276
-111
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ rand = ["bitcoin/rand"]
1818
[dependencies]
1919
bitcoin = "0.29.1"
2020
elements = "0.21.0"
21-
bitcoin-miniscript = {package = "miniscript", git = "https://github.com/rust-bitcoin/rust-miniscript", rev = "d5615acda1a7fdc4041a11c1736af139b8c7ebe8"}
21+
bitcoin-miniscript = {package = "miniscript", git = "https://github.com/rust-bitcoin/rust-miniscript", rev = "72dab64af741383d502cb2aad8c78d3a749e6d50"}
2222

2323
# Do NOT use this as a feature! Use the `serde` feature instead.
2424
actual-serde = { package = "serde", version = "1.0", optional = true }

src/descriptor/bare.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use core::fmt;
2222

2323
use elements::{self, script, secp256k1_zkp, Script};
2424

25-
use super::checksum::{desc_checksum, verify_checksum};
25+
use super::checksum::verify_checksum;
2626
use super::ELMTS_STR;
27+
use crate::descriptor::checksum;
2728
use crate::expression::{self, FromTree};
2829
use crate::miniscript::context::ScriptContext;
2930
use crate::policy::{semantic, Liftable};
@@ -130,10 +131,11 @@ impl<Pk: MiniscriptKey> fmt::Debug for Bare<Pk> {
130131
}
131132

132133
impl<Pk: MiniscriptKey> fmt::Display for Bare<Pk> {
133-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134-
let desc = format!("{}{}", ELMTS_STR, self.ms);
135-
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
136-
write!(f, "{}#{}", &desc, &checksum)
134+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135+
use fmt::Write;
136+
let mut wrapped_f = checksum::Formatter::new(f);
137+
write!(wrapped_f, "{}{}", ELMTS_STR, self.ms)?;
138+
wrapped_f.write_checksum_if_not_alt()
137139
}
138140
}
139141

@@ -296,10 +298,11 @@ impl<Pk: MiniscriptKey> fmt::Debug for Pkh<Pk> {
296298
}
297299

298300
impl<Pk: MiniscriptKey> fmt::Display for Pkh<Pk> {
299-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300-
let desc = format!("{}pkh({})", ELMTS_STR, self.pk);
301-
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
302-
write!(f, "{}#{}", &desc, &checksum)
301+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302+
use fmt::Write;
303+
let mut wrapped_f = checksum::Formatter::new(f);
304+
write!(wrapped_f, "{}pkh({})", ELMTS_STR, self.pk)?;
305+
wrapped_f.write_checksum_if_not_alt()
303306
}
304307
}
305308

src/descriptor/blinded.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use std::fmt;
2222

2323
use elements::{self, Script};
2424

25-
use super::checksum::{desc_checksum, strip_checksum, verify_checksum};
25+
use super::checksum::verify_checksum;
2626
use super::{Descriptor, TranslatePk};
27+
use crate::descriptor::checksum;
2728
use crate::expression::{self, FromTree};
2829
use crate::extensions::{CovExtArgs, CovenantExt};
2930
use crate::policy::{semantic, Liftable};
@@ -74,10 +75,10 @@ impl<Pk: MiniscriptKey> fmt::Debug for Blinded<Pk> {
7475
impl<Pk: MiniscriptKey> fmt::Display for Blinded<Pk> {
7576
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7677
// strip thec checksum from display
77-
let desc = format!("{}", self.desc);
78-
let desc = format!("blinded({},{})", self.blinder, strip_checksum(&desc));
79-
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
80-
write!(f, "{}#{}", &desc, &checksum)
78+
use fmt::Write;
79+
let mut wrapped_f = checksum::Formatter::new(f);
80+
write!(wrapped_f, "blinded({},{:#})", self.blinder, self.desc)?;
81+
wrapped_f.write_checksum_if_not_alt()
8182
}
8283
}
8384

src/descriptor/checksum.rs

+109-41
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
44
//! checksum of a descriptor
55
6-
use std::iter::FromIterator;
6+
use core::fmt;
7+
use core::iter::FromIterator;
78

89
use crate::Error;
910

1011
const INPUT_CHARSET: &str = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
11-
const CHECKSUM_CHARSET: &str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
12+
const CHECKSUM_CHARSET: &[u8] = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l";
1213

1314
fn poly_mod(mut c: u64, val: u64) -> u64 {
1415
let c0 = c >> 35;
@@ -38,40 +39,9 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
3839
/// descriptor string is syntactically correct or not.
3940
/// This only computes the checksum
4041
pub fn desc_checksum(desc: &str) -> Result<String, Error> {
41-
let mut c = 1;
42-
let mut cls = 0;
43-
let mut clscount = 0;
44-
45-
for ch in desc.chars() {
46-
let pos = INPUT_CHARSET.find(ch).ok_or_else(|| {
47-
Error::BadDescriptor(format!("Invalid character in checksum: '{}'", ch))
48-
})? as u64;
49-
c = poly_mod(c, pos & 31);
50-
cls = cls * 3 + (pos >> 5);
51-
clscount += 1;
52-
if clscount == 3 {
53-
c = poly_mod(c, cls);
54-
cls = 0;
55-
clscount = 0;
56-
}
57-
}
58-
if clscount > 0 {
59-
c = poly_mod(c, cls);
60-
}
61-
(0..8).for_each(|_| c = poly_mod(c, 0));
62-
c ^= 1;
63-
64-
let mut chars = Vec::with_capacity(8);
65-
for j in 0..8 {
66-
chars.push(
67-
CHECKSUM_CHARSET
68-
.chars()
69-
.nth(((c >> (5 * (7 - j))) & 31) as usize)
70-
.unwrap(),
71-
);
72-
}
73-
74-
Ok(String::from_iter(chars))
42+
let mut eng = Engine::new();
43+
eng.input(desc)?;
44+
Ok(eng.checksum())
7545
}
7646

7747
/// Helper function for FromStr for various
@@ -99,12 +69,110 @@ pub(super) fn verify_checksum(s: &str) -> Result<&str, Error> {
9969
Ok(desc_str)
10070
}
10171

102-
/// Helper function to strip checksum without verifying
103-
#[allow(dead_code)]
104-
pub(super) fn strip_checksum(s: &str) -> &str {
105-
let mut parts = s.splitn(2, '#');
106-
parts.next().unwrap()
72+
/// An engine to compute a checksum from a string
73+
pub struct Engine {
74+
c: u64,
75+
cls: u64,
76+
clscount: u64,
77+
}
78+
79+
impl Engine {
80+
/// Construct an engine with no input
81+
pub fn new() -> Self {
82+
Engine {
83+
c: 1,
84+
cls: 0,
85+
clscount: 0,
86+
}
87+
}
88+
89+
/// Checksum some data
90+
///
91+
/// If this function returns an error, the `Engine` will be left in an indeterminate
92+
/// state! It is safe to continue feeding it data but the result will not be meaningful.
93+
pub fn input(&mut self, s: &str) -> Result<(), Error> {
94+
for ch in s.chars() {
95+
let pos = INPUT_CHARSET.find(ch).ok_or_else(|| {
96+
Error::BadDescriptor(format!("Invalid character in checksum: '{}'", ch))
97+
})? as u64;
98+
self.c = poly_mod(self.c, pos & 31);
99+
self.cls = self.cls * 3 + (pos >> 5);
100+
self.clscount += 1;
101+
if self.clscount == 3 {
102+
self.c = poly_mod(self.c, self.cls);
103+
self.cls = 0;
104+
self.clscount = 0;
105+
}
106+
}
107+
Ok(())
108+
}
109+
110+
/// Obtain the checksum of all the data thus-far fed to the engine
111+
pub fn checksum_chars(&mut self) -> [char; 8] {
112+
if self.clscount > 0 {
113+
self.c = poly_mod(self.c, self.cls);
114+
}
115+
(0..8).for_each(|_| self.c = poly_mod(self.c, 0));
116+
self.c ^= 1;
117+
118+
let mut chars = [0 as char; 8];
119+
for j in 0..8 {
120+
chars[j] = CHECKSUM_CHARSET[((self.c >> (5 * (7 - j))) & 31) as usize] as char;
121+
}
122+
chars
123+
}
124+
125+
/// Obtain the checksum of all the data thus-far fed to the engine
126+
pub fn checksum(&mut self) -> String {
127+
String::from_iter(self.checksum_chars().iter().copied())
128+
}
107129
}
130+
131+
/// A wrapper around a `fmt::Formatter` which provides checksumming ability
132+
pub struct Formatter<'f, 'a> {
133+
fmt: &'f mut fmt::Formatter<'a>,
134+
eng: Engine,
135+
}
136+
137+
impl<'f, 'a> Formatter<'f, 'a> {
138+
/// Contruct a new `Formatter`, wrapping a given `fmt::Formatter`
139+
pub fn new(f: &'f mut fmt::Formatter<'a>) -> Self {
140+
Formatter {
141+
fmt: f,
142+
eng: Engine::new(),
143+
}
144+
}
145+
146+
/// Writes the checksum into the underlying `fmt::Formatter`
147+
pub fn write_checksum(&mut self) -> fmt::Result {
148+
use fmt::Write;
149+
self.fmt.write_char('#')?;
150+
for ch in self.eng.checksum_chars().iter().copied() {
151+
self.fmt.write_char(ch)?;
152+
}
153+
Ok(())
154+
}
155+
156+
/// Writes the checksum into the underlying `fmt::Formatter`, unless it has "alternate" display on
157+
pub fn write_checksum_if_not_alt(&mut self) -> fmt::Result {
158+
if !self.fmt.alternate() {
159+
self.write_checksum()?;
160+
}
161+
Ok(())
162+
}
163+
}
164+
165+
impl<'f, 'a> fmt::Write for Formatter<'f, 'a> {
166+
fn write_str(&mut self, s: &str) -> fmt::Result {
167+
self.fmt.write_str(s)?;
168+
if self.eng.input(s).is_ok() {
169+
Ok(())
170+
} else {
171+
Err(fmt::Error)
172+
}
173+
}
174+
}
175+
108176
#[cfg(test)]
109177
mod test {
110178
use std::str;

src/descriptor/mod.rs

+96-16
Original file line numberDiff line numberDiff line change
@@ -1086,29 +1086,29 @@ impl_from_str!(
10861086
impl<Pk: MiniscriptKey, T: Extension> fmt::Debug for Descriptor<Pk, T> {
10871087
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10881088
match *self {
1089-
Descriptor::Bare(ref sub) => write!(f, "{:?}", sub),
1090-
Descriptor::Pkh(ref pkh) => write!(f, "{:?}", pkh),
1091-
Descriptor::Wpkh(ref wpkh) => write!(f, "{:?}", wpkh),
1092-
Descriptor::Sh(ref sub) => write!(f, "{:?}", sub),
1093-
Descriptor::Wsh(ref sub) => write!(f, "{:?}", sub),
1094-
Descriptor::LegacyCSFSCov(ref cov) => write!(f, "{:?}", cov),
1095-
Descriptor::Tr(ref tr) => write!(f, "{:?}", tr),
1096-
Descriptor::TrExt(ref tr) => write!(f, "{:?}", tr),
1089+
Descriptor::Bare(ref sub) => fmt::Debug::fmt(sub, f),
1090+
Descriptor::Pkh(ref pkh) => fmt::Debug::fmt(pkh, f),
1091+
Descriptor::Wpkh(ref wpkh) => fmt::Debug::fmt(wpkh, f),
1092+
Descriptor::Sh(ref sub) => fmt::Debug::fmt(sub, f),
1093+
Descriptor::Wsh(ref sub) => fmt::Debug::fmt(sub, f),
1094+
Descriptor::Tr(ref tr) => fmt::Debug::fmt(tr, f),
1095+
Descriptor::TrExt(ref tr) => fmt::Debug::fmt(tr, f),
1096+
Descriptor::LegacyCSFSCov(ref cov) => fmt::Debug::fmt(cov, f),
10971097
}
10981098
}
10991099
}
11001100

11011101
impl<Pk: MiniscriptKey, T: Extension> fmt::Display for Descriptor<Pk, T> {
11021102
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11031103
match *self {
1104-
Descriptor::Bare(ref sub) => write!(f, "{}", sub),
1105-
Descriptor::Pkh(ref pkh) => write!(f, "{}", pkh),
1106-
Descriptor::Wpkh(ref wpkh) => write!(f, "{}", wpkh),
1107-
Descriptor::Sh(ref sub) => write!(f, "{}", sub),
1108-
Descriptor::Wsh(ref sub) => write!(f, "{}", sub),
1109-
Descriptor::LegacyCSFSCov(ref cov) => write!(f, "{}", cov),
1110-
Descriptor::Tr(ref tr) => write!(f, "{}", tr),
1111-
Descriptor::TrExt(ref tr) => write!(f, "{}", tr),
1104+
Descriptor::Bare(ref sub) => fmt::Display::fmt(sub, f),
1105+
Descriptor::Pkh(ref pkh) => fmt::Display::fmt(pkh, f),
1106+
Descriptor::Wpkh(ref wpkh) => fmt::Display::fmt(wpkh, f),
1107+
Descriptor::Sh(ref sub) => fmt::Display::fmt(sub, f),
1108+
Descriptor::Wsh(ref sub) => fmt::Display::fmt(sub, f),
1109+
Descriptor::Tr(ref tr) => fmt::Display::fmt(tr, f),
1110+
Descriptor::TrExt(ref tr) => fmt::Display::fmt(tr, f),
1111+
Descriptor::LegacyCSFSCov(ref cov) => fmt::Display::fmt(cov, f),
11121112
}
11131113
}
11141114
}
@@ -2029,4 +2029,84 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
20292029
Ok(Some((1, expected_concrete)))
20302030
);
20312031
}
2032+
2033+
#[test]
2034+
fn display_alternate() {
2035+
let bare = StdDescriptor::from_str(
2036+
"elpk(020000000000000000000000000000000000000000000000000000000000000002)",
2037+
)
2038+
.unwrap();
2039+
assert_eq!(
2040+
format!("{}", bare),
2041+
"elpk(020000000000000000000000000000000000000000000000000000000000000002)#vlpqwfjv",
2042+
);
2043+
assert_eq!(
2044+
format!("{:#}", bare),
2045+
"elpk(020000000000000000000000000000000000000000000000000000000000000002)",
2046+
);
2047+
2048+
let pkh = StdDescriptor::from_str(
2049+
"elpkh(020000000000000000000000000000000000000000000000000000000000000002)",
2050+
)
2051+
.unwrap();
2052+
assert_eq!(
2053+
format!("{}", pkh),
2054+
"elpkh(020000000000000000000000000000000000000000000000000000000000000002)#jzq8e832",
2055+
);
2056+
assert_eq!(
2057+
format!("{:#}", pkh),
2058+
"elpkh(020000000000000000000000000000000000000000000000000000000000000002)",
2059+
);
2060+
2061+
let wpkh = StdDescriptor::from_str(
2062+
"elwpkh(020000000000000000000000000000000000000000000000000000000000000002)",
2063+
)
2064+
.unwrap();
2065+
assert_eq!(
2066+
format!("{}", wpkh),
2067+
"elwpkh(020000000000000000000000000000000000000000000000000000000000000002)#vxhqdpz9",
2068+
);
2069+
assert_eq!(
2070+
format!("{:#}", wpkh),
2071+
"elwpkh(020000000000000000000000000000000000000000000000000000000000000002)",
2072+
);
2073+
2074+
let shwpkh = StdDescriptor::from_str(
2075+
"elsh(wpkh(020000000000000000000000000000000000000000000000000000000000000002))",
2076+
)
2077+
.unwrap();
2078+
assert_eq!(
2079+
format!("{}", shwpkh),
2080+
"elsh(wpkh(020000000000000000000000000000000000000000000000000000000000000002))#h9ajn2ft",
2081+
);
2082+
assert_eq!(
2083+
format!("{:#}", shwpkh),
2084+
"elsh(wpkh(020000000000000000000000000000000000000000000000000000000000000002))",
2085+
);
2086+
2087+
let wsh = StdDescriptor::from_str("elwsh(1)").unwrap();
2088+
assert_eq!(format!("{}", wsh), "elwsh(1)#s78w5gmj");
2089+
assert_eq!(format!("{:#}", wsh), "elwsh(1)");
2090+
2091+
let sh = StdDescriptor::from_str("elsh(1)").unwrap();
2092+
assert_eq!(format!("{}", sh), "elsh(1)#k4aqrx5p");
2093+
assert_eq!(format!("{:#}", sh), "elsh(1)");
2094+
2095+
let shwsh = StdDescriptor::from_str("elsh(wsh(1))").unwrap();
2096+
assert_eq!(format!("{}", shwsh), "elsh(wsh(1))#d05z4wjl");
2097+
assert_eq!(format!("{:#}", shwsh), "elsh(wsh(1))");
2098+
2099+
let tr = StdDescriptor::from_str(
2100+
"eltr(020000000000000000000000000000000000000000000000000000000000000002)",
2101+
)
2102+
.unwrap();
2103+
assert_eq!(
2104+
format!("{}", tr),
2105+
"eltr(020000000000000000000000000000000000000000000000000000000000000002)#e874qu8z",
2106+
);
2107+
assert_eq!(
2108+
format!("{:#}", tr),
2109+
"eltr(020000000000000000000000000000000000000000000000000000000000000002)",
2110+
);
2111+
}
20322112
}

0 commit comments

Comments
 (0)