diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index 8ca1fa0c..28aeda59 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -195,8 +195,8 @@ impl Block { // commitment is in the last output that starts with below magic if let Some(pos) = coinbase.output.iter() .rposition(|o| { - o.script_pubkey.len () >= 38 && - o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) { + o.script_pubkey.len () >= 38 + && o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) { let commitment = WitnessCommitment::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap(); // witness reserved value is in coinbase input witness let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect(); diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 88406702..81d7568b 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -239,9 +239,10 @@ pub fn read_scriptint(v: &[u8]) -> Result { /// else as true", except that the overflow rules don't apply. #[inline] pub fn read_scriptbool(v: &[u8]) -> bool { - !(v.is_empty() || - ((v[v.len() - 1] == 0 || v[v.len() - 1] == 0x80) && - v.iter().rev().skip(1).all(|&w| w == 0))) + match v.split_last() { + Some((last, rest)) => !((last & !0x80 == 0x00) && rest.iter().all(|&b| b == 0)), + None => false, + } } /// Read a script-encoded unsigned integer @@ -417,32 +418,37 @@ impl Script { /// Checks whether a script pubkey is a p2sh output #[inline] pub fn is_p2sh(&self) -> bool { - self.0.len() == 23 && - self.0[0] == opcodes::all::OP_HASH160.into_u8() && - self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() && - self.0[22] == opcodes::all::OP_EQUAL.into_u8() + self.0.len() == 23 + && self.0[0] == opcodes::all::OP_HASH160.into_u8() + && self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() + && self.0[22] == opcodes::all::OP_EQUAL.into_u8() } /// Checks whether a script pubkey is a p2pkh output #[inline] pub fn is_p2pkh(&self) -> bool { - self.0.len() == 25 && - self.0[0] == opcodes::all::OP_DUP.into_u8() && - self.0[1] == opcodes::all::OP_HASH160.into_u8() && - self.0[2] == opcodes::all::OP_PUSHBYTES_20.into_u8() && - self.0[23] == opcodes::all::OP_EQUALVERIFY.into_u8() && - self.0[24] == opcodes::all::OP_CHECKSIG.into_u8() + self.0.len() == 25 + && self.0[0] == opcodes::all::OP_DUP.into_u8() + && self.0[1] == opcodes::all::OP_HASH160.into_u8() + && self.0[2] == opcodes::all::OP_PUSHBYTES_20.into_u8() + && self.0[23] == opcodes::all::OP_EQUALVERIFY.into_u8() + && self.0[24] == opcodes::all::OP_CHECKSIG.into_u8() } /// Checks whether a script pubkey is a p2pk output #[inline] pub fn is_p2pk(&self) -> bool { - (self.0.len() == 67 && - self.0[0] == opcodes::all::OP_PUSHBYTES_65.into_u8() && - self.0[66] == opcodes::all::OP_CHECKSIG.into_u8()) - || (self.0.len() == 35 && - self.0[0] == opcodes::all::OP_PUSHBYTES_33.into_u8() && - self.0[34] == opcodes::all::OP_CHECKSIG.into_u8()) + match self.len() { + 67 => { + self.0[0] == opcodes::all::OP_PUSHBYTES_65.into_u8() + && self.0[66] == opcodes::all::OP_CHECKSIG.into_u8() + } + 35 => { + self.0[0] == opcodes::all::OP_PUSHBYTES_33.into_u8() + && self.0[34] == opcodes::all::OP_CHECKSIG.into_u8() + } + _ => false + } } /// Checks whether a script pubkey is a Segregated Witness (segwit) program. @@ -468,37 +474,48 @@ impl Script { /// Checks whether a script pubkey is a p2wsh output #[inline] pub fn is_v0_p2wsh(&self) -> bool { - self.0.len() == 34 && - self.witness_version() == Some(WitnessVersion::V0) && - self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8() + self.0.len() == 34 + && self.witness_version() == Some(WitnessVersion::V0) + && self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8() } /// Checks whether a script pubkey is a p2wpkh output #[inline] pub fn is_v0_p2wpkh(&self) -> bool { - self.0.len() == 22 && - self.witness_version() == Some(WitnessVersion::V0) && - self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() + self.0.len() == 22 + && self.witness_version() == Some(WitnessVersion::V0) + && self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() } /// Checks whether a script pubkey is a P2TR output #[inline] pub fn is_v1_p2tr(&self) -> bool { - self.0.len() == 34 && - self.witness_version() == Some(WitnessVersion::V1) && - self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8() + self.0.len() == 34 + && self.witness_version() == Some(WitnessVersion::V1) + && self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8() } /// Check if this is an OP_RETURN output pub fn is_op_return (&self) -> bool { - !self.0.is_empty() && (opcodes::All::from(self.0[0]) == opcodes::all::OP_RETURN) + match self.0.first() { + Some(b) => *b == opcodes::all::OP_RETURN.into_u8(), + None => false + } } /// Whether a script can be proven to have no satisfying input pub fn is_provably_unspendable(&self) -> bool { - !self.0.is_empty() && - (opcodes::All::from(self.0[0]).classify(opcodes::ClassifyContext::Legacy) == opcodes::Class::ReturnOp || - opcodes::All::from(self.0[0]).classify(opcodes::ClassifyContext::Legacy) == opcodes::Class::IllegalOp) + use blockdata::opcodes::Class::{ReturnOp, IllegalOp}; + + match self.0.first() { + Some(b) => { + let first = opcodes::All::from(*b); + let class = first.classify(opcodes::ClassifyContext::Legacy); + + class == ReturnOp || class == IllegalOp + }, + None => false, + } } /// Gets the minimum value an output with this script should have in order to be @@ -1448,5 +1465,23 @@ mod test { assert!(instructions.next().is_none()); assert!(instructions.next().is_none()); } + + #[test] + fn read_scriptbool_zero_is_false() { + let v: Vec = vec![0x00, 0x00, 0x00, 0x00]; + assert!(!read_scriptbool(&v)); + + let v: Vec = vec![0x00, 0x00, 0x00, 0x80]; // With sign bit set. + assert!(!read_scriptbool(&v)); + } + + #[test] + fn read_scriptbool_non_zero_is_true() { + let v: Vec = vec![0x01, 0x00, 0x00, 0x00]; + assert!(read_scriptbool(&v)); + + let v: Vec = vec![0x01, 0x00, 0x00, 0x80]; // With sign bit set. + assert!(read_scriptbool(&v)); + } }