From 05b24946eb504d599a4b9d0faafaea966a0eb447 Mon Sep 17 00:00:00 2001 From: harshit933 Date: Fri, 23 Feb 2024 01:48:59 +0530 Subject: [PATCH 1/3] 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))) ); } From 5182a8d7a8084e197a6abd84e96af78902a162b2 Mon Sep 17 00:00:00 2001 From: harshit933 Date: Fri, 23 Feb 2024 03:36:35 +0530 Subject: [PATCH 2/3] Remove unused variants from `Address::Error` --- bitcoin/src/address/error.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs index 45e5e99b..0bddd213 100644 --- a/bitcoin/src/address/error.rs +++ b/bitcoin/src/address/error.rs @@ -13,14 +13,8 @@ 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 +31,7 @@ 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)?; @@ -57,22 +48,12 @@ impl std::error::Error for Error { use Error::*; match self { - WitnessVersion(e) => Some(e), - WitnessProgram(e) => Some(e), UnknownHrp(e) => Some(e), - ExcessiveScriptSize | UnrecognizedScript | NetworkValidation { .. } => None, + ExcessiveScriptSize | NetworkValidation { .. } => None, } } } -impl From for Error { - fn from(e: witness_version::TryFromError) -> Error { Error::WitnessVersion(e) } -} - -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 { From c2d658ac05cb6d5f09c4d73c7368b85563654269 Mon Sep 17 00:00:00 2001 From: harshit933 Date: Fri, 23 Feb 2024 03:44:12 +0530 Subject: [PATCH 3/3] Add `P2shError` for handling errors related to P2sh Added a new `P2shError` struct for handling errors emmited while generating addresses from P2sh scripts. --- bitcoin/src/address/error.rs | 49 ++++++++++++++++++++++++++++-------- bitcoin/src/address/mod.rs | 7 +++--- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs index 0bddd213..c522656e 100644 --- a/bitcoin/src/address/error.rs +++ b/bitcoin/src/address/error.rs @@ -13,8 +13,6 @@ use crate::{base58, Network}; #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] pub enum Error { - /// Address size more than 520 bytes is not allowed. - ExcessiveScriptSize, /// Address's network differs from required one. NetworkValidation { /// Network that was required. @@ -31,7 +29,6 @@ impl fmt::Display for Error { use Error::*; match *self { - ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"), NetworkValidation { required, ref address } => { write!(f, "address ")?; fmt::Display::fmt(&address.0, f)?; @@ -47,15 +44,16 @@ impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use Error::*; - match self { - UnknownHrp(e) => Some(e), - ExcessiveScriptSize | NetworkValidation { .. } => None, + match *self { + UnknownHrp(ref e) => Some(e), + NetworkValidation { .. } => None, } } } /// 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, @@ -82,20 +80,49 @@ 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), + 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 { FromScriptError::WitnessProgram(e)} + fn from(e : witness_program::Error) -> Self { Self::WitnessProgram(e) } } impl From for FromScriptError { - fn from(e: witness_version::TryFromError) -> Self { FromScriptError::WitnessVersion(e) } + 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 4059853d..a8e058e6 100644 --- a/bitcoin/src/address/mod.rs +++ b/bitcoin/src/address/mod.rs @@ -51,6 +51,7 @@ 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::{ @@ -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)) @@ -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]