1use alloc::{
4 string::{String, ToString},
5 vec::Vec,
6};
7
8use thiserror::Error;
9
10use crate::{
11 interpreter,
12 op::{self},
13 opcode::{self, push_value::LargeValue::*, Operation::*, PossiblyBad, PushValue},
14 signature, Opcode,
15};
16
17pub(crate) mod iter;
18
19#[allow(missing_docs)]
21#[derive(Clone, Debug, PartialEq, Eq, Error)]
22pub enum Error {
23 #[error(
25 "Script size{} exceeded maxmimum ({} bytes)",
26 .0.map_or("", |size| " ({size} bytes)"),
27 Code::MAX_SIZE
28 )]
29 ScriptSize(Option<usize>),
30
31 #[error("during parsing: {0}")]
32 Opcode(opcode::Error),
33
34 #[error("non-push opcode encountered in script sig when push-only required")]
35 SigPushOnly,
36
37 #[error("during interpretation: {1}")]
39 Interpreter(Option<opcode::PossiblyBad>, interpreter::Error),
40
41 #[error("external error: {0}")]
46 ExternalError(&'static str),
47
48 #[error("{} closed before the end of the script", match .0 { 1 => "1 conditional opcode wasn’t", n => "{n} conditional opcodes weren’t"})]
49 UnclosedConditional(usize),
50
51 #[error("the script is P2SH, but there was no redeem script left on the stack")]
52 MissingRedeemScript,
53
54 #[error("clean stack requirement not met")]
55 CleanStack,
56}
57
58impl Error {
59 pub const AMBIGUOUS_COUNT_DISABLED: Self =
67 Self::ExternalError("ambiguous OpCount or DisabledOpcode error");
68
69 pub const AMBIGUOUS_UNKNOWN_NUM_HIGHS: Self =
74 Self::ExternalError("ambiguous Unknown, or ScriptNum, or HighS error");
75
76 pub fn normalize(&self) -> Self {
78 match self {
79 Self::ScriptSize(Some(_)) => Self::ScriptSize(None),
80 Self::Opcode(oerr) => match oerr {
81 opcode::Error::Read { .. } => {
82 Self::Interpreter(None, interpreter::Error::BadOpcode)
83 }
84 opcode::Error::Disabled(_) => Self::AMBIGUOUS_COUNT_DISABLED,
85 opcode::Error::PushSize(Some(_)) => Self::from(opcode::Error::PushSize(None)),
86 _ => self.clone(),
87 },
88 Self::Interpreter(
89 Some(opcode::PossiblyBad::Good(op::IF | op::NOTIF)),
90 interpreter::Error::InvalidStackOperation(_),
91 ) => Self::Interpreter(None, interpreter::Error::UnbalancedConditional),
92 Self::Interpreter(
93 Some(opcode::PossiblyBad::Good(op::FROMALTSTACK)),
94 interpreter::Error::InvalidStackOperation(_),
95 ) => Self::Interpreter(
96 Some(opcode::PossiblyBad::Good(op::FROMALTSTACK)),
97 interpreter::Error::InvalidStackOperation(None),
98 ),
99 Self::Interpreter(_, ierr) => match ierr {
100 interpreter::Error::OpCount => Self::AMBIGUOUS_COUNT_DISABLED,
101 interpreter::Error::SignatureEncoding(signature::Error::SigHighS) => {
102 Self::AMBIGUOUS_UNKNOWN_NUM_HIGHS
103 }
104 interpreter::Error::Num(_) => Self::AMBIGUOUS_UNKNOWN_NUM_HIGHS,
105 interpreter::Error::Verify => self.clone(),
106 _ => Self::Interpreter(None, ierr.normalize()),
107 },
108 Self::UnclosedConditional(_) => {
109 Self::Interpreter(None, interpreter::Error::UnbalancedConditional)
110 }
111 Self::MissingRedeemScript => {
112 Self::Interpreter(None, interpreter::Error::InvalidStackOperation(None))
113 }
114 _ => self.clone(),
115 }
116 }
117}
118
119impl From<opcode::Error> for Error {
120 fn from(value: opcode::Error) -> Self {
121 Error::Opcode(value)
122 }
123}
124
125type AnnOpcode = Result<Opcode, Vec<Error>>;
126
127pub trait Evaluable {
129 fn byte_len(&self) -> usize;
131
132 fn to_bytes(&self) -> Vec<u8>;
134
135 fn eval(
137 &self,
138 flags: interpreter::Flags,
139 checker: &dyn interpreter::SignatureChecker,
140 stack: interpreter::Stack<Vec<u8>>,
141 ) -> Result<interpreter::Stack<Vec<u8>>, Error>;
142
143 fn is_pay_to_script_hash(&self) -> bool;
145
146 fn is_push_only(&self) -> bool;
148}
149
150pub trait Asm {
152 fn to_asm(&self, attempt_sighash_decode: bool) -> String;
160}
161
162#[derive(Clone, Debug, PartialEq, Eq)]
171pub struct Component<T>(pub Vec<T>);
172
173pub type Sig = Component<PushValue>;
175
176pub type PubKey = Component<Opcode>;
178
179pub type Redeem = Component<Opcode>;
181
182pub type FromChain = Component<opcode::PossiblyBad>;
195
196impl<T: Clone> Component<T> {
197 pub fn weaken<U: From<T>>(&self) -> Component<U> {
199 Component(self.0.iter().cloned().map(|op| U::from(op)).collect())
200 }
201}
202
203impl<T: opcode::Evaluable> Component<T> {
204 pub fn parse(raw_script: &Code) -> Result<Self, Error> {
210 raw_script
211 .parse()
212 .map(|mpb| mpb.map_err(Error::Opcode).and_then(T::restrict))
213 .collect::<Result<_, _>>()
214 .map(Component)
215 }
216}
217
218impl<T: Into<opcode::PossiblyBad> + Clone> Component<T> {
219 pub fn refine<U: opcode::Evaluable>(&self) -> Result<Component<U>, Error> {
222 self.0
223 .iter()
224 .cloned()
225 .map(|op| U::restrict(op.into()))
226 .collect::<Result<_, _>>()
227 .map(Component)
228 }
229}
230
231impl<T: Into<opcode::PossiblyBad> + opcode::Evaluable + Clone> Evaluable for Component<T> {
232 fn byte_len(&self) -> usize {
233 self.0.iter().map(T::byte_len).sum()
234 }
235
236 fn to_bytes(&self) -> Vec<u8> {
238 self.0.iter().flat_map(|elem| elem.to_bytes()).collect()
239 }
240
241 fn eval(
242 &self,
243 flags: interpreter::Flags,
244 checker: &dyn interpreter::SignatureChecker,
245 stack: interpreter::Stack<Vec<u8>>,
246 ) -> Result<interpreter::Stack<Vec<u8>>, Error> {
247 match self.byte_len() {
249 ..=Code::MAX_SIZE => iter::eval(
250 self.0.iter().cloned().map(Ok),
251 flags,
252 &Code(self.to_bytes()),
253 stack,
254 checker,
255 ),
256 n => Err(Error::ScriptSize(Some(n))),
257 }
258 }
259
260 fn is_pay_to_script_hash(&self) -> bool {
262 match &self
263 .0
264 .iter()
265 .map(|op| op.clone().into())
266 .collect::<Vec<_>>()[..]
267 {
268 [opcode::PossiblyBad::Good(Opcode::Operation(OP_HASH160)), opcode::PossiblyBad::Good(Opcode::PushValue(PushValue::LargeValue(
269 PushdataBytelength(v),
270 ))), opcode::PossiblyBad::Good(Opcode::Operation(OP_EQUAL))] => v.len() == 0x14,
271 _ => false,
272 }
273 }
274
275 fn is_push_only(&self) -> bool {
277 self.0.iter().all(|op| {
278 matches!(
279 op.extract_push_value(),
280 Ok(_)
281 | Err(
282 Error::Opcode(opcode::Error::PushSize(_))
285 | Error::Interpreter(
286 Some(opcode::PossiblyBad::Bad(opcode::Bad::OP_RESERVED)),
287 interpreter::Error::BadOpcode
288 )
289 )
290 )
291 })
292 }
293}
294
295impl<T: Asm> Asm for Component<T> {
296 fn to_asm(&self, attempt_sighash_decode: bool) -> String {
297 self.0
298 .iter()
299 .map(|op| op.to_asm(attempt_sighash_decode))
300 .collect::<Vec<_>>()
301 .join(" ")
302 }
303}
304
305#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
307pub struct Parser<'a>(&'a [u8]);
308
309impl Iterator for Parser<'_> {
310 type Item = Result<opcode::PossiblyBad, opcode::Error>;
311 fn next(&mut self) -> Option<Self::Item> {
312 if self.0.is_empty() {
313 None
314 } else {
315 let (res, rem) = opcode::PossiblyBad::parse(self.0);
316 self.0 = rem;
317 Some(res)
318 }
319 }
320}
321
322#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
325pub enum ComponentType {
326 Sig,
328 PubKey,
330 Redeem,
332}
333
334#[derive(Clone, Debug)]
336pub struct Code(pub Vec<u8>);
337
338impl Code {
339 pub(crate) const MAX_SIZE: usize = 10_000;
341
342 pub fn parse(&self) -> Parser<'_> {
344 Parser(&self.0)
345 }
346
347 pub fn annotate(&self, flags: &interpreter::Flags) -> Vec<AnnOpcode> {
349 self.parse()
350 .map(|mpb| {
351 mpb.map_err(|e| vec![Error::Opcode(e)]).and_then(|pb| {
352 pb.analyze(flags)
353 .map_err(|ierrs| {
354 ierrs
355 .into_iter()
356 .map(|ie| Error::Interpreter(Some(pb.clone()), ie))
357 .collect()
358 })
359 .cloned()
360 })
361 })
362 .collect::<Vec<_>>()
363 }
364
365 pub fn to_component(&self) -> Result<Component<opcode::PossiblyBad>, opcode::Error> {
367 self.parse().collect::<Result<_, _>>().map(Component)
368 }
369
370 pub fn serialize(script: &[Opcode]) -> Vec<u8> {
372 script.iter().flat_map(Vec::from).collect()
373 }
374
375 pub fn sig_op_count(&self, accurate: bool) -> u32 {
381 iter::sig_op_count(self.parse(), accurate)
382 }
383
384 pub fn is_unspendable(&self) -> bool {
392 self.parse().next() == Some(Ok(opcode::PossiblyBad::Good(op::RETURN)))
393 || self.0.len() > Self::MAX_SIZE
394 }
395}
396
397impl Evaluable for Code {
398 fn byte_len(&self) -> usize {
400 self.0.len()
401 }
402
403 fn to_bytes(&self) -> Vec<u8> {
405 self.0.clone()
406 }
407
408 fn eval(
409 &self,
410 flags: interpreter::Flags,
411 checker: &dyn interpreter::SignatureChecker,
412 stack: interpreter::Stack<Vec<u8>>,
413 ) -> Result<interpreter::Stack<Vec<u8>>, Error> {
414 match self.byte_len() {
415 ..=Code::MAX_SIZE => iter::eval(
416 self.parse().map(|res| res.map_err(Error::Opcode)),
417 flags,
418 self,
419 stack,
420 checker,
421 ),
422 n => Err(Error::ScriptSize(Some(n))),
423 }
424 }
425
426 fn is_pay_to_script_hash(&self) -> bool {
428 self.parse()
429 .collect::<Result<Vec<_>, _>>()
430 .is_ok_and(|ops| Component(ops).is_pay_to_script_hash())
431 }
432
433 fn is_push_only(&self) -> bool {
435 self.parse().all(|op| {
436 matches!(
437 op,
438 Err(opcode::Error::PushSize(_))
441 | Ok(opcode::PossiblyBad::Good(Opcode::PushValue(_))
442 | opcode::PossiblyBad::Bad(opcode::Bad::OP_RESERVED))
443 )
444 })
445 }
446}
447
448impl Asm for Code {
449 fn to_asm(&self, attempt_sighash_decode: bool) -> String {
450 let mut v = Vec::new();
451 let is_unspendable = self.is_unspendable();
452 for r in self.parse() {
453 match r {
454 Ok(PossiblyBad::Good(op)) => {
455 v.push(op.to_asm(attempt_sighash_decode && !is_unspendable))
456 }
457 Ok(PossiblyBad::Bad(_)) | Err(_) => {
458 v.push("[error]".to_string());
459 break;
460 }
461 }
462 }
463 v.join(" ")
464 }
465}
466
467impl core::fmt::Display for Code {
468 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
469 write!(f, "{}", self.to_asm(false))
470 }
471}
472
473pub struct Raw {
475 pub sig: Code,
477 pub pub_key: Code,
479}
480
481impl Raw {
482 pub fn from_raw_parts(sig: Vec<u8>, pub_key: Vec<u8>) -> Self {
484 Raw {
485 sig: Code(sig),
486 pub_key: Code(pub_key),
487 }
488 }
489
490 pub(crate) fn map<T>(&self, f: impl Fn(&Code) -> T) -> (T, T) {
492 (f(&self.sig), f(&self.pub_key))
493 }
494
495 pub fn annotate(&self, flags: &interpreter::Flags) -> (Vec<AnnOpcode>, Vec<AnnOpcode>) {
497 self.map(|c| c.annotate(flags))
498 }
499
500 pub fn eval(
502 &self,
503 flags: interpreter::Flags,
504 checker: &dyn interpreter::SignatureChecker,
505 ) -> Result<bool, (ComponentType, Error)> {
506 iter::eval_script(&self.sig, &self.pub_key, flags, checker)
507 }
508}
509
510impl Asm for Raw {
511 fn to_asm(&self, attempt_sighash_decode: bool) -> String {
512 self.sig.to_asm(attempt_sighash_decode) + " " + &self.pub_key.to_asm(false)
513 }
514}
515
516impl core::fmt::Display for Raw {
517 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
518 write!(f, "{}", self.to_asm(true))
519 }
520}