Skip to content

Commit 40172cc

Browse files
author
ChallengeDev210
committed
Merge rust-bitcoin/rust-bitcoin#673: Use iterator in blockdata::script::Instructions
2c28d3b Fix handling of empty slice in Instructions (Martin Habovštiak) e6ff754 Fix doc of take_slice_or_kill (Martin Habovštiak) 0ec6d96 Cleanup after `Instructions` refactoring (Martin Habovstiak) bc76325 Move repeated code to functions in script (Martin Habovstiak) 1f55edf Use iterator in `blockdata::script::Instructions` (Martin Habovstiak) Pull request description: This refactors `blockdata::script::Instructions` to use `::core::slice::Iter<'a, u8>` instead of `&'a [u8]` to better express the intention and to avoid some slicing mistakes. Similarly to a previous change this uses a macro to deduplicate the common logic and the new `read_uint_iter` internal function to automatically advance the iterator. Addresses: rust-bitcoin/rust-bitcoin#662 (review) ACKs for top commit: tcharding: ACK 2c28d3b sanket1729: ACK 2c28d3b. I don't want to hold ACKs on minor things as they can be in a fixup later. Tree-SHA512: 9dc770b9f7958efbd0df2cc2d3546e23deca5df2f94ea2c42b089df628f4b99f08032ca4aa8822caf6643a8892903e1bda41228b78c8519b90bcaa1255d9acc6
2 parents 8966ec7 + 156c7c5 commit 40172cc

File tree

1 file changed

+70
-87
lines changed

1 file changed

+70
-87
lines changed

src/blockdata/script.rs

Lines changed: 70 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -601,15 +601,15 @@ impl Script {
601601
/// To force minimal pushes, use [`Self::instructions_minimal`].
602602
pub fn instructions(&self) -> Instructions {
603603
Instructions {
604-
data: &self.0[..],
604+
data: self.0.iter(),
605605
enforce_minimal: false,
606606
}
607607
}
608608

609609
/// Iterates over the script in the form of `Instruction`s while enforcing minimal pushes.
610610
pub fn instructions_minimal(&self) -> Instructions {
611611
Instructions {
612-
data: &self.0[..],
612+
data: self.0.iter(),
613613
enforce_minimal: true,
614614
}
615615
}
@@ -748,114 +748,97 @@ pub enum Instruction<'a> {
748748

749749
/// Iterator over a script returning parsed opcodes.
750750
pub struct Instructions<'a> {
751-
data: &'a [u8],
751+
data: ::core::slice::Iter<'a, u8>,
752752
enforce_minimal: bool,
753753
}
754754

