bitcoin/blockdata/script/
instruction.rs1use crate::blockdata::opcodes::{self, Opcode};
4use crate::blockdata::script::{read_uint_iter, Error, PushBytes, Script, ScriptBuf, UintError};
5
6#[derive(Debug, PartialEq, Eq, Copy, Clone)]
8pub enum Instruction<'a> {
9 PushBytes(&'a PushBytes),
11 Op(Opcode),
13}
14
15impl<'a> Instruction<'a> {
16 pub fn opcode(&self) -> Option<Opcode> {
18 match self {
19 Instruction::Op(op) => Some(*op),
20 Instruction::PushBytes(_) => None,
21 }
22 }
23
24 pub fn push_bytes(&self) -> Option<&PushBytes> {
26 match self {
27 Instruction::Op(_) => None,
28 Instruction::PushBytes(bytes) => Some(bytes),
29 }
30 }
31
32 pub fn script_num(&self) -> Option<i64> {
37 match self {
38 Instruction::Op(op) => {
39 let v = op.to_u8();
40 match v {
41 0x51..=0x60 => Some(v as i64 - 0x50),
43 0x4f => Some(-1),
45 _ => None,
46 }
47 }
48 Instruction::PushBytes(bytes) => {
49 match super::read_scriptint_non_minimal(bytes.as_bytes()) {
50 Ok(v) => Some(v),
51 _ => None,
52 }
53 }
54 }
55 }
56
57 pub(super) fn script_serialized_len(&self) -> usize {
59 match self {
60 Instruction::Op(_) => 1,
61 Instruction::PushBytes(bytes) => ScriptBuf::reserved_len_for_slice(bytes.len()),
62 }
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct Instructions<'a> {
69 pub(crate) data: core::slice::Iter<'a, u8>,
70 pub(crate) enforce_minimal: bool,
71}
72
73impl<'a> Instructions<'a> {
74 pub fn as_script(&self) -> &'a Script { Script::from_bytes(self.data.as_slice()) }
78
79 pub(super) fn kill(&mut self) {
81 let len = self.data.len();
82 self.data.nth(len.max(1) - 1);
83 }
84
85 pub(super) fn take_slice_or_kill(&mut self, len: u32) -> Result<&'a PushBytes, Error> {
90 let len = len as usize;
91 if self.data.len() >= len {
92 let slice = &self.data.as_slice()[..len];
93 if len > 0 {
94 self.data.nth(len - 1);
95 }
96
97 Ok(slice.try_into().expect("len was created from u32, so can't happen"))
98 } else {
99 self.kill();
100 Err(Error::EarlyEndOfScript)
101 }
102 }
103
104 pub(super) fn next_push_data_len(
105 &mut self,
106 len: PushDataLenLen,
107 min_push_len: usize,
108 ) -> Option<Result<Instruction<'a>, Error>> {
109 let n = match read_uint_iter(&mut self.data, len as usize) {
110 Ok(n) => n,
111 Err(UintError::EarlyEndOfScript) | Err(UintError::NumericOverflow) => {
116 self.kill();
117 return Some(Err(Error::EarlyEndOfScript));
118 }
119 };
120 if self.enforce_minimal && n < min_push_len {
121 self.kill();
122 return Some(Err(Error::NonMinimalPush));
123 }
124 let result = n
125 .try_into()
126 .map_err(|_| Error::NumericOverflow)
127 .and_then(|n| self.take_slice_or_kill(n))
128 .map(Instruction::PushBytes);
129 Some(result)
130 }
131}
132
133pub(super) enum PushDataLenLen {
137 One = 1,
138 Two = 2,
139 Four = 4,
140}
141
142impl<'a> Iterator for Instructions<'a> {
143 type Item = Result<Instruction<'a>, Error>;
144
145 fn next(&mut self) -> Option<Result<Instruction<'a>, Error>> {
146 let &byte = self.data.next()?;
147
148 match Opcode::from(byte).classify(opcodes::ClassifyContext::Legacy) {
151 opcodes::Class::PushBytes(n) => {
152 let n: u32 = n;
154
155 let op_byte = self.data.as_slice().first();
156 match (self.enforce_minimal, op_byte, n) {
157 (true, Some(&op_byte), 1)
158 if op_byte == 0x81 || (op_byte > 0 && op_byte <= 16) =>
159 {
160 self.kill();
161 Some(Err(Error::NonMinimalPush))
162 }
163 (_, None, 0) => {
164 Some(Ok(Instruction::PushBytes(PushBytes::empty())))
167 }
168 _ => Some(self.take_slice_or_kill(n).map(Instruction::PushBytes)),
169 }
170 }
171 opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) =>
172 self.next_push_data_len(PushDataLenLen::One, 76),
173 opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) =>
174 self.next_push_data_len(PushDataLenLen::Two, 0x100),
175 opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) =>
176 self.next_push_data_len(PushDataLenLen::Four, 0x10000),
177 _ => Some(Ok(Instruction::Op(Opcode::from(byte)))),
179 }
180 }
181
182 #[inline]
183 fn size_hint(&self) -> (usize, Option<usize>) {
184 if self.data.len() == 0 {
185 (0, Some(0))
186 } else {
187 (1, Some(self.data.len()))
189 }
190 }
191}
192
193impl<'a> core::iter::FusedIterator for Instructions<'a> {}
194
195#[derive(Debug, Clone)]
201pub struct InstructionIndices<'a> {
202 instructions: Instructions<'a>,
203 pos: usize,
204}
205
206impl<'a> InstructionIndices<'a> {
207 #[inline]
211 pub fn as_script(&self) -> &'a Script { self.instructions.as_script() }
212
213 pub(super) fn from_instructions(instructions: Instructions<'a>) -> Self {
215 InstructionIndices { instructions, pos: 0 }
216 }
217
218 pub(super) fn remaining_bytes(&self) -> usize { self.instructions.as_script().len() }
219
220 pub(super) fn next_with<F: FnOnce(&mut Self) -> Option<Result<Instruction<'a>, Error>>>(
225 &mut self,
226 next_fn: F,
227 ) -> Option<<Self as Iterator>::Item> {
228 let prev_remaining = self.remaining_bytes();
229 let prev_pos = self.pos;
230 let instruction = next_fn(self)?;
231 let consumed = prev_remaining - self.remaining_bytes();
233 self.pos += consumed;
235 Some(instruction.map(move |instruction| (prev_pos, instruction)))
236 }
237}
238
239impl<'a> Iterator for InstructionIndices<'a> {
240 type Item = Result<(usize, Instruction<'a>), Error>;
242
243 fn next(&mut self) -> Option<Self::Item> { self.next_with(|this| this.instructions.next()) }
244
245 #[inline]
246 fn size_hint(&self) -> (usize, Option<usize>) { self.instructions.size_hint() }
247
248 fn nth(&mut self, n: usize) -> Option<Self::Item> {
250 self.next_with(|this| this.instructions.nth(n))
251 }
252}
253
254impl core::iter::FusedIterator for InstructionIndices<'_> {}