diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 7e6c3026..c81b5361 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -8,6 +8,12 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "base58check" version = "0.1.0" @@ -159,9 +165,12 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "hex-conservative" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986" +dependencies = [ + "arrayvec", +] [[package]] name = "hex_lit" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 196e597d..2b0e4072 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -8,6 +8,12 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "base58check" version = "0.1.0" @@ -158,9 +164,12 @@ checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" [[package]] name = "hex-conservative" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986" +dependencies = [ + "arrayvec", +] [[package]] name = "hex_lit" diff --git a/base58/Cargo.toml b/base58/Cargo.toml index 7c294a5f..fb312dd3 100644 --- a/base58/Cargo.toml +++ b/base58/Cargo.toml @@ -25,4 +25,4 @@ hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = fa internals = { package = "bitcoin-internals", version = "0.2.0" } [dev-dependencies] -hex = { package = "hex-conservative", version = "0.1.1", default-features = false, features = ["alloc"] } +hex = { package = "hex-conservative", version = "0.2.0", default-features = false, features = ["alloc"] } diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml index ffc02bb1..8a46cb1b 100644 --- a/bitcoin/Cargo.toml +++ b/bitcoin/Cargo.toml @@ -31,7 +31,7 @@ rustdoc-args = ["--cfg", "docsrs"] base58 = { package = "base58check", version = "0.1.0", default-features = false } bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] } hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = false, features = ["alloc", "io"] } -hex = { package = "hex-conservative", version = "0.1.1", default-features = false, features = ["alloc"] } +hex = { package = "hex-conservative", version = "0.2.0", default-features = false, features = ["alloc"] } hex_lit = "0.1.1" internals = { package = "bitcoin-internals", version = "0.2.0" } io = { package = "bitcoin-io", version = "0.1.1", default-features = false, features = ["alloc"] } diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index 92bdca93..d2e0bfb9 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -1960,6 +1960,10 @@ mod tests { format!("{:x}", tx.compute_txid()), "9652aa62b0e748caeec40c4cb7bc17c6792435cc3dfe447dd1ca24f912a1c6ec" ); + assert_eq!( + format!("{:.10x}", tx.compute_txid()), + "9652aa62b0" + ); assert_eq!(tx.weight(), Weight::from_wu(2718)); // non-segwit tx from my mempool diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index 2974de43..d48d3b38 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -506,18 +506,19 @@ impl<'de> serde::Deserialize<'de> for Witness { while let Some(elem) = a.next_element::()? { let vec = Vec::::from_hex(&elem).map_err(|e| match e { - InvalidChar(b) => match core::char::from_u32(b.into()) { + InvalidChar(ref e) => match core::char::from_u32(e.invalid_char( + ).into()) { Some(c) => de::Error::invalid_value( Unexpected::Char(c), &"a valid hex character", ), None => de::Error::invalid_value( - Unexpected::Unsigned(b.into()), + Unexpected::Unsigned(e.invalid_char().into()), &"a valid hex character", ), }, - OddLengthString(len) => - de::Error::invalid_length(len, &"an even length string"), + OddLengthString(ref e) => + de::Error::invalid_length(e.length(), &"an even length string"), })?; ret.push(vec); } diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs index eb5acfd4..5a096e6c 100644 --- a/bitcoin/src/consensus/serde.rs +++ b/bitcoin/src/consensus/serde.rs @@ -71,11 +71,11 @@ pub mod hex { /// Hex byte encoder. // We wrap `BufEncoder` to not leak internal representation. - pub struct Encoder(BufEncoder<[u8; HEX_BUF_SIZE]>, PhantomData); + pub struct Encoder(BufEncoder<{ HEX_BUF_SIZE }>, PhantomData); impl From> for Encoder { fn from(_: super::Hex) -> Self { - Encoder(BufEncoder::new([0; HEX_BUF_SIZE]), Default::default()) + Encoder(BufEncoder::new(), Default::default()) } } @@ -100,15 +100,15 @@ pub mod hex { // Newtypes to hide internal details. /// Error returned when a hex string decoder can't be created. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct DecodeInitError(hex::HexToBytesError); + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DecodeInitError(hex::OddLengthStringError); /// Error returned when a hex string contains invalid characters. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct DecodeError(hex::HexToBytesError); + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct DecodeError(hex::InvalidCharError); /// Hex decoder state. - pub struct Decoder<'a>(hex::HexToBytesIter<'a>); + pub struct Decoder<'a>(hex::HexSliceToBytesIter<'a>); impl<'a> Decoder<'a> { fn new(s: &'a str) -> Result { @@ -137,29 +137,19 @@ pub mod hex { impl super::IntoDeError for DecodeInitError { fn into_de_error(self) -> E { - use hex::HexToBytesError; - - match self.0 { - HexToBytesError::OddLengthString(len) => - E::invalid_length(len, &"an even number of ASCII-encoded hex digits"), - error => panic!("unexpected error: {:?}", error), - } + E::invalid_length(self.0.length(), &"an even number of ASCII-encoded hex digits") } } impl super::IntoDeError for DecodeError { fn into_de_error(self) -> E { - use hex::HexToBytesError; use serde::de::Unexpected; const EXPECTED_CHAR: &str = "an ASCII-encoded hex digit"; - match self.0 { - HexToBytesError::InvalidChar(c) if c.is_ascii() => - E::invalid_value(Unexpected::Char(c as _), &EXPECTED_CHAR), - HexToBytesError::InvalidChar(c) => - E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR), - error => panic!("unexpected error: {:?}", error), + match self.0.invalid_char() { + c if c.is_ascii() => E::invalid_value(Unexpected::Char(c as _), &EXPECTED_CHAR), + c => E::invalid_value(Unexpected::Unsigned(c.into()), &EXPECTED_CHAR), } } } diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs index 48ac319c..e3babd05 100644 --- a/bitcoin/src/crypto/key.rs +++ b/bitcoin/src/crypto/key.rs @@ -10,7 +10,7 @@ use core::ops; use core::str::FromStr; use hashes::{hash160, Hash}; -use hex::{FromHex, HexToArrayError, HexToBytesError}; +use hex::{FromHex, HexToArrayError}; use internals::array_vec::ArrayVec; use internals::write_err; use io::{Read, Write}; @@ -233,22 +233,22 @@ impl fmt::Display for PublicKey { impl FromStr for PublicKey { type Err = ParsePublicKeyError; fn from_str(s: &str) -> Result { + use HexToArrayError::*; + match s.len() { 66 => { - PublicKey::from_slice(&<[u8; 33]>::from_hex(s).map_err(|op| { - match op { - HexToArrayError::Conversion(HexToBytesError::InvalidChar(char)) => ParsePublicKeyError::InvalidChar(char), - HexToArrayError::Conversion(HexToBytesError::OddLengthString(_)) | HexToArrayError::InvalidLength(_,_) => unreachable!("invalid length"), - } - })?).map_err(From::from) + let bytes = <[u8; 33]>::from_hex(s).map_err(|e| match e { + InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()), + InvalidLength(_) => unreachable!("length checked already") + })?; + Ok(PublicKey::from_slice(&bytes).expect("length checked already")) }, 130 => { - PublicKey::from_slice(&<[u8; 65]>::from_hex(s).map_err(|op| { - match op { - HexToArrayError::Conversion(HexToBytesError::InvalidChar(char)) => ParsePublicKeyError::InvalidChar(char), - HexToArrayError::Conversion(HexToBytesError::OddLengthString(_)) | HexToArrayError::InvalidLength(_,_) => unreachable!("invalid length"), - } - })?).map_err(From::from) + let bytes = <[u8; 65]>::from_hex(s).map_err(|e| match e { + InvalidChar(e) => ParsePublicKeyError::InvalidChar(e.invalid_char()), + InvalidLength(_) => unreachable!("length checked already") + })?; + Ok(PublicKey::from_slice(&bytes).expect("length checked already")) } len => Err(ParsePublicKeyError::InvalidHexLength(len)), } diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index d14e186a..4eac052d 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -1035,7 +1035,7 @@ mod tests { #[track_caller] pub fn hex_psbt(s: &str) -> Result { - let r: Result, hex::HexToBytesError> = Vec::from_hex(s); + let r = Vec::from_hex(s); match r { Err(_e) => panic!("unable to parse hex string {}", s), Ok(v) => Psbt::deserialize(&v), diff --git a/bitcoin/src/taproot/serialized_signature.rs b/bitcoin/src/taproot/serialized_signature.rs index 337ced5f..4def2e57 100644 --- a/bitcoin/src/taproot/serialized_signature.rs +++ b/bitcoin/src/taproot/serialized_signature.rs @@ -28,10 +28,7 @@ impl fmt::Debug for SerializedSignature { impl fmt::Display for SerializedSignature { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut buf = [0u8; MAX_LEN * 2]; - let mut encoder = hex::buf_encoder::BufEncoder::new(&mut buf); - encoder.put_bytes(self, hex::Case::Lower); - f.pad_integral(true, "0x", encoder.as_str()) + hex::fmt_hex_exact!(f, MAX_LEN, self, hex::Case::Lower) } } diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml index 70a6758e..14368252 100644 --- a/hashes/Cargo.toml +++ b/hashes/Cargo.toml @@ -27,7 +27,7 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -hex = { package = "hex-conservative", version = "0.1.1", default-features = false } +hex = { package = "hex-conservative", version = "0.2.0", default-features = false } bitcoin-io = { version = "0.1.1", default-features = false, optional = true } schemars = { version = "0.8.3", default-features = false, optional = true } diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 9352cde5..9b3f9dde 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -71,22 +71,6 @@ pub(crate) use arr_newtype_fmt_impl; macro_rules! hash_trait_impls { ($bits:expr, $reverse:expr $(, $gen:ident: $gent:ident)*) => { impl<$($gen: $gent),*> Hash<$($gen),*> { - /// Displays hex forwards, regardless of how this type would display it naturally. - /// - /// This is mainly intended as an internal method and you shouldn't need it unless - /// you're doing something special. - pub fn forward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { - $crate::hex::DisplayHex::as_hex(&self.0) - } - - /// Displays hex backwards, regardless of how this type would display it naturally. - /// - /// This is mainly intended as an internal method and you shouldn't need it unless - /// you're doing something special. - pub fn backward_hex(&self) -> impl '_ + core::fmt::LowerHex + core::fmt::UpperHex { - $crate::hex::display::DisplayArray::<_, [u8; $bits / 8 * 2]>::new(self.0.iter().rev()) - } - /// Zero cost conversion between a fixed length byte array shared reference and /// a shared reference to this Hash type. pub fn from_bytes_ref(bytes: &[u8; $bits / 8]) -> &Self { @@ -105,15 +89,13 @@ macro_rules! hash_trait_impls { impl<$($gen: $gent),*> str::FromStr for Hash<$($gen),*> { type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> $crate::_export::_core::result::Result { - use $crate::hex::{FromHex, HexToBytesIter}; - use $crate::Hash; + use $crate::{Hash, hex::{FromHex}}; - let inner: [u8; $bits / 8] = if $reverse { - FromHex::from_byte_iter(HexToBytesIter::new(s)?.rev())? - } else { - FromHex::from_byte_iter(HexToBytesIter::new(s)?)? - }; - Ok(Self::from_byte_array(inner)) + let mut bytes = <[u8; $bits / 8]>::from_hex(s)?; + if $reverse { + bytes.reverse(); + } + Ok(Self::from_byte_array(bytes)) } } diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index d2e7b850..d612493f 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -273,4 +273,12 @@ mod tests { let h2: TestNewtype = h.to_string().parse().unwrap(); assert_eq!(h2.to_raw_hash(), h); } + + #[test] + fn newtype_fmt_roundtrip() { + let orig = TestNewtype::hash(&[]); + let hex = format!("{}", orig); + let rinsed = hex.parse::().expect("failed to parse hex"); + assert_eq!(rinsed, orig) + } } diff --git a/hashes/src/sha256.rs b/hashes/src/sha256.rs index 77eca04b..63e214a9 100644 --- a/hashes/src/sha256.rs +++ b/hashes/src/sha256.rs @@ -176,15 +176,13 @@ impl Midstate { } impl hex::FromHex for Midstate { - type Err = hex::HexToArrayError; - fn from_byte_iter(iter: I) -> Result - where - I: Iterator> - + ExactSizeIterator - + DoubleEndedIterator, - { + type Error = hex::HexToArrayError; + + fn from_hex(s: &str) -> Result { // DISPLAY_BACKWARD is true - Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?)) + let mut bytes = <[u8; 32]>::from_hex(s)?; + bytes.reverse(); + Ok(Midstate(bytes)) } } @@ -817,7 +815,8 @@ impl HashEngine { #[cfg(test)] mod tests { - use crate::{sha256, Hash, HashEngine}; + use crate::{sha256, Hash as _, HashEngine}; + use super::*; #[test] #[cfg(feature = "alloc")] @@ -882,6 +881,14 @@ mod tests { } } + #[test] + fn fmt_roundtrips() { + let hash = sha256::Hash::hash(b"some arbitrary bytes"); + let hex = format!("{}", hash); + let rinsed = hex.parse::().expect("failed to parse hex"); + assert_eq!(rinsed, hash) + } + #[test] #[rustfmt::skip] fn midstate() { @@ -964,14 +971,14 @@ mod tests { #[test] fn const_hash() { - assert_eq!(super::Hash::hash(&[]), super::Hash::const_hash(&[])); + assert_eq!(Hash::hash(&[]), Hash::const_hash(&[])); let mut bytes = Vec::new(); for i in 0..256 { bytes.push(i as u8); assert_eq!( - super::Hash::hash(&bytes), - super::Hash::const_hash(&bytes), + Hash::hash(&bytes), + Hash::const_hash(&bytes), "hashes don't match for length {}", i + 1 ); @@ -980,8 +987,6 @@ mod tests { #[test] fn const_midstate() { - use super::Midstate; - assert_eq!( Midstate::hash_tag(b"TapLeaf"), Midstate([ @@ -991,6 +996,14 @@ mod tests { ) } + #[test] + fn midstate_fmt_roundtrip() { + let midstate = Midstate::hash_tag(b"ArbitraryTag"); + let hex = format!("{}", midstate); + let rinsed = hex.parse::().expect("failed to parse hex"); + assert_eq!(rinsed, midstate) + } + #[cfg(feature = "serde")] #[test] fn sha256_serde() { diff --git a/hashes/src/sha256d.rs b/hashes/src/sha256d.rs index 4fb813bd..fb3b0c5e 100644 --- a/hashes/src/sha256d.rs +++ b/hashes/src/sha256d.rs @@ -30,10 +30,12 @@ fn from_engine(e: sha256::HashEngine) -> Hash { #[cfg(test)] mod tests { + use crate::{sha256d, Hash as _}; + #[test] #[cfg(feature = "alloc")] fn test() { - use crate::{sha256, sha256d, Hash, HashEngine}; + use crate::{sha256, HashEngine}; #[derive(Clone)] struct Test { @@ -81,13 +83,19 @@ mod tests { } } + #[test] + fn fmt_roundtrips() { + let hash = sha256d::Hash::hash(b"some arbitrary bytes"); + let hex = format!("{}", hash); + let rinsed = hex.parse::().expect("failed to parse hex"); + assert_eq!(rinsed, hash) + } + #[cfg(feature = "serde")] #[test] fn sha256_serde() { use serde_test::{assert_tokens, Configure, Token}; - use crate::{sha256d, Hash}; - #[rustfmt::skip] static HASH_BYTES: [u8; 32] = [ 0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, diff --git a/hashes/src/util.rs b/hashes/src/util.rs index 92fd4855..c84b928a 100644 --- a/hashes/src/util.rs +++ b/hashes/src/util.rs @@ -3,17 +3,17 @@ #[macro_export] /// Adds hexadecimal formatting implementation of a trait `$imp` to a given type `$ty`. macro_rules! hex_fmt_impl( - ($reverse:expr, $ty:ident) => ( - $crate::hex_fmt_impl!($reverse, $ty, ); + ($reverse:expr, $len:expr, $ty:ident) => ( + $crate::hex_fmt_impl!($reverse, $len, $ty, ); ); - ($reverse:expr, $ty:ident, $($gen:ident: $gent:ident),*) => ( + ($reverse:expr, $len:expr, $ty:ident, $($gen:ident: $gent:ident),*) => ( impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> { #[inline] fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { if $reverse { - $crate::_export::_core::fmt::LowerHex::fmt(&self.0.backward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self).iter().rev(), $crate::hex::Case::Lower) } else { - $crate::_export::_core::fmt::LowerHex::fmt(&self.0.forward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self), $crate::hex::Case::Lower) } } } @@ -22,9 +22,9 @@ macro_rules! hex_fmt_impl( #[inline] fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result { if $reverse { - $crate::_export::_core::fmt::UpperHex::fmt(&self.0.backward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self).iter().rev(), $crate::hex::Case::Upper) } else { - $crate::_export::_core::fmt::UpperHex::fmt(&self.0.forward_hex(), f) + $crate::hex::fmt_hex_exact!(f, $len, ::as_byte_array(&self), $crate::hex::Case::Upper) } } } @@ -188,7 +188,7 @@ macro_rules! hash_newtype { $({ $($type_attrs)* })* } - $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, $newtype); + $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, <$newtype as $crate::Hash>::LEN, $newtype); $crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN); $crate::borrow_slice_impl!($newtype); @@ -270,15 +270,13 @@ macro_rules! hash_newtype { impl $crate::_export::_core::str::FromStr for $newtype { type Err = $crate::hex::HexToArrayError; fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::Err> { - use $crate::hex::{FromHex, HexToBytesIter}; - use $crate::Hash; + use $crate::{Hash, hex::FromHex}; - let inner: <$hash as Hash>::Bytes = if ::DISPLAY_BACKWARD { - FromHex::from_byte_iter(HexToBytesIter::new(s)?.rev())? - } else { - FromHex::from_byte_iter(HexToBytesIter::new(s)?)? + let mut bytes = <[u8; ::LEN]>::from_hex(s)?; + if ::DISPLAY_BACKWARD { + bytes.reverse(); }; - Ok($newtype(<$hash>::from_byte_array(inner))) + Ok($newtype(<$hash>::from_byte_array(bytes))) } }