Skip to content

Commit

Permalink
Merge #1038: Modify from_script functions in address.rs to return result
Browse files Browse the repository at this point in the history
66e852c Update format of ExcessiveScriptSize error message (eunoia_1729)
89bd4b6 Modify from_script functions in address.rs to return result (eunoia_1729)

Pull request description:

  Modify from_script functions to return result instead of option so that, in case of errors, there is more
  information on what went wrong.

  Resolves: #1022

ACKs for top commit:
  sanket1729:
    ACK 66e852c. LGTM
  tcharding:
    ACK 66e852c
  apoelstra:
    ACK 66e852c

Tree-SHA512: 0d9529aee0a5459351bed2cc8b2c5571736d3293e2931c43d98f53330e9ac5f3d998a19da2b4575af0a3c1c4dcfd5a24c8813390bf6f5492a689c36ebb9cb426
  • Loading branch information
apoelstra committed Jun 8, 2022
2 parents 8fd7008 + 66e852c commit 3ed3d57
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 19 deletions.
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/deserialize_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn do_test(data: &[u8]) {
assert_eq!(data, &encode::serialize(&script)[..]);

// Check if valid address and if that address roundtrips.
if let Some(addr) = Address::from_script(&script, Network::Bitcoin) {
if let Ok(addr) = Address::from_script(&script, Network::Bitcoin) {
assert_eq!(addr.script_pubkey(), script);
}
}
Expand Down
39 changes: 21 additions & 18 deletions src/util/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ pub enum Error {
/// An uncompressed pubkey was used where it is not allowed.
UncompressedPubkey,
/// Address size more than 520 bytes is not allowed.
ExcessiveScriptSize
ExcessiveScriptSize,
/// Script is not a p2pkh, p2sh or witness program.
UnrecognizedScript,
}

impl fmt::Display for Error {
Expand All @@ -97,7 +99,8 @@ impl fmt::Display for Error {
Error::InvalidWitnessProgramLength(l) => write!(f, "the witness program must be between 2 and 40 bytes in length: length={}", l),
Error::InvalidSegwitV0ProgramLength(l) => write!(f, "a v0 witness program must be either of length 20 or 32 bytes: length={}", l),
Error::UncompressedPubkey => write!(f, "an uncompressed pubkey was used where it is not allowed"),
Error::ExcessiveScriptSize => write!(f, "Script size exceed 520 bytes"),
Error::ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"),
Error::UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program")
}
}
}
Expand All @@ -119,7 +122,8 @@ impl std::error::Error for Error {
| InvalidWitnessProgramLength(_)
| InvalidSegwitV0ProgramLength(_)
| UncompressedPubkey
| ExcessiveScriptSize => None,
| ExcessiveScriptSize
| UnrecognizedScript => None,
}
}
}
Expand Down Expand Up @@ -386,8 +390,8 @@ pub enum Payload {

impl Payload {
/// Constructs a [Payload] from an output script (`scriptPubkey`).
pub fn from_script(script: &script::Script) -> Option<Payload> {
Some(if script.is_p2pkh() {
pub fn from_script(script: &script::Script) -> Result<Payload, Error> {
Ok(if script.is_p2pkh() {
let mut hash_inner = [0u8; 20];
hash_inner.copy_from_slice(&script.as_bytes()[3..23]);
Payload::PubkeyHash(PubkeyHash::from_inner(hash_inner))
Expand All @@ -396,12 +400,16 @@ impl Payload {
hash_inner.copy_from_slice(&script.as_bytes()[2..22]);
Payload::ScriptHash(ScriptHash::from_inner(hash_inner))
} else if script.is_witness_program() {
if script.witness_version() == Some(WitnessVersion::V0) && !(script.is_v0_p2wpkh() || script.is_v0_p2wsh()) {
return Err(Error::InvalidSegwitV0ProgramLength(script.len()));
}

Payload::WitnessProgram {
version: WitnessVersion::from_opcode(opcodes::All::from(script[0])).ok()?,
version: WitnessVersion::from_opcode(opcodes::All::from(script[0]))?,
program: script[2..].to_vec(),
}
} else {
return None;
return Err(Error::UnrecognizedScript);
})
}

Expand Down Expand Up @@ -692,14 +700,8 @@ impl Address {
}

/// Constructs an [`Address`] from an output script (`scriptPubkey`).
pub fn from_script(script: &script::Script, network: Network) -> Option<Address> {
if script.is_witness_program()
&& script.witness_version() == Some(WitnessVersion::V0)
&& !(script.is_v0_p2wpkh() || script.is_v0_p2wsh()) {
return None
}

Some(Address {
pub fn from_script(script: &script::Script, network: Network) -> Result<Address, Error> {
Ok(Address {
payload: Payload::from_script(script)?,
network,
})
Expand Down Expand Up @@ -959,7 +961,7 @@ mod tests {
);
assert_eq!(
Address::from_script(&addr.script_pubkey(), addr.network).as_ref(),
Some(addr),
Ok(addr),
"script round-trip failed for {}",
addr,
);
Expand Down Expand Up @@ -1428,8 +1430,9 @@ mod tests {
fn test_fail_address_from_script() {
let bad_p2wpkh = hex_script!("0014dbc5b0a8f9d4353b4b54c3db48846bb15abfec");
let bad_p2wsh = hex_script!("00202d4fa2eb233d008cc83206fa2f4f2e60199000f5b857a835e3172323385623");
let expected = Err(Error::UnrecognizedScript);

assert_eq!(Address::from_script(&bad_p2wpkh, Network::Bitcoin), None);
assert_eq!(Address::from_script(&bad_p2wsh, Network::Bitcoin), None);
assert_eq!(Address::from_script(&bad_p2wpkh, Network::Bitcoin), expected);
assert_eq!(Address::from_script(&bad_p2wsh, Network::Bitcoin), expected);
}
}

0 comments on commit 3ed3d57

Please sign in to comment.