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
11 changes: 9 additions & 2 deletions Cargo-latest.lock
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ dependencies = [
"bitcoin-io",
"bitcoin-units",
"bitcoin_hashes",
"hex-conservative",
"hex-conservative 0.2.1",
"hex_lit",
"secp256k1",
"serde",
Expand Down Expand Up @@ -108,7 +108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
dependencies = [
"bitcoin-io",
"hex-conservative",
"hex-conservative 0.2.1",
"serde",
]

Expand Down Expand Up @@ -196,6 +196,7 @@ dependencies = [
"bincode",
"bitcoin",
"getrandom 0.2.16",
"hex-conservative 1.0.0",
"rand",
"rand_chacha",
"secp256k1-zkp",
Expand Down Expand Up @@ -274,6 +275,12 @@ dependencies = [
"arrayvec",
]

[[package]]
name = "hex-conservative"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ee770c000993d17c185713463d5ebfbd1af9afae4c17cc295640104383bfbf0"

[[package]]
name = "hex_lit"
version = "0.1.1"
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ serde_json = { version = "1.0", optional = true }
actual-serde = { package = "serde", version = "1.0.103", features = [
"derive",
], optional = true }
hex-conservative = "1.0.0"
Copy link
Member

Choose a reason for hiding this comment

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

Hi @Velnbur thanks for the PR, could you please regenerate the Cargo.lock file and include it as Cargo-latest.lock ?



[target.wasm32-unknown-unknown.dev-dependencies]
Expand Down
88 changes: 68 additions & 20 deletions src/confidential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,60 @@ impl<'de> Deserialize<'de> for Nonce {
}
}

/// Error decoding hexadecimal string into tweak-like value.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TweakHexDecodeError {
/// Invalid hexadecimal string.
InvalidHex(hex_conservative::DecodeFixedLengthBytesError),
/// Invalid tweak after decoding hexadecimal string.
InvalidTweak(secp256k1_zkp::Error),
}

impl fmt::Display for TweakHexDecodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TweakHexDecodeError::InvalidHex(err) => {
write!(f, "Invalid hex: {}", err)
}
TweakHexDecodeError::InvalidTweak(err) => {
write!(f, "Invalid tweak: {}", err)
}
}
}
}

#[doc(hidden)]
impl From<hex_conservative::DecodeFixedLengthBytesError> for TweakHexDecodeError {
Copy link
Member

Choose a reason for hiding this comment

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

In 775d7cd:

We don't have to fix this here (it's a problem throughout the crate) but I would like to get rid of these From impls since they increase the API surface and encourage unthinking error promotion.

fn from(err: hex_conservative::DecodeFixedLengthBytesError) -> Self {
TweakHexDecodeError::InvalidHex(err)
}
}

#[doc(hidden)]
impl From<secp256k1_zkp::Error> for TweakHexDecodeError {
fn from(err: secp256k1_zkp::Error) -> Self {
TweakHexDecodeError::InvalidTweak(err)
}
}

impl From<TweakHexDecodeError> for encode::Error {
fn from(value: TweakHexDecodeError) -> Self {
match value {
TweakHexDecodeError::InvalidHex(err) => encode::Error::HexFixedError(err),
TweakHexDecodeError::InvalidTweak(err) => encode::Error::Secp256k1zkp(err),
}
}
}

impl std::error::Error for TweakHexDecodeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
TweakHexDecodeError::InvalidHex(err) => Some(err),
Copy link
Member

Choose a reason for hiding this comment

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

In 775d7cd:

Also don't need to fix it here but we should use Self:: instead of writing TweakHexDecodeError:: all over the place.

TweakHexDecodeError::InvalidTweak(err) => Some(err),
}
}
}

/// Blinding factor used for asset commitments.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct AssetBlindingFactor(pub(crate) Tweak);
Expand Down Expand Up @@ -747,16 +801,13 @@ impl AssetBlindingFactor {
}

