Merge rust-bitcoin/rust-bitcoin#808: Refactor logical operators
df7bb03a67
Simplify read_scriptbool (Tobin Harding)4b6e86658d
Refactor is_provably_unspendable (Tobin Harding)e54a2d653b
Put && operator at front of line (Tobin Harding)f5512c4931
Refactor is_p2pkh (Tobin Harding)373ea89a9a
Simplify read_scriptbool (Tobin Harding)654b2772b8
Add passing unit tests for read_scriptbool (Tobin Harding) Pull request description: In an effort to make the code clearer and more explicit, do various refactorings around logical operators. Each done as a separate patch to ease review and limit scope of discussion. Based on review of https://github.com/rust-bitcoin/rust-bitcoin/pull/806 ACKs for top commit: Kixunil: ACKdf7bb03a67
apoelstra: ACKdf7bb03a67
Tree-SHA512: 06460979d492eb38cefc147397338b7fd95320c66ce8e8b4f8e2b454bb35721ce308413690a0618bd19d695df56175646d4d0c619388c0268f7fd35d5a7b6a3d
This commit is contained in:
commit
cb35766979
|
@ -195,8 +195,8 @@ impl Block {
|
||||||
// commitment is in the last output that starts with below magic
|
// commitment is in the last output that starts with below magic
|
||||||
if let Some(pos) = coinbase.output.iter()
|
if let Some(pos) = coinbase.output.iter()
|
||||||
.rposition(|o| {
|
.rposition(|o| {
|
||||||
o.script_pubkey.len () >= 38 &&
|
o.script_pubkey.len () >= 38
|
||||||
o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) {
|
&& 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();
|
let commitment = WitnessCommitment::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
|
||||||
// witness reserved value is in coinbase input witness
|
// witness reserved value is in coinbase input witness
|
||||||
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();
|
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();
|
||||||
|
|
|
@ -239,9 +239,10 @@ pub fn read_scriptint(v: &[u8]) -> Result<i64, Error> {
|
||||||
/// else as true", except that the overflow rules don't apply.
|
/// else as true", except that the overflow rules don't apply.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_scriptbool(v: &[u8]) -> bool {
|
pub fn read_scriptbool(v: &[u8]) -> bool {
|
||||||
!(v.is_empty() ||
|
match v.split_last() {
|
||||||
((v[v.len() - 1] == 0 || v[v.len() - 1] == 0x80) &&
|
Some((last, rest)) => !((last & !0x80 == 0x00) && rest.iter().all(|&b| b == 0)),
|
||||||
v.iter().rev().skip(1).all(|&w| w == 0)))
|
None => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a script-encoded unsigned integer
|
/// Read a script-encoded unsigned integer
|
||||||
|
@ -417,32 +418,37 @@ impl Script {
|
||||||
/// Checks whether a script pubkey is a p2sh output
|
/// Checks whether a script pubkey is a p2sh output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_p2sh(&self) -> bool {
|
pub fn is_p2sh(&self) -> bool {
|
||||||
self.0.len() == 23 &&
|
self.0.len() == 23
|
||||||
self.0[0] == opcodes::all::OP_HASH160.into_u8() &&
|
&& self.0[0] == opcodes::all::OP_HASH160.into_u8()
|
||||||
self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() &&
|
&& self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
|
||||||
self.0[22] == opcodes::all::OP_EQUAL.into_u8()
|
&& self.0[22] == opcodes::all::OP_EQUAL.into_u8()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a script pubkey is a p2pkh output
|
/// Checks whether a script pubkey is a p2pkh output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_p2pkh(&self) -> bool {
|
pub fn is_p2pkh(&self) -> bool {
|
||||||
self.0.len() == 25 &&
|
self.0.len() == 25
|
||||||
self.0[0] == opcodes::all::OP_DUP.into_u8() &&
|
&& self.0[0] == opcodes::all::OP_DUP.into_u8()
|
||||||
self.0[1] == opcodes::all::OP_HASH160.into_u8() &&
|
&& self.0[1] == opcodes::all::OP_HASH160.into_u8()
|
||||||
self.0[2] == opcodes::all::OP_PUSHBYTES_20.into_u8() &&
|
&& self.0[2] == opcodes::all::OP_PUSHBYTES_20.into_u8()
|
||||||
self.0[23] == opcodes::all::OP_EQUALVERIFY.into_u8() &&
|
&& self.0[23] == opcodes::all::OP_EQUALVERIFY.into_u8()
|
||||||
self.0[24] == opcodes::all::OP_CHECKSIG.into_u8()
|
&& self.0[24] == opcodes::all::OP_CHECKSIG.into_u8()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a script pubkey is a p2pk output
|
/// Checks whether a script pubkey is a p2pk output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_p2pk(&self) -> bool {
|
pub fn is_p2pk(&self) -> bool {
|
||||||
(self.0.len() == 67 &&
|
match self.len() {
|
||||||
self.0[0] == opcodes::all::OP_PUSHBYTES_65.into_u8() &&
|
67 => {
|
||||||
self.0[66] == opcodes::all::OP_CHECKSIG.into_u8())
|
self.0[0] == opcodes::all::OP_PUSHBYTES_65.into_u8()
|
||||||
|| (self.0.len() == 35 &&
|
&& self.0[66] == opcodes::all::OP_CHECKSIG.into_u8()
|
||||||
self.0[0] == opcodes::all::OP_PUSHBYTES_33.into_u8() &&
|
}
|
||||||
self.0[34] == 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.
|
/// 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
|
/// Checks whether a script pubkey is a p2wsh output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_v0_p2wsh(&self) -> bool {
|
pub fn is_v0_p2wsh(&self) -> bool {
|
||||||
self.0.len() == 34 &&
|
self.0.len() == 34
|
||||||
self.witness_version() == Some(WitnessVersion::V0) &&
|
&& self.witness_version() == Some(WitnessVersion::V0)
|
||||||
self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
|
&& self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a script pubkey is a p2wpkh output
|
/// Checks whether a script pubkey is a p2wpkh output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_v0_p2wpkh(&self) -> bool {
|
pub fn is_v0_p2wpkh(&self) -> bool {
|
||||||
self.0.len() == 22 &&
|
self.0.len() == 22
|
||||||
self.witness_version() == Some(WitnessVersion::V0) &&
|
&& self.witness_version() == Some(WitnessVersion::V0)
|
||||||
self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
|
&& self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a script pubkey is a P2TR output
|
/// Checks whether a script pubkey is a P2TR output
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_v1_p2tr(&self) -> bool {
|
pub fn is_v1_p2tr(&self) -> bool {
|
||||||
self.0.len() == 34 &&
|
self.0.len() == 34
|
||||||
self.witness_version() == Some(WitnessVersion::V1) &&
|
&& self.witness_version() == Some(WitnessVersion::V1)
|
||||||
self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
|
&& self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this is an OP_RETURN output
|
/// Check if this is an OP_RETURN output
|
||||||
pub fn is_op_return (&self) -> bool {
|
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
|
/// Whether a script can be proven to have no satisfying input
|
||||||
pub fn is_provably_unspendable(&self) -> bool {
|
pub fn is_provably_unspendable(&self) -> bool {
|
||||||
!self.0.is_empty() &&
|
use blockdata::opcodes::Class::{ReturnOp, IllegalOp};
|
||||||
(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)
|
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
|
/// 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());
|
||||||
assert!(instructions.next().is_none());
|
assert!(instructions.next().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_scriptbool_zero_is_false() {
|
||||||
|
let v: Vec<u8> = vec![0x00, 0x00, 0x00, 0x00];
|
||||||
|
assert!(!read_scriptbool(&v));
|
||||||
|
|
||||||
|
let v: Vec<u8> = vec![0x00, 0x00, 0x00, 0x80]; // With sign bit set.
|
||||||
|
assert!(!read_scriptbool(&v));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_scriptbool_non_zero_is_true() {
|
||||||
|
let v: Vec<u8> = vec![0x01, 0x00, 0x00, 0x00];
|
||||||
|
assert!(read_scriptbool(&v));
|
||||||
|
|
||||||
|
let v: Vec<u8> = vec![0x01, 0x00, 0x00, 0x80]; // With sign bit set.
|
||||||
|
assert!(read_scriptbool(&v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue