Skip to content

Commit d1509f3

Browse files
committed
Merge rust-bitcoin/rust-bitcoin#808: Refactor logical operators
df7bb03 Simplify read_scriptbool (Tobin Harding) 4b6e866 Refactor is_provably_unspendable (Tobin Harding) e54a2d6 Put && operator at front of line (Tobin Harding) f5512c4 Refactor is_p2pkh (Tobin Harding) 373ea89 Simplify read_scriptbool (Tobin Harding) 654b277 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 rust-bitcoin/rust-bitcoin#806 ACKs for top commit: Kixunil: ACK df7bb03 apoelstra: ACK df7bb03 Tree-SHA512: 06460979d492eb38cefc147397338b7fd95320c66ce8e8b4f8e2b454bb35721ce308413690a0618bd19d695df56175646d4d0c619388c0268f7fd35d5a7b6a3d
2 parents 7bdae14 + 0184b19 commit d1509f3

File tree

2 files changed

+69
-34
lines changed

2 files changed

+69
-34
lines changed

src/blockdata/block.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ impl Block {
195195
// commitment is in the last output that starts with below magic
196196
if let Some(pos) = coinbase.output.iter()
197197
.rposition(|o| {
198-
o.script_pubkey.len () >= 38 &&
199-
o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) {
198+
o.script_pubkey.len () >= 38
199+
&& o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) {
200200
let commitment = WitnessCommitment::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
201201
// witness reserved value is in coinbase input witness
202202
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();

src/blockdata/script.rs

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,10 @@ pub fn read_scriptint(v: &[u8]) -> Result<i64, Error> {
239239
/// else as true", except that the overflow rules don't apply.
240240
#[inline]
241241
pub fn read_scriptbool(v: &[u8]) -> bool {
242-
!(v.is_empty() ||
243-
((v[v.len() - 1] == 0 || v[v.len() - 1] == 0x80) &&
244-
v.iter().rev().skip(1).all(|&w| w == 0)))
242+
match v.split_last() {
243+
Some((last, rest)) => !((last & !0x80 == 0x00) && rest.iter().all(|&b| b == 0)),
244+
None => false,
245+
}
245246
}
246247

247248
/// Read a script-encoded unsigned integer
@@ -417,32 +418,37 @@ impl Script {
417418
/// Checks whether a script pubkey is a p2sh output
418419
#[inline]
419420
pub fn is_p2sh(&self) -> bool {
420-
self.0.len() == 23 &&
421-
self.0[0] == opcodes::all::OP_HASH160.into_u8() &&
422-
self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8() &&
423-
self.0[22] == opcodes::all::OP_EQUAL.into_u8()
421+
self.0.len() == 23
422+
&& self.0[0] == opcodes::all::OP_HASH160.into_u8()
423+
&& self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
424+
&& self.0[22] == opcodes::all::OP_EQUAL.into_u8()
424425
}
425426

426427
/// Checks whether a script pubkey is a p2pkh output
427428
#[inline]
428429
pub fn is_p2pkh(&self) -> bool {
429-
self.0.len() == 25 &&
430-
self.0[0] == opcodes::all::OP_DUP.into_u8() &&
431-
self.0[1] == opcodes::all::OP_HASH160.into_u8() &&
432-
self.0[2] == opcodes::all::OP_PUSHBYTES_20.into_u8() &&
433-
self.0[23] == opcodes::all::OP_EQUALVERIFY.into_u8() &&
434-
self.0[24] == opcodes::all::OP_CHECKSIG.into_u8()
430+
self.0.len() == 25
431+
&& self.0[0] == opcodes::all::OP_DUP.into_u8()
432+
&& self.0[1] == opcodes::all::OP_HASH160.into_u8()
433+
&& self.0[2] == opcodes::all::OP_PUSHBYTES_20.into_u8()
434+
&& self.0[23] == opcodes::all::OP_EQUALVERIFY.into_u8()
435+
&& self.0[24] == opcodes::all::OP_CHECKSIG.into_u8()
435436
}
436437

437438
/// Checks whether a script pubkey is a p2pk output
438439
#[inline]
439440
pub fn is_p2pk(&self) -> bool {
440-
(self.0.len() == 67 &&
441-
self.0[0] == opcodes::all::OP_PUSHBYTES_65.into_u8() &&
442-
self.0[66] == opcodes::all::OP_CHECKSIG.into_u8())
443-
|| (self.0.len() == 35 &&
444-
self.0[0] == opcodes::all::OP_PUSHBYTES_33.into_u8() &&
445-
self.0[34] == opcodes::all::OP_CHECKSIG.into_u8())
441+
match self.len() {
442+
67 => {
443+
self.0[0] == opcodes::all::OP_PUSHBYTES_65.into_u8()
444+
&& self.0[66] == opcodes::all::OP_CHECKSIG.into_u8()
445+
}
446+
35 => {
447+
self.0[0] == opcodes::all::OP_PUSHBYTES_33.into_u8()
448+
&& self.0[34] == opcodes::all::OP_CHECKSIG.into_u8()
449+
}
450+
_ => false
451+
}
446452
}
447453

448454
/// Checks whether a script pubkey is a Segregated Witness (segwit) program.
@@ -468,37 +474,48 @@ impl Script {
468474
/// Checks whether a script pubkey is a p2wsh output
469475
#[inline]
470476
pub fn is_v0_p2wsh(&self) -> bool {
471-
self.0.len() == 34 &&
472-
self.witness_version() == Some(WitnessVersion::V0) &&
473-
self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
477+
self.0.len() == 34
478+
&& self.witness_version() == Some(WitnessVersion::V0)
479+
&& self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
474480
}
475481

476482
/// Checks whether a script pubkey is a p2wpkh output
477483
#[inline]
478484
pub fn is_v0_p2wpkh(&self) -> bool {
479-
self.0.len() == 22 &&
480-
self.witness_version() == Some(WitnessVersion::V0) &&
481-
self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
485+
self.0.len() == 22
486+
&& self.witness_version() == Some(WitnessVersion::V0)
487+
&& self.0[1] == opcodes::all::OP_PUSHBYTES_20.into_u8()
482488
}
483489

484490
/// Checks whether a script pubkey is a P2TR output
485491
#[inline]
486492
pub fn is_v1_p2tr(&self) -> bool {
487-
self.0.len() == 34 &&
488-
self.witness_version() == Some(WitnessVersion::V1) &&
489-
self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
493+
self.0.len() == 34
494+
&& self.witness_version() == Some(WitnessVersion::V1)
495+
&& self.0[1] == opcodes::all::OP_PUSHBYTES_32.into_u8()
490496
}
491497

492498
/// Check if this is an OP_RETURN output
493499
pub fn is_op_return (&self) -> bool {
494-
!self.0.is_empty() && (opcodes::All::from(self.0[0]) == opcodes::all::OP_RETURN)
500+
match self.0.first() {
501+
Some(b) => *b == opcodes::all::OP_RETURN.into_u8(),
502+
None => false
503+
}
495504
}
496505

497506
/// Whether a script can be proven to have no satisfying input
498507
pub fn is_provably_unspendable(&self) -> bool {
499-
!self.0.is_empty() &&
500-
(opcodes::All::from(self.0[0]).classify(opcodes::ClassifyContext::Legacy) == opcodes::Class::ReturnOp ||
501-
opcodes::All::from(self.0[0]).classify(opcodes::ClassifyContext::Legacy) == opcodes::Class::IllegalOp)
508+
use blockdata::opcodes::Class::{ReturnOp, IllegalOp};
509+
510+
match self.0.first() {
511+
Some(b) => {
512+
let first = opcodes::All::from(*b);
513+
let class = first.classify(opcodes::ClassifyContext::Legacy);
514+
515+
class == ReturnOp || class == IllegalOp
516+
},
517+
None => false,
518+
}
502519
}
503520

504521
/// Gets the minimum value an output with this script should have in order to be
@@ -1448,5 +1465,23 @@ mod test {
14481465
assert!(instructions.next().is_none());
14491466
assert!(instructions.next().is_none());
14501467
}
1468+
1469+
#[test]
1470+
fn read_scriptbool_zero_is_false() {
1471+
let v: Vec<u8> = vec![0x00, 0x00, 0x00, 0x00];
1472+
assert!(!read_scriptbool(&v));
1473+
1474+
let v: Vec<u8> = vec![0x00, 0x00, 0x00, 0x80]; // With sign bit set.
1475+
assert!(!read_scriptbool(&v));
1476+
}
1477+
1478+
#[test]
1479+
fn read_scriptbool_non_zero_is_true() {
1480+
let v: Vec<u8> = vec![0x01, 0x00, 0x00, 0x00];
1481+
assert!(read_scriptbool(&v));
1482+
1483+
let v: Vec<u8> = vec![0x01, 0x00, 0x00, 0x80]; // With sign bit set.
1484+
assert!(read_scriptbool(&v));
1485+
}
14511486
}
14521487

0 commit comments

Comments
 (0)