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 17, 2021
1 parent ad60698 commit 8d72ee3
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 40 deletions.
81 changes: 47 additions & 34 deletions src/util/address.rs
Expand Up @@ -79,31 +79,46 @@ pub enum Error {
InvalidSegwitV0ProgramLength(usize),
/// An uncompressed pubkey was used where it is not allowed.
UncompressedPubkey,
/// Witness version which is not (yet) supported by this library.
///
/// It may be in the future that the consensus rules will support more
/// witness versions and corresponding addresses, but this library is not
/// (yet) updated for them. Also, a user can always send money to the output
/// with a future witness version, and construct a corresponding address –
/// but such address will not be parsed by this library.

This comment has been minimized.

Copy link
@Kixunil

Kixunil Sep 17, 2021

Collaborator

I believe it still is parsed it's just some methods will return this error. Am I missing something?

This comment has been minimized.

Copy link
@apoelstra

apoelstra Sep 20, 2021

Member

No, you are correct.

@dr-orlovsky this library can parse any valid witness version. The missing functionality is that the library cannot be used to spend outputs sent to these witness versions. There should be no other missing functionality.

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
}

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")
}
}
}
Expand Down Expand Up @@ -531,23 +546,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 @@ -559,7 +572,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 @@ -612,7 +625,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 @@ -830,7 +843,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 @@ -843,7 +856,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 @@ -859,7 +872,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 @@ -868,7 +881,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);
}

Expand All @@ -884,7 +897,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 @@ -901,7 +914,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 @@ -911,7 +924,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 @@ -925,7 +938,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 @@ -959,8 +972,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 @@ -162,14 +162,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 8d72ee3

Please sign in to comment.