755+
impl<'a> Instructions<'a> {
756+
/// Set the iterator to end so that it won't iterate any longer
757+
fn kill(&mut self) {
758+
let len = self.data.len();
759+
self.data.nth(len.max(1) - 1);
760+
}
761+
762+
/// takes `len` bytes long slice from iterator and returns it advancing iterator
763+
/// if the iterator is not long enough [`Error::EarlyEndOfScript`] is returned and the iterator is killed
764+
/// to avoid returning an infinite stream of errors.
765+
fn take_slice_or_kill(&mut self, len: usize) -> Result<&'a [u8], Error> {
766+
if self.data.len() >= len {
767+
let slice = &self.data.as_slice()[..len];
768+
if len > 0 {
769+
self.data.nth(len - 1);
770+
}
771+
772+
Ok(slice)
773+
} else {
774+
self.kill();
775+
Err(Error::EarlyEndOfScript)
776+
}
777+
}
778+
779+
fn next_push_data_len(&mut self, len: usize, min_push_len: usize) -> Option<Result<Instruction<'a>, Error>> {
780+
let n = match read_uint_iter(&mut self.data, len) {
781+
Ok(n) => n,
782+
// We do exhaustive matching to not forget to handle new variants if we extend
783+
// `UintError` type.
784+
// Overflow actually means early end of script (script is definitely shorter
785+
// than `usize::max_value()`)
786+
Err(UintError::EarlyEndOfScript) | Err(UintError::NumericOverflow) => {
787+
self.kill();
788+
return Some(Err(Error::EarlyEndOfScript));
789+
},
790+
};
791+
if self.enforce_minimal && n < min_push_len {
792+
self.kill();
793+
return Some(Err(Error::NonMinimalPush));
794+
}
795+
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes))
796+
}
797+
}
798+
755799
impl<'a> Iterator for Instructions<'a> {
756800
type Item = Result<Instruction<'a>, Error>;
757801

758802
fn next(&mut self) -> Option<Result<Instruction<'a>, Error>> {
759-
if self.data.is_empty() {
760-
return None;
761-
}
803+
let &byte = self.data.next()?;
762804

763805
// classify parameter does not really matter here since we are only using
764806
// it for pushes and nums
765-
match opcodes::All::from(self.data[0]).classify(opcodes::ClassifyContext::Legacy) {
807+
match opcodes::All::from(byte).classify(opcodes::ClassifyContext::Legacy) {
766808
opcodes::Class::PushBytes(n) => {
809+
// make sure safety argument holds across refactorings
810+
let n: u32 = n;
811+
// casting is safe because we don't support 16-bit architectures
767812
let n = n as usize;
768-
if self.data.len() < n + 1 {
769-
self.data = &[]; // Kill iterator so that it does not return an infinite stream of errors
770-
return Some(Err(Error::EarlyEndOfScript));
771-
}
772-
if self.enforce_minimal {
773-
if n == 1 && (self.data[1] == 0x81 || (self.data[1] > 0 && self.data[1] <= 16)) {
774-
self.data = &[];
775-
return Some(Err(Error::NonMinimalPush));
813+
814+
let op_byte = self.data.as_slice().first();
815+
match (self.enforce_minimal, op_byte, n) {
816+
(true, Some(&op_byte), 1) if op_byte == 0x81 || (op_byte > 0 && op_byte <= 16) => {
817+
self.kill();
818+
Some(Err(Error::NonMinimalPush))
819+
},
820+
(_, None, 0) => {
821+
// the iterator is already empty, may as well use this information to avoid
822+
// whole take_slice_or_kill function
823+
Some(Ok(Instruction::PushBytes(&[])))
824+
},
825+
_ => {
826+
Some(self.take_slice_or_kill(n).map(Instruction::PushBytes))
776827
}
777828
}
778-
let ret = Some(Ok(Instruction::PushBytes(&self.data[1..n+1])));
779-
self.data = &self.data[n + 1..];
780-
ret
781829
}
782830
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => {
783-
if self.data.len() < 2 {
784-
self.data = &[];
785-
return Some(Err(Error::EarlyEndOfScript));
786-
}
787-
let n = match read_uint(&self.data[1..], 1) {
788-
Ok(n) => n,
789-
Err(e) => {
790-
self.data = &[];
791-
return Some(Err(e));
792-
}
793-
};
794-
if self.data.len() < n + 2 {
795-
self.data = &[];
796-
return Some(Err(Error::EarlyEndOfScript));
797-
}
798-
if self.enforce_minimal && n < 76 {
799-
self.data = &[];
800-
return Some(Err(Error::NonMinimalPush));
801-
}
802-
let ret = Some(Ok(Instruction::PushBytes(&self.data[2..n+2])));
803-
self.data = &self.data[n + 2..];
804-
ret
831+
self.next_push_data_len(1, 76)
805832
}
806833
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => {
807-
if self.data.len() < 3 {
808-
self.data = &[];
809-
return Some(Err(Error::EarlyEndOfScript));
810-
}
811-
let n = match read_uint(&self.data[1..], 2) {
812-
Ok(n) => n,
813-
Err(e) => {
814-
self.data = &[];
815-
return Some(Err(e));
816-
}
817-
};
818-
if self.enforce_minimal && n < 0x100 {
819-
self.data = &[];
820-
return Some(Err(Error::NonMinimalPush));
821-
}
822-
if self.data.len() < n + 3 {
823-
self.data = &[];
824-
return Some(Err(Error::EarlyEndOfScript));
825-
}
826-
let ret = Some(Ok(Instruction::PushBytes(&self.data[3..n + 3])));
827-
self.data = &self.data[n + 3..];
828-
ret
834+
self.next_push_data_len(2, 0x100)
829835
}
830836
opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => {
831-
if self.data.len() < 5 {
832-
self.data = &[];
833-
return Some(Err(Error::EarlyEndOfScript));
834-
}
835-
let n = match read_uint(&self.data[1..], 4) {
836-
Ok(n) => n,
837-
Err(e) => {
838-
self.data = &[];
839-
return Some(Err(e));
840-
}
841-
};
842-
if self.enforce_minimal && n < 0x10000 {
843-
self.data = &[];
844-
return Some(Err(Error::NonMinimalPush));
845-
}
846-
if self.data.len() < n + 5 {
847-
self.data = &[];
848-
return Some(Err(Error::EarlyEndOfScript));
849-
}
850-
let ret = Some(Ok(Instruction::PushBytes(&self.data[5..n + 5])));
851-
self.data = &self.data[n + 5..];
852-
ret
837+
self.next_push_data_len(4, 0x10000)
853838
}
854839
// Everything else we can push right through
855840
_ => {
856-
let ret = Some(Ok(Instruction::Op(opcodes::All::from(self.data[0]))));
857-
self.data = &self.data[1..];
858-
ret
841+
Some(Ok(Instruction::Op(opcodes::All::from(byte))))
859842
}
860843
}
861844
}

0 commit comments

Comments
 (0)