Skip to content

Commit 2d75d84

Browse files
committed
Move repeated code to functions in script
This creates a few primitive functions for handling iterators and uses them to avoid repeated code. As a result not only is the code simpler but also fixes a forgotten bound check. Thanks to a helper function which always does bounds check correctly this can no longer be forgotten.
1 parent 97c8005 commit 2d75d84

File tree

1 file changed

+36
-21
lines changed

1 file changed

+36
-21
lines changed

src/blockdata/script.rs

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,26 @@ pub struct Instructions<'a> {
734734
}
735735

736736
impl<'a> Instructions<'a> {
737+
/// Set the iterator to end so that it won't iterate any longer
738+
fn kill(&mut self) {
739+
let len = self.data.len();
740+
self.data.nth(len.max(1) - 1);
741+
}
742+
743+
/// takes `len` bytes long slice from iterator and returns it advancing iterator
744+
/// if the iterator is not long enough `None` is returned and the iterator is killed
745+
/// to avoid returning an infinite stream of errors.
746+
fn take_slice_or_kill(&mut self, len: usize) -> Option<&'a [u8]> {
747+
if self.data.len() >= len {
748+
let slice = &self.data.as_slice()[..len];
749+
self.data.nth(len.max(1) - 1);
750+
Some(slice)
751+
} else {
752+
self.kill();
753+
None
754+
}
755+
}
756+
737757
fn next_push_data_len(&mut self, len: usize, max: usize) -> Option<Result<Instruction<'a>, Error>> {
738758
let n = match read_uint_iter(&mut self.data, len) {
739759
Ok(n) => n,
@@ -742,19 +762,15 @@ impl<'a> Instructions<'a> {
742762
// Overflow actually means early end of script (script is definitely shorter
743763
// than `usize::max_value()`)
744764
Err(UintError::EarlyEndOfScript) | Err(UintError::NumericOverflow) => {
745-
let data_len = self.data.len();
746-
self.data.nth(data_len); // Kill iterator so that it does not return an infinite stream of errors
765+
self.kill();
747766
return Some(Err(Error::EarlyEndOfScript));
748767
},
749768
};
750769
if self.enforce_minimal && n < max {
751-
let data_len = self.data.len();
752-
self.data.nth(data_len); // Kill iterator so that it does not return an infinite stream of errors
770+
self.kill();
753771
return Some(Err(Error::NonMinimalPush));
754772
}
755-
let ret = Some(Ok(Instruction::PushBytes(&self.data.as_slice()[..n])));
756-
self.data.nth(n.max(1) - 1);
757-
ret
773+
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes).ok_or(Error::EarlyEndOfScript))
758774
}
759775
}
760776

@@ -773,22 +789,21 @@ impl<'a> Iterator for Instructions<'a> {
773789
// casting is safe because we don't support 16-bit architectures
774790
let n = n as usize;
775791

776-
if self.data.len() < n {
777-
let data_len = self.data.len();
778-
self.data.nth(data_len); // Kill iterator so that it does not return an infinite stream of errors
779-
return Some(Err(Error::EarlyEndOfScript));
780-
}
781-
if self.enforce_minimal {
782-
// index acceess is safe because we checked the lenght above
783-
if n == 1 && (self.data.as_slice()[0] == 0x81 || (self.data.as_slice()[0] > 0 && self.data.as_slice()[0] <= 16)) {
784-
let data_len = self.data.len();
785-
self.data.nth(data_len); // Kill iterator so that it does not return an infinite stream of errors
786-
return Some(Err(Error::NonMinimalPush));
792+
let op_byte = self.data.as_slice().first();
793+
match (self.enforce_minimal, op_byte, n) {
794+
(true, Some(&op_byte), 1) if op_byte == 0x81 || (op_byte > 0 && op_byte <= 16) => {
795+
self.kill();
796+
Some(Err(Error::NonMinimalPush))
797+
},
798+
(_, None, 0) => {
799+
// the iterator is already empty, may as well use this information to avoid
800+
// whole take_slice_or_kill function
801+
Some(Ok(Instruction::PushBytes(&[])))
802+
},
803+
_ => {
804+
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes).ok_or(Error::EarlyEndOfScript))
787805
}
788806
}
789-
let ret = Some(Ok(Instruction::PushBytes(&self.data.as_slice()[..n])));
790-
self.data.nth(n.max(1) - 1);
791-
ret
792807
}
793808
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => {
794809
self.next_push_data_len(1, 76)

0 commit comments

Comments
 (0)