Skip to content

Commit

Permalink
Address::address_type returns Result instead of Option
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Sep 14, 2021
1 parent 747d97c commit 3d23ac7
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 38 deletions.
72 changes: 40 additions & 32 deletions src/util/address.rs
Expand Up @@ -79,27 +79,37 @@ 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
},
}

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::UnsupportedWitnessVersion { version, length } => write!(
f, "witness version {:?} is not supported yet for witness program length {}", version, length
),
}
}
}
Expand Down Expand Up @@ -523,23 +533,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<AddressType> {
pub fn address_type(&self) -> Result<AddressType, Error> {
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 }),
}
}
}
Expand All @@ -551,7 +559,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).
Expand Down Expand Up @@ -604,7 +612,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
};

Expand Down Expand Up @@ -822,7 +830,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);
}

Expand All @@ -835,7 +843,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);
}

Expand All @@ -851,7 +859,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);
}

Expand All @@ -861,7 +869,7 @@ mod tests {
let addr = Address::p2sh(&script, Testnet);

assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
assert_eq!(addr.address_type(), Ok(AddressType::P2sh));
roundtrips(&addr);
}

Expand All @@ -871,7 +879,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
Expand All @@ -888,7 +896,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);
}

Expand All @@ -898,7 +906,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
Expand All @@ -912,7 +920,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);
}

Expand Down Expand Up @@ -946,8 +954,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);
}

Expand Down
12 changes: 6 additions & 6 deletions src/util/misc.rs
Expand Up @@ -158,14 +158,14 @@ mod message_signing {
) -> Result<bool, secp256k1::Error> {
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,
})
}

Expand Down

0 comments on commit 3d23ac7

Please sign in to comment.