Skip to content

Commit

Permalink
Merge commit '72dab64af741383d502cb2aad8c78d3a749e6d50' into update_2…
Browse files Browse the repository at this point in the history
…022_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
  • Loading branch information
sanket1729 committed Oct 21, 2022
2 parents 8b53a6c + 72dab64 commit f1349b1
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 111 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ rand = ["bitcoin/rand"]
[dependencies]
bitcoin = "0.29.1"
elements = "0.21.0"
bitcoin-miniscript = {package = "miniscript", git = "https://github.com/rust-bitcoin/rust-miniscript", rev = "d5615acda1a7fdc4041a11c1736af139b8c7ebe8"}
bitcoin-miniscript = {package = "miniscript", git = "https://github.com/rust-bitcoin/rust-miniscript", rev = "72dab64af741383d502cb2aad8c78d3a749e6d50"}

# Do NOT use this as a feature! Use the `serde` feature instead.
actual-serde = { package = "serde", version = "1.0", optional = true }
Expand Down
21 changes: 12 additions & 9 deletions src/descriptor/bare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ use core::fmt;

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

use super::checksum::{desc_checksum, verify_checksum};
use super::checksum::verify_checksum;
use super::ELMTS_STR;
use crate::descriptor::checksum;
use crate::expression::{self, FromTree};
use crate::miniscript::context::ScriptContext;
use crate::policy::{semantic, Liftable};
Expand Down Expand Up @@ -130,10 +131,11 @@ impl<Pk: MiniscriptKey> fmt::Debug for Bare<Pk> {
}

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

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

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

Expand Down
11 changes: 6 additions & 5 deletions src/descriptor/blinded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ use std::fmt;

use elements::{self, Script};

use super::checksum::{desc_checksum, strip_checksum, verify_checksum};
use super::checksum::verify_checksum;
use super::{Descriptor, TranslatePk};
use crate::descriptor::checksum;
use crate::expression::{self, FromTree};
use crate::extensions::{CovExtArgs, CovenantExt};
use crate::policy::{semantic, Liftable};
Expand Down Expand Up @@ -74,10 +75,10 @@ impl<Pk: MiniscriptKey> fmt::Debug for Blinded<Pk> {
impl<Pk: MiniscriptKey> fmt::Display for Blinded<Pk> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// strip thec checksum from display
let desc = format!("{}", self.desc);
let desc = format!("blinded({},{})", self.blinder, strip_checksum(&desc));
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
write!(f, "{}#{}", &desc, &checksum)
use fmt::Write;
let mut wrapped_f = checksum::Formatter::new(f);
write!(wrapped_f, "blinded({},{:#})", self.blinder, self.desc)?;
wrapped_f.write_checksum_if_not_alt()
}
}

Expand Down
150 changes: 109 additions & 41 deletions src/descriptor/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
//! checksum of a descriptor

use std::iter::FromIterator;
use core::fmt;
use core::iter::FromIterator;

use crate::Error;

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

fn poly_mod(mut c: u64, val: u64) -> u64 {
let c0 = c >> 35;
Expand Down Expand Up @@ -38,40 +39,9 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
/// descriptor string is syntactically correct or not.
/// This only computes the checksum
pub fn desc_checksum(desc: &str) -> Result<String, Error> {
let mut c = 1;
let mut cls = 0;
let mut clscount = 0;

for ch in desc.chars() {
let pos = INPUT_CHARSET.find(ch).ok_or_else(|| {
Error::BadDescriptor(format!("Invalid character in checksum: '{}'", 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 chars = Vec::with_capacity(8);
for j in 0..8 {
chars.push(
CHECKSUM_CHARSET
.chars()
.nth(((c >> (5 * (7 - j))) & 31) as usize)
.unwrap(),
);
}

Ok(String::from_iter(chars))
let mut eng = Engine::new();
eng.input(desc)?;
Ok(eng.checksum())
}

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

/// Helper function to strip checksum without verifying
#[allow(dead_code)]
pub(super) fn strip_checksum(s: &str) -> &str {
let mut parts = s.splitn(2, '#');
parts.next().unwrap()
/// An engine to compute a checksum from a string
pub struct Engine {
c: u64,
cls: u64,
clscount: u64,
}

impl Engine {
/// Construct an engine with no input
pub fn new() -> Self {
Engine {
c: 1,
cls: 0,
clscount: 0,
}
}

/// Checksum some data
///
/// If this function returns an error, the `Engine` will be left in an indeterminate
/// state! It is safe to continue feeding it data but the result will not be meaningful.
pub fn input(&mut self, s: &str) -> Result<(), Error> {
for ch in s.chars() {
let pos = INPUT_CHARSET.find(ch).ok_or_else(|| {
Error::BadDescriptor(format!("Invalid character in checksum: '{}'", ch))
})? as u64;
self.c = poly_mod(self.c, pos & 31);
self.cls = self.cls * 3 + (pos >> 5);
self.clscount += 1;
if self.clscount == 3 {
self.c = poly_mod(self.c, self.cls);
self.cls = 0;
self.clscount = 0;
}
}
Ok(())
}

/// Obtain the checksum of all the data thus-far fed to the engine
pub fn checksum_chars(&mut self) -> [char; 8] {
if self.clscount > 0 {
self.c = poly_mod(self.c, self.cls);
}
(0..8).for_each(|_| self.c = poly_mod(self.c, 0));
self.c ^= 1;

let mut chars = [0 as char; 8];
for j in 0..8 {
chars[j] = CHECKSUM_CHARSET[((self.c >> (5 * (7 - j))) & 31) as usize] as char;
}
chars
}

/// Obtain the checksum of all the data thus-far fed to the engine
pub fn checksum(&mut self) -> String {
String::from_iter(self.checksum_chars().iter().copied())
}
}

/// A wrapper around a `fmt::Formatter` which provides checksumming ability
pub struct Formatter<'f, 'a> {
fmt: &'f mut fmt::Formatter<'a>,
eng: Engine,
}

impl<'f, 'a> Formatter<'f, 'a> {
/// Contruct a new `Formatter`, wrapping a given `fmt::Formatter`
pub fn new(f: &'f mut fmt::Formatter<'a>) -> Self {
Formatter {
fmt: f,
eng: Engine::new(),
}
}

/// Writes the checksum into the underlying `fmt::Formatter`
pub fn write_checksum(&mut self) -> fmt::Result {
use fmt::Write;
self.fmt.write_char('#')?;
for ch in self.eng.checksum_chars().iter().copied() {
self.fmt.write_char(ch)?;
}
Ok(())
}

/// Writes the checksum into the underlying `fmt::Formatter`, unless it has "alternate" display on
pub fn write_checksum_if_not_alt(&mut self) -> fmt::Result {
if !self.fmt.alternate() {
self.write_checksum()?;
}
Ok(())
}
}

impl<'f, 'a> fmt::Write for Formatter<'f, 'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.fmt.write_str(s)?;
if self.eng.input(s).is_ok() {
Ok(())
} else {
Err(fmt::Error)
}
}
}

#[cfg(test)]
mod test {
use std::str;
Expand Down
112 changes: 96 additions & 16 deletions src/descriptor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,29 +1086,29 @@ impl_from_str!(
impl<Pk: MiniscriptKey, T: Extension> fmt::Debug for Descriptor<Pk, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Descriptor::Bare(ref sub) => write!(f, "{:?}", sub),
Descriptor::Pkh(ref pkh) => write!(f, "{:?}", pkh),
Descriptor::Wpkh(ref wpkh) => write!(f, "{:?}", wpkh),
Descriptor::Sh(ref sub) => write!(f, "{:?}", sub),
Descriptor::Wsh(ref sub) => write!(f, "{:?}", sub),
Descriptor::LegacyCSFSCov(ref cov) => write!(f, "{:?}", cov),
Descriptor::Tr(ref tr) => write!(f, "{:?}", tr),
Descriptor::TrExt(ref tr) => write!(f, "{:?}", tr),
Descriptor::Bare(ref sub) => fmt::Debug::fmt(sub, f),
Descriptor::Pkh(ref pkh) => fmt::Debug::fmt(pkh, f),
Descriptor::Wpkh(ref wpkh) => fmt::Debug::fmt(wpkh, f),
Descriptor::Sh(ref sub) => fmt::Debug::fmt(sub, f),
Descriptor::Wsh(ref sub) => fmt::Debug::fmt(sub, f),
Descriptor::Tr(ref tr) => fmt::Debug::fmt(tr, f),
Descriptor::TrExt(ref tr) => fmt::Debug::fmt(tr, f),
Descriptor::LegacyCSFSCov(ref cov) => fmt::Debug::fmt(cov, f),
}
}
}

impl<Pk: MiniscriptKey, T: Extension> fmt::Display for Descriptor<Pk, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Descriptor::Bare(ref sub) => write!(f, "{}", sub),
Descriptor::Pkh(ref pkh) => write!(f, "{}", pkh),
Descriptor::Wpkh(ref wpkh) => write!(f, "{}", wpkh),
Descriptor::Sh(ref sub) => write!(f, "{}", sub),
Descriptor::Wsh(ref sub) => write!(f, "{}", sub),
Descriptor::LegacyCSFSCov(ref cov) => write!(f, "{}", cov),
Descriptor::Tr(ref tr) => write!(f, "{}", tr),
Descriptor::TrExt(ref tr) => write!(f, "{}", tr),
Descriptor::Bare(ref sub) => fmt::Display::fmt(sub, f),
Descriptor::Pkh(ref pkh) => fmt::Display::fmt(pkh, f),
Descriptor::Wpkh(ref wpkh) => fmt::Display::fmt(wpkh, f),
Descriptor::Sh(ref sub) => fmt::Display::fmt(sub, f),
Descriptor::Wsh(ref sub) => fmt::Display::fmt(sub, f),
Descriptor::Tr(ref tr) => fmt::Display::fmt(tr, f),
Descriptor::TrExt(ref tr) => fmt::Display::fmt(tr, f),
Descriptor::LegacyCSFSCov(ref cov) => fmt::Display::fmt(cov, f),
}
}
}
Expand Down Expand Up @@ -2029,4 +2029,84 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
Ok(Some((1, expected_concrete)))
);
}