impl hex::FromHex for AssetBlindingFactor {
Copy link
Member

Choose a reason for hiding this comment

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

In 775d7cd:

I don't think we should keep the FromHex trait. Where is it used? Can we replace it with FromStr or with a special-purpose utility trait?

Copy link
Member

Choose a reason for hiding this comment

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

I see, it's used for the serde hex_bytes module.

It seems to me that we can replace it with FromStr, introduce a sealed trait if we really want to limit its scope (but do we? I don't think so), and then we can drop hex-conservative 0.2 entirely.

Copy link
Contributor Author

@Velnbur Velnbur Nov 4, 2025

Choose a reason for hiding this comment

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

Shouldn't than all fmt::Display implementations use hex as well? Asking cause it isn't so for Script at least:

fmt::Debug::fmt(self, f)

fn from_byte_iter<I>(iter: I) -> Result<Self, hex::Error>
where I: Iterator<Item=Result<u8, hex::Error>> +
ExactSizeIterator +
DoubleEndedIterator
{
let slice = <[u8; 32]>::from_byte_iter(iter.rev())?;
// Incorrect Return Error
// See: https://github.com/rust-bitcoin/bitcoin_hashes/issues/124
let inner = Tweak::from_inner(slice)
.map_err(|_e| hex::Error::InvalidChar(0))?;
type Err = TweakHexDecodeError;

fn from_hex(s: &str) -> Result<Self, Self::Err> {
let mut slice: [u8; 32] = hex_conservative::decode_to_array(s)?;
slice.reverse();

let inner = Tweak::from_inner(slice)?;
Ok(AssetBlindingFactor(inner))
}
}
Expand Down Expand Up @@ -951,16 +1002,13 @@ impl Neg for ValueBlindingFactor {
}

impl hex::FromHex for ValueBlindingFactor {
fn from_byte_iter<I>(iter: I) -> Result<Self, hex::Error>
where I: Iterator<Item=Result<u8, hex::Error>> +
ExactSizeIterator +
DoubleEndedIterator
{
let slice = <[u8; 32]>::from_byte_iter(iter.rev())?;
// Incorrect Return Error
// See: https://github.com/rust-bitcoin/bitcoin_hashes/issues/124
let inner = Tweak::from_inner(slice)
.map_err(|_e| hex::Error::InvalidChar(0))?;
type Err = TweakHexDecodeError;

fn from_hex(s: &str) -> Result<Self, Self::Err> {
let mut slice: [u8; 32] = hex_conservative::decode_to_array(s)?;
slice.reverse();

let inner = Tweak::from_inner(slice)?;
Ok(ValueBlindingFactor(inner))
}
}
Expand Down
23 changes: 17 additions & 6 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::io::Cursor;
use std::{any, error, fmt, io, mem};

use bitcoin::ScriptBuf;
use hex_conservative::{DecodeFixedLengthBytesError, DecodeVariableLengthBytesError};
use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak};

use crate::hashes::{sha256, Hash};
Expand Down Expand Up @@ -54,8 +55,10 @@ pub enum Error {
Secp256k1zkp(secp256k1_zkp::Error),
/// Pset related Errors
PsetError(pset::Error),
/// Hex parsing errors
HexError(crate::hex::Error),
/// Hex fixed parsing errors
HexFixedError(DecodeFixedLengthBytesError),
/// Hex variable parsing errors
HexVariableError(DecodeVariableLengthBytesError),
Comment on lines +58 to +61
Copy link
Contributor Author

@Velnbur Velnbur Nov 2, 2025

Choose a reason for hiding this comment

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

Not really sure if it's okay to keep both this way. We can alternatively keep only one variant as before HexError(), add hex::Error which is merge of DecodeFixedLengthBytesError and DecodeVariableLengthBytesError and add From implementations

/// Got a time-based locktime when expecting a height-based one, or vice-versa
BadLockTime(crate::LockTime),
/// `VarInt` was encoded in a non-minimal way.
Expand Down Expand Up @@ -83,7 +86,8 @@ impl fmt::Display for Error {
Error::Secp256k1(ref e) => write!(f, "{}", e),
Error::Secp256k1zkp(ref e) => write!(f, "{}", e),
Error::PsetError(ref e) => write!(f, "Pset Error: {}", e),
Error::HexError(ref e) => write!(f, "Hex error {}", e),
Error::HexFixedError(ref e) => write!(f, "Hex fixed error: {}", e),
Error::HexVariableError(ref e) => write!(f, "Hex variable error: {}", e),
Error::BadLockTime(ref lt) => write!(f, "Invalid locktime {}", lt),
Error::NonMinimalVarInt => write!(f, "non-minimal varint"),
}
Expand Down Expand Up @@ -134,9 +138,16 @@ impl From<secp256k1_zkp::Error> for Error {
}

#[doc(hidden)]
impl From<crate::hex::Error> for Error {
fn from(e: crate::hex::Error) -> Self {
Error::HexError(e)
impl From<DecodeFixedLengthBytesError> for Error {
fn from(e: DecodeFixedLengthBytesError) -> Self {
Error::HexFixedError(e)
}
}

#[doc(hidden)]
impl From<DecodeVariableLengthBytesError> for Error {
fn from(e: DecodeVariableLengthBytesError) -> Self {
Error::HexVariableError(e)
}
}

Expand Down
Loading