From 05b24946eb504d599a4b9d0faafaea966a0eb447 Mon Sep 17 00:00:00 2001 From: harshit933 Date: Fri, 23 Feb 2024 01:48:59 +0530 Subject: [PATCH] Add the `FromScriptError` for handling errors in `address` This commit adds the `FromScriptError` struct to handle the errors while generating address from any script. It includes : - Unrecognized script error. - Witness Program error. - Witness Version error. --- bitcoin/src/address/error.rs | 44 ++++++++++++++++++++++++++++++++++++ bitcoin/src/address/mod.rs | 10 ++++---- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs index cbb1cb43..45e5e99b 100644 --- a/bitcoin/src/address/error.rs +++ b/bitcoin/src/address/error.rs @@ -73,6 +73,50 @@ impl From for Error { fn from(e: witness_program::Error) -> Error { Error::WitnessProgram(e) } } +/// Error while generating address from script. +#[derive(Debug, Clone, PartialEq, Eq)] +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 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 { + WitnessVersion(e) => Some(e), + WitnessProgram(e) => Some(e), + UnrecognizedScript => None, + } + } +} + +impl From for FromScriptError { + fn from(e : witness_program::Error) -> Self { FromScriptError::WitnessProgram(e)} +} + +impl From for FromScriptError { + fn from(e: witness_version::TryFromError) -> Self { FromScriptError::WitnessVersion(e) } +} + /// Address type is either invalid or not supported in rust-bitcoin. #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs index 8d97edab..4059853d 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -54,7 +54,7 @@ use crate::taproot::TapNodeHash; #[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. @@ -503,7 +503,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 +519,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) } } @@ -1236,13 +1236,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))) ); }