diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs index cbb1cb43..c522656e 100644 --- a/bitcoin/src/address/error.rs +++ b/bitcoin/src/address/error.rs @@ -13,14 +13,6 @@ use crate::{base58, Network}; #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum Error { - /// A witness version construction error. - WitnessVersion(witness_version::TryFromError), - /// A witness program error. - WitnessProgram(witness_program::Error), - /// Address size more than 520 bytes is not allowed. - ExcessiveScriptSize, - /// Script is not a p2pkh, p2sh or witness program. - UnrecognizedScript, /// Address's network differs from required one. NetworkValidation { /// Network that was required. @@ -37,10 +29,6 @@ impl fmt::Display for Error { use Error::*; match *self { - WitnessVersion(ref e) => write_err!(f, "witness version construction error"; e), - WitnessProgram(ref e) => write_err!(f, "witness program error"; e), - ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"), - UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program"), NetworkValidation { required, ref address } => { write!(f, "address ")?; fmt::Display::fmt(&address.0, f)?; @@ -56,21 +44,85 @@ impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use Error::*; - match self { - WitnessVersion(e) => Some(e), - WitnessProgram(e) => Some(e), - UnknownHrp(e) => Some(e), - ExcessiveScriptSize | UnrecognizedScript | NetworkValidation { .. } => None, + match *self { + UnknownHrp(ref e) => Some(e), + NetworkValidation { .. } => None, } } } -impl From for Error { - fn from(e: witness_version::TryFromError) -> Error { Error::WitnessVersion(e) } +/// Error while generating address from script. +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum FromScriptError { + /// Script is not a p2pkh, p2sh or witness program. + UnrecognizedScript, + /// A witness program error. + WitnessProgram(witness_program::Error), + /// A witness version construction error. + WitnessVersion(witness_version::TryFromError), } -impl From for Error { - fn from(e: witness_program::Error) -> Error { Error::WitnessProgram(e) } +impl fmt::Display for FromScriptError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use FromScriptError::*; + + match *self { + WitnessVersion(ref e) => write_err!(f, "witness version construction error"; e), + WitnessProgram(ref e) => write_err!(f, "witness program error"; e), + UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for FromScriptError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use FromScriptError::*; + + match *self { + UnrecognizedScript => None, + WitnessVersion(ref e) => Some(e), + WitnessProgram(ref e) => Some(e), + } + } +} + +impl From for FromScriptError { + fn from(e : witness_program::Error) -> Self { Self::WitnessProgram(e) } +} + +impl From for FromScriptError { + fn from(e: witness_version::TryFromError) -> Self { Self::WitnessVersion(e) } +} + +/// Error while generating address from a p2sh script. +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum P2shError { + /// Address size more than 520 bytes is not allowed. + ExcessiveScriptSize, +} + +impl fmt::Display for P2shError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use P2shError::*; + + match *self { + ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for P2shError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use P2shError::*; + + match self { + ExcessiveScriptSize => None, + } + } } /// Address type is either invalid or not supported in rust-bitcoin. diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 8d97edab..a8e058e6 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -51,10 +51,11 @@ use crate::network::{Network, NetworkKind}; use crate::prelude::*; use crate::taproot::TapNodeHash; +use self::error::P2shError; #[rustfmt::skip] // Keep public re-exports separate. #[doc(inline)] pub use self::{ - error::{Error, ParseError, UnknownAddressTypeError, UnknownHrpError}, + error::{Error, ParseError, UnknownAddressTypeError, UnknownHrpError, FromScriptError}, }; /// The different types of addresses. @@ -373,9 +374,9 @@ impl Address { /// This address type was introduced with BIP16 and is the popular type to implement multi-sig /// these days. #[inline] - pub fn p2sh(script: &Script, network: impl Into) -> Result { + pub fn p2sh(script: &Script, network: impl Into) -> Result { if script.len() > MAX_SCRIPT_ELEMENT_SIZE { - return Err(Error::ExcessiveScriptSize); + return Err(P2shError::ExcessiveScriptSize); } let hash = script.script_hash(); Ok(Address::p2sh_from_hash(hash, network)) @@ -503,7 +504,7 @@ impl Address { pub fn is_spend_standard(&self) -> bool { self.address_type().is_some() } /// Constructs an [`Address`] from an output script (`scriptPubkey`). - pub fn from_script(script: &Script, network: Network) -> Result { + pub fn from_script(script: &Script, network: Network) -> Result { if script.is_p2pkh() { let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long"); let hash = PubkeyHash::from_byte_array(bytes); @@ -519,7 +520,7 @@ impl Address { let program = WitnessProgram::new(version, &script.as_bytes()[2..])?; Ok(Address::from_witness_program(program, network)) } else { - Err(Error::UnrecognizedScript) + Err(FromScriptError::UnrecognizedScript) } } @@ -861,7 +862,7 @@ mod tests { #[test] fn test_p2sh_parse_for_large_script() { let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123").unwrap(); - assert_eq!(Address::p2sh(&script, NetworkKind::Test), Err(Error::ExcessiveScriptSize)); + assert_eq!(Address::p2sh(&script, NetworkKind::Test), Err(P2shError::ExcessiveScriptSize)); } #[test] @@ -1236,13 +1237,13 @@ mod tests { .unwrap(); let invalid_segwitv0_script = ScriptBuf::from_hex("001161458e330389cd0437ee9fe3641d70cc18").unwrap(); - let expected = Err(Error::UnrecognizedScript); + let expected = Err(FromScriptError::UnrecognizedScript); assert_eq!(Address::from_script(&bad_p2wpkh, Network::Bitcoin), expected); assert_eq!(Address::from_script(&bad_p2wsh, Network::Bitcoin), expected); assert_eq!( Address::from_script(&invalid_segwitv0_script, Network::Bitcoin), - Err(Error::WitnessProgram(witness_program::Error::InvalidSegwitV0Length(17))) + Err(FromScriptError::WitnessProgram(witness_program::Error::InvalidSegwitV0Length(17))) ); }