#[test]
fn display_alternate() {
let bare = StdDescriptor::from_str(
"elpk(020000000000000000000000000000000000000000000000000000000000000002)",
)
.unwrap();
assert_eq!(
format!("{}", bare),
"elpk(020000000000000000000000000000000000000000000000000000000000000002)#vlpqwfjv",
);
assert_eq!(
format!("{:#}", bare),
"elpk(020000000000000000000000000000000000000000000000000000000000000002)",
);

let pkh = StdDescriptor::from_str(
"elpkh(020000000000000000000000000000000000000000000000000000000000000002)",
)
.unwrap();
assert_eq!(
format!("{}", pkh),
"elpkh(020000000000000000000000000000000000000000000000000000000000000002)#jzq8e832",
);
assert_eq!(
format!("{:#}", pkh),
"elpkh(020000000000000000000000000000000000000000000000000000000000000002)",
);

let wpkh = StdDescriptor::from_str(
"elwpkh(020000000000000000000000000000000000000000000000000000000000000002)",
)
.unwrap();
assert_eq!(
format!("{}", wpkh),
"elwpkh(020000000000000000000000000000000000000000000000000000000000000002)#vxhqdpz9",
);
assert_eq!(
format!("{:#}", wpkh),
"elwpkh(020000000000000000000000000000000000000000000000000000000000000002)",
);

let shwpkh = StdDescriptor::from_str(
"elsh(wpkh(020000000000000000000000000000000000000000000000000000000000000002))",
)
.unwrap();
assert_eq!(
format!("{}", shwpkh),
"elsh(wpkh(020000000000000000000000000000000000000000000000000000000000000002))#h9ajn2ft",
);
assert_eq!(
format!("{:#}", shwpkh),
"elsh(wpkh(020000000000000000000000000000000000000000000000000000000000000002))",
);

let wsh = StdDescriptor::from_str("elwsh(1)").unwrap();
assert_eq!(format!("{}", wsh), "elwsh(1)#s78w5gmj");
assert_eq!(format!("{:#}", wsh), "elwsh(1)");

let sh = StdDescriptor::from_str("elsh(1)").unwrap();
assert_eq!(format!("{}", sh), "elsh(1)#k4aqrx5p");
assert_eq!(format!("{:#}", sh), "elsh(1)");

let shwsh = StdDescriptor::from_str("elsh(wsh(1))").unwrap();
assert_eq!(format!("{}", shwsh), "elsh(wsh(1))#d05z4wjl");
assert_eq!(format!("{:#}", shwsh), "elsh(wsh(1))");

let tr = StdDescriptor::from_str(
"eltr(020000000000000000000000000000000000000000000000000000000000000002)",
)
.unwrap();
assert_eq!(
format!("{}", tr),
"eltr(020000000000000000000000000000000000000000000000000000000000000002)#e874qu8z",
);
assert_eq!(
format!("{:#}", tr),
"eltr(020000000000000000000000000000000000000000000000000000000000000002)",
);
}
}
Loading

0 comments on commit f1349b1

Please sign in to comment.