Skip to content

Commit

Permalink
Merge #2610: Add a validation variant to ParseError
Browse files Browse the repository at this point in the history
a7a99e0 Add a validation variant to `ParseError` (Tobin C. Harding)
d5c5261 Move NetworkValidationError within file (Tobin C. Harding)

Pull request description:

  As requested, return `ParseError` from `Address<NetworkUnchecked>::require_network`.

  - Patch 1: Preparatory refactor
  - Patch 2: Add the variant.

  Replaces #2593.

  Close: #2507

ACKs for top commit:
  sanket1729:
    ACK a7a99e0.
  apoelstra:
    ACK a7a99e0

Tree-SHA512: 08b290e5f11a5d787918a5b851f98efbeff5bd20a2838e00564f03bb242af3640da663ddc42473419255fff6f64280eaf2595706504676b3228ac81212ea7cf2
  • Loading branch information
apoelstra committed Mar 25, 2024
2 parents c211e7b + a7a99e0 commit 92d8f70
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 22 deletions.
48 changes: 28 additions & 20 deletions bitcoin/src/address/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,6 @@ use crate::blockdata::script::{witness_program, witness_version};
use crate::prelude::*;
use crate::Network;

/// Address's network differs from required one.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NetworkValidationError {
/// Network that was required.
pub(crate) required: Network,
/// The address itself.
pub(crate) address: Address<NetworkUnchecked>,
}

impl fmt::Display for NetworkValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "address ")?;
fmt::Display::fmt(&self.address.0, f)?;
write!(f, " is not valid on {}", self.required)
}
}

#[cfg(feature = "std")]
impl std::error::Error for NetworkValidationError {}

/// Error while generating address from script.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
Expand Down Expand Up @@ -143,6 +123,8 @@ pub enum ParseError {
InvalidBase58PayloadLength(InvalidBase58PayloadLengthError),
/// Invalid legacy address prefix in base58 data payload.
InvalidLegacyPrefix(InvalidLegacyPrefixError),
/// Address's network differs from required one.
NetworkValidation(NetworkValidationError),
}

internals::impl_from_infallible!(ParseError);
Expand All @@ -160,6 +142,7 @@ impl fmt::Display for ParseError {
LegacyAddressTooLong(ref e) => write_err!(f, "legacy address base58 string"; e),
InvalidBase58PayloadLength(ref e) => write_err!(f, "legacy address base58 data"; e),
InvalidLegacyPrefix(ref e) => write_err!(f, "legacy address base58 prefix"; e),
NetworkValidation(ref e) => write_err!(f, "validation error"; e),
}
}
}
Expand All @@ -178,6 +161,7 @@ impl std::error::Error for ParseError {
LegacyAddressTooLong(ref e) => Some(e),
InvalidBase58PayloadLength(ref e) => Some(e),
InvalidLegacyPrefix(ref e) => Some(e),
NetworkValidation(ref e) => Some(e),
}
}
}
Expand Down Expand Up @@ -214,6 +198,10 @@ impl From<InvalidLegacyPrefixError> for ParseError {
fn from(e: InvalidLegacyPrefixError) -> Self { Self::InvalidLegacyPrefix(e) }
}

impl From<NetworkValidationError> for ParseError {
fn from(e: NetworkValidationError) -> Self { Self::NetworkValidation(e) }
}

/// Unknown HRP error.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
Expand All @@ -228,6 +216,26 @@ impl std::error::Error for UnknownHrpError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
}

/// Address's network differs from required one.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NetworkValidationError {
/// Network that was required.
pub(crate) required: Network,
/// The address itself.
pub(crate) address: Address<NetworkUnchecked>,
}

impl fmt::Display for NetworkValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "address ")?;
fmt::Display::fmt(&self.address.0, f)?;
write!(f, " is not valid on {}", self.required)
}
}

#[cfg(feature = "std")]
impl std::error::Error for NetworkValidationError {}

/// Decoded base58 data was an invalid length.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidBase58PayloadLengthError {
Expand Down
42 changes: 40 additions & 2 deletions bitcoin/src/address/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,12 +667,50 @@ impl Address<NetworkUnchecked> {
///
/// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`].
///
/// # Errors
///
/// This function only ever returns the [`ParseError::NetworkValidation`] variant of
/// `ParseError`. This is not how we normally implement errors in this library but
/// `require_network` is not a typical function, it is conceptually part of string parsing.
///
/// # Examples
///
/// ```
/// use bitcoin::address::{NetworkChecked, NetworkUnchecked, ParseError};
/// use bitcoin::{Address, Network};
///
/// const ADDR: &str = "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs";
///
/// fn parse_and_validate_address(network: Network) -> Result<Address, ParseError> {
/// let address = ADDR.parse::<Address<_>>()?
/// .require_network(network)?;
/// Ok(address)
/// }
///
/// fn parse_and_validate_address_combinator(network: Network) -> Result<Address, ParseError> {
/// let address = ADDR.parse::<Address<_>>()
/// .and_then(|a| a.require_network(network))?;
/// Ok(address)
/// }
///
/// fn parse_and_validate_address_show_types(network: Network) -> Result<Address, ParseError> {
/// let address: Address<NetworkChecked> = ADDR.parse::<Address<NetworkUnchecked>>()?
/// .require_network(network)?;
/// Ok(address)
/// }
///
/// let network = Network::Bitcoin; // Don't hard code network in applications.
/// let _ = parse_and_validate_address(network).unwrap();
/// let _ = parse_and_validate_address_combinator(network).unwrap();
/// let _ = parse_and_validate_address_show_types(network).unwrap();
/// ```
#[inline]
pub fn require_network(self, required: Network) -> Result<Address, NetworkValidationError> {
pub fn require_network(self, required: Network) -> Result<Address, ParseError> {
if self.is_valid_for_network(required) {
Ok(self.assume_checked())
} else {
Err(NetworkValidationError { required, address: self })
Err(NetworkValidationError { required, address: self }.into())
}
}

Expand Down

0 comments on commit 92d8f70

Please sign in to comment.