From 1767bccefa28755b427da659e54d845efa3856c0 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 10 Sep 2021 10:29:45 +0200 Subject: [PATCH] Address::address_type returns Result instead of Option --- src/util/address.rs | 75 +++++++++++++++++++++++++-------------------- src/util/misc.rs | 12 ++++---- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/util/address.rs b/src/util/address.rs index d8fafe5cf2..7b69772a1e 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -79,6 +79,13 @@ pub enum Error { InvalidSegwitV0ProgramLength(usize), /// An uncompressed pubkey was used where it is not allowed. UncompressedPubkey, + /// Witness version which is not (yet) supported + UnsupportedWitnessVersion { + /// Witness version that was found during address decoding + version: WitnessVersion, + /// Length of the witness program + length: usize + }, /// Address size more than 520 bytes is not allowed. ExcessiveScriptSize } @@ -86,24 +93,26 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - Error::Base58(_) => write!(f, "base58 address encoding error"), - Error::Bech32(_) => write!(f, "bech32 address encoding error"), - Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"), + Error::Base58(_) => f.write_str("base58 address encoding error"), + Error::Bech32(_) => f.write_str( "bech32 address encoding error"), + Error::EmptyBech32Payload => f.write_str( "the bech32 payload was empty"), Error::InvalidBech32Variant { expected, found } => write!(f, "invalid bech32 checksum variant found {:?} when {:?} was expected", found, expected), Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v), - Error::UnparsableWitnessVersion(_) => write!(f, "incorrect format of a witness version byte"), + Error::UnparsableWitnessVersion(_) => f.write_str( "incorrect format of a witness version byte"), Error::MalformedWitnessVersion => f.write_str("bitcoin script opcode does not match any known witness version, the script is malformed"), - Error::InvalidWitnessProgramLength(l) => write!(f, - "the witness program must be between 2 and 40 bytes in length: length={}", l, + Error::InvalidWitnessProgramLength(len) => write!(f, + "the witness program must be between 2 and 40 bytes in length: length={}", len, ), - Error::InvalidSegwitV0ProgramLength(l) => write!(f, - "a v0 witness program must be either of length 20 or 32 bytes: length={}", l, + Error::InvalidSegwitV0ProgramLength(len) => write!(f, + "a v0 witness program must be either of length 20 or 32 bytes: length={}", len, ), - Error::UncompressedPubkey => write!(f, + Error::UncompressedPubkey => f.write_str( "an uncompressed pubkey was used where it is not allowed", ), - Error::ExcessiveScriptSize => write!(f, - "Script size exceed 520 bytes") + Error::UnsupportedWitnessVersion { version, length } => write!( + f, "witness version {:?} is not supported yet for witness program length {}", version, length + ), + Error::ExcessiveScriptSize => write!(f, "Script size exceed 520 bytes") } } } @@ -531,23 +540,21 @@ impl Address { /// Get the address type of the address. /// None if unknown, non-standard or related to the future witness version. - pub fn address_type(&self) -> Option { + pub fn address_type(&self) -> Result { match self.payload { - Payload::PubkeyHash(_) => Some(AddressType::P2pkh), - Payload::ScriptHash(_) => Some(AddressType::P2sh), + Payload::PubkeyHash(_) => Ok(AddressType::P2pkh), + Payload::ScriptHash(_) => Ok(AddressType::P2sh), Payload::WitnessProgram { version, program: ref prog, } => { // BIP-141 p2wpkh or p2wsh addresses. - match version { - WitnessVersion::V0 => match prog.len() { - 20 => Some(AddressType::P2wpkh), - 32 => Some(AddressType::P2wsh), - _ => None, - }, - WitnessVersion::V1 if prog.len() == 32 => Some(AddressType::P2tr), - _ => None, + match (version, prog.len()) { + (WitnessVersion::V0, 20) => Ok(AddressType::P2wpkh), + (WitnessVersion::V0, 32) => Ok(AddressType::P2wsh), + (WitnessVersion::V0, len) => Err(Error::InvalidWitnessProgramLength(len)), + (WitnessVersion::V1, 32) => Ok(AddressType::P2tr), + (version, length) => Err(Error::UnsupportedWitnessVersion { version, length }), } } } @@ -559,7 +566,7 @@ impl Address { /// SegWit addresses with unassigned witness versions or non-standard /// program sizes are considered non-standard. pub fn is_standard(&self) -> bool { - self.address_type().is_some() + self.address_type().is_ok() } /// Get an [Address] from an output script (scriptPubkey). @@ -612,7 +619,7 @@ impl Address { /// ``` pub fn is_valid_for_network(&self, network: Network) -> bool { let is_legacy = match self.address_type() { - Some(AddressType::P2pkh) | Some(AddressType::P2sh) => true, + Ok(AddressType::P2pkh) | Ok(AddressType::P2sh) => true, _ => false }; @@ -830,7 +837,7 @@ mod tests { hex_script!("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac") ); assert_eq!(&addr.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); - assert_eq!(addr.address_type(), Some(AddressType::P2pkh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2pkh)); roundtrips(&addr); } @@ -843,7 +850,7 @@ mod tests { let key = hex_key!(&"03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f"); let addr = Address::p2pkh(&key, Testnet); assert_eq!(&addr.to_string(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC"); - assert_eq!(addr.address_type(), Some(AddressType::P2pkh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2pkh)); roundtrips(&addr); } @@ -859,7 +866,7 @@ mod tests { hex_script!("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087") ); assert_eq!(&addr.to_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"); - assert_eq!(addr.address_type(), Some(AddressType::P2sh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2sh)); roundtrips(&addr); } @@ -868,7 +875,7 @@ mod tests { let script = hex_script!("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae"); let addr = Address::p2sh(&script, Testnet).unwrap(); assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"); - assert_eq!(addr.address_type(), Some(AddressType::P2sh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2sh)); roundtrips(&addr); } @@ -884,7 +891,7 @@ mod tests { let mut key = hex_key!("033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc"); let addr = Address::p2wpkh(&key, Bitcoin).unwrap(); assert_eq!(&addr.to_string(), "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw"); - assert_eq!(addr.address_type(), Some(AddressType::P2wpkh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2wpkh)); roundtrips(&addr); // Test uncompressed pubkey @@ -901,7 +908,7 @@ mod tests { &addr.to_string(), "bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej" ); - assert_eq!(addr.address_type(), Some(AddressType::P2wsh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2wsh)); roundtrips(&addr); } @@ -911,7 +918,7 @@ mod tests { let mut key = hex_key!("026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766"); let addr = Address::p2shwpkh(&key, Bitcoin).unwrap(); assert_eq!(&addr.to_string(), "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE"); - assert_eq!(addr.address_type(), Some(AddressType::P2sh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2sh)); roundtrips(&addr); // Test uncompressed pubkey @@ -925,7 +932,7 @@ mod tests { let script = hex_script!("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae"); let addr = Address::p2shwsh(&script, Bitcoin); assert_eq!(&addr.to_string(), "36EqgNnsWW94SreZgBWc1ANC6wpFZwirHr"); - assert_eq!(addr.address_type(), Some(AddressType::P2sh)); + assert_eq!(addr.address_type(), Ok(AddressType::P2sh)); roundtrips(&addr); } @@ -959,8 +966,8 @@ mod tests { ("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798") ]; for vector in &valid_vectors { - let addr: Address = vector.0.parse().unwrap(); - assert_eq!(&addr.script_pubkey().as_bytes().to_hex(), vector.1); + let addr: Address = vector.0.parse().expect(&format!("error in test vector {}", vector.0)); + assert_eq!(&addr.script_pubkey().as_bytes().to_hex(), vector.1, "failure for test vector {}", vector.0); roundtrips(&addr); } diff --git a/src/util/misc.rs b/src/util/misc.rs index b4de4e118e..89eb8b86f7 100644 --- a/src/util/misc.rs +++ b/src/util/misc.rs @@ -162,14 +162,14 @@ mod message_signing { ) -> Result { let pubkey = self.recover_pubkey(&secp_ctx, msg_hash)?; Ok(match address.address_type() { - Some(AddressType::P2pkh) => { + Ok(AddressType::P2pkh) => { *address == Address::p2pkh(&pubkey, address.network) } - Some(AddressType::P2sh) => false, - Some(AddressType::P2wpkh) => false, - Some(AddressType::P2wsh) => false, - Some(AddressType::P2tr) => false, - None => false, + Ok(AddressType::P2sh) => false, + Ok(AddressType::P2wpkh) => false, + Ok(AddressType::P2wsh) => false, + Ok(AddressType::P2tr) => false, + Err(_) => false, }) }