Skip to main content

bitcoin_primitives/script/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bitcoin scripts.
4
5mod borrowed;
6mod owned;
7mod tag;
8
9use core::cmp::Ordering;
10use core::fmt;
11#[cfg(feature = "serde")]
12use core::marker::PhantomData;
13
14#[cfg(feature = "hex")]
15use hex_unstable::DisplayHex;
16use internals::script::{self, PushDataLenLen};
17
18use crate::prelude::rc::Rc;
19#[cfg(target_has_atomic = "ptr")]
20use crate::prelude::sync::Arc;
21use crate::prelude::{Borrow, BorrowMut, Box, Cow, ToOwned, Vec};
22
23#[rustfmt::skip]                // Keep public re-exports separate.
24#[doc(inline)]
25pub use self::{
26    borrowed::{Script, ScriptEncoder},
27    owned::{ScriptBuf, ScriptBufDecoder, ScriptBufDecoderError},
28    tag::{Tag, RedeemScriptTag, ScriptPubKeyTag, ScriptSigTag, TapScriptTag, WitnessScriptTag},
29};
30#[doc(inline)]
31pub use crate::hash_types::{
32    RedeemScriptSizeError, ScriptHash, WScriptHash, WitnessScriptSizeError,
33};
34
35/// A P2SH redeem script.
36pub type RedeemScriptBuf = ScriptBuf<RedeemScriptTag>;
37
38/// A reference to a P2SH redeem script.
39pub type RedeemScript = Script<RedeemScriptTag>;
40
41/// A reference to a `scriptPubKey` (locking script).
42pub type ScriptPubKey = Script<ScriptPubKeyTag>;
43
44/// A reference to a script signature (scriptSig).
45pub type ScriptSig = Script<ScriptSigTag>;
46
47/// A `scriptPubKey` (locking script).
48pub type ScriptPubKeyBuf = ScriptBuf<ScriptPubKeyTag>;
49
50/// A `scriptPubKey` decoder.
51pub type ScriptPubKeyBufDecoder = ScriptBufDecoder<ScriptPubKeyTag>;
52
53/// A script signature (scriptSig).
54pub type ScriptSigBuf = ScriptBuf<ScriptSigTag>;
55
56/// A `scriptSig` decoder.
57pub type ScriptSigBufDecoder = ScriptBufDecoder<ScriptSigTag>;
58
59/// A Segwit v1 Taproot script.
60pub type TapScriptBuf = ScriptBuf<TapScriptTag>;
61
62/// A reference to a Segwit v1 Taproot script.
63pub type TapScript = Script<TapScriptTag>;
64
65/// A Segwit v0 witness script.
66pub type WitnessScriptBuf = ScriptBuf<WitnessScriptTag>;
67
68/// A reference to a Segwit v0 witness script.
69pub type WitnessScript = Script<WitnessScriptTag>;
70
71/// The maximum allowed redeem script size for a P2SH output.
72pub const MAX_REDEEM_SCRIPT_SIZE: usize = 520;
73/// The maximum allowed redeem script size of the witness script.
74pub const MAX_WITNESS_SCRIPT_SIZE: usize = 10_000;
75
76/// Either a redeem script or a Segwit version 0 scriptpubkey.
77///
78/// In the case of P2SH-wrapped Segwit version outputs, we take a Segwit scriptPubKey
79/// and put it in a redeem script slot. The Bitcoin script interpreter has special
80/// logic to handle this case, which is reflected in our API in several methods
81/// relating to P2SH and signature hashing. These methods take either a normal
82/// P2SH redeem script, or a Segwit version 0 scriptpubkey.
83///
84/// Segwit version 1 (Taproot) and higher do **not** support P2SH-wrapping, and such
85/// scriptPubKeys should not be used with this trait.
86pub trait ScriptHashableTag: sealed::Sealed {}
87
88impl ScriptHashableTag for RedeemScriptTag {}
89impl ScriptHashableTag for ScriptPubKeyTag {}
90
91mod sealed {
92    pub trait Sealed {}
93    impl Sealed for super::RedeemScriptTag {}
94    impl Sealed for super::ScriptPubKeyTag {}
95}
96
97impl<T: ScriptHashableTag> TryFrom<ScriptBuf<T>> for ScriptHash {
98    type Error = RedeemScriptSizeError;
99
100    #[inline]
101    fn try_from(redeem_script: ScriptBuf<T>) -> Result<Self, Self::Error> {
102        Self::from_script(&redeem_script)
103    }
104}
105
106impl<T: ScriptHashableTag> TryFrom<&ScriptBuf<T>> for ScriptHash {
107    type Error = RedeemScriptSizeError;
108
109    #[inline]
110    fn try_from(redeem_script: &ScriptBuf<T>) -> Result<Self, Self::Error> {
111        Self::from_script(redeem_script)
112    }
113}
114
115impl<T: ScriptHashableTag> TryFrom<&Script<T>> for ScriptHash {
116    type Error = RedeemScriptSizeError;
117
118    #[inline]
119    fn try_from(redeem_script: &Script<T>) -> Result<Self, Self::Error> {
120        Self::from_script(redeem_script)
121    }
122}
123
124impl TryFrom<WitnessScriptBuf> for WScriptHash {
125    type Error = WitnessScriptSizeError;
126
127    #[inline]
128    fn try_from(witness_script: WitnessScriptBuf) -> Result<Self, Self::Error> {
129        Self::from_script(&witness_script)
130    }
131}
132
133impl TryFrom<&WitnessScriptBuf> for WScriptHash {
134    type Error = WitnessScriptSizeError;
135
136    #[inline]
137    fn try_from(witness_script: &WitnessScriptBuf) -> Result<Self, Self::Error> {
138        Self::from_script(witness_script)
139    }
140}
141
142impl TryFrom<&WitnessScript> for WScriptHash {
143    type Error = WitnessScriptSizeError;
144
145    #[inline]
146    fn try_from(witness_script: &WitnessScript) -> Result<Self, Self::Error> {
147        Self::from_script(witness_script)
148    }
149}
150
151// We keep all the `Script` and `ScriptBuf` impls together since it's easier to see side-by-side.
152
153impl<T> From<ScriptBuf<T>> for Box<Script<T>> {
154    #[inline]
155    fn from(v: ScriptBuf<T>) -> Self { v.into_boxed_script() }
156}
157
158impl<T> From<ScriptBuf<T>> for Cow<'_, Script<T>> {
159    #[inline]
160    fn from(value: ScriptBuf<T>) -> Self { Cow::Owned(value) }
161}
162
163impl<'a, T> From<Cow<'a, Script<T>>> for ScriptBuf<T> {
164    #[inline]
165    fn from(value: Cow<'a, Script<T>>) -> Self {
166        match value {
167            Cow::Owned(owned) => owned,
168            Cow::Borrowed(borrowed) => borrowed.into(),
169        }
170    }
171}
172
173impl<'a, T> From<Cow<'a, Script<T>>> for Box<Script<T>> {
174    #[inline]
175    fn from(value: Cow<'a, Script<T>>) -> Self {
176        match value {
177            Cow::Owned(owned) => owned.into(),
178            Cow::Borrowed(borrowed) => borrowed.into(),
179        }
180    }
181}
182
183impl<'a, T> From<&'a Script<T>> for Box<Script<T>> {
184    #[inline]
185    fn from(value: &'a Script<T>) -> Self { value.to_owned().into() }
186}
187
188impl<'a, T> From<&'a Script<T>> for ScriptBuf<T> {
189    #[inline]
190    fn from(value: &'a Script<T>) -> Self { value.to_owned() }
191}
192
193impl<'a, T> From<&'a Script<T>> for Cow<'a, Script<T>> {
194    #[inline]
195    fn from(value: &'a Script<T>) -> Self { Cow::Borrowed(value) }
196}
197
198/// Note: This will fail to compile on old Rust for targets that don't support atomics
199#[cfg(target_has_atomic = "ptr")]
200impl<'a, T> From<&'a Script<T>> for Arc<Script<T>> {
201    #[inline]
202    fn from(value: &'a Script<T>) -> Self { Script::from_arc_bytes(Arc::from(value.as_bytes())) }
203}
204
205impl<'a, T> From<&'a Script<T>> for Rc<Script<T>> {
206    #[inline]
207    fn from(value: &'a Script<T>) -> Self { Script::from_rc_bytes(Rc::from(value.as_bytes())) }
208}
209
210impl<T> From<Vec<u8>> for ScriptBuf<T> {
211    #[inline]
212    fn from(v: Vec<u8>) -> Self { Self::from_bytes(v) }
213}
214
215impl<T> From<ScriptBuf<T>> for Vec<u8> {
216    #[inline]
217    fn from(v: ScriptBuf<T>) -> Self { v.into_bytes() }
218}
219
220impl<T> AsRef<Self> for Script<T> {
221    #[inline]
222    fn as_ref(&self) -> &Self { self }
223}
224
225impl<T> AsRef<Script<T>> for ScriptBuf<T> {
226    #[inline]
227    fn as_ref(&self) -> &Script<T> { self }
228}
229
230impl<T> AsRef<[u8]> for Script<T> {
231    #[inline]
232    fn as_ref(&self) -> &[u8] { self.as_bytes() }
233}
234
235impl<T> AsRef<[u8]> for ScriptBuf<T> {
236    #[inline]
237    fn as_ref(&self) -> &[u8] { self.as_bytes() }
238}
239
240impl<T> AsMut<Self> for Script<T> {
241    #[inline]
242    fn as_mut(&mut self) -> &mut Self { self }
243}
244
245impl<T> AsMut<Script<T>> for ScriptBuf<T> {
246    #[inline]
247    fn as_mut(&mut self) -> &mut Script<T> { self }
248}
249
250impl<T> AsMut<[u8]> for Script<T> {
251    #[inline]
252    fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
253}
254
255impl<T> AsMut<[u8]> for ScriptBuf<T> {
256    #[inline]
257    fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
258}
259
260impl<T> fmt::Debug for Script<T> {
261    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262        f.write_str("Script(")?;
263        fmt::Display::fmt(self, f)?;
264        f.write_str(")")
265    }
266}
267
268impl<T> fmt::Debug for ScriptBuf<T> {
269    #[inline]
270    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_script(), f) }
271}
272
273impl<T> fmt::Display for Script<T> {
274    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275        // This has to be a macro because it needs to break the loop
276        macro_rules! read_push_data_len {
277            ($iter:expr, $size:path, $formatter:expr) => {
278                match script::read_push_data_len($iter, $size) {
279                    Ok(n) => n,
280                    Err(_) => {
281                        $formatter.write_str("<unexpected end>")?;
282                        break;
283                    }
284                }
285            };
286        }
287
288        let mut iter = self.as_bytes().iter();
289        // Was at least one opcode emitted?
290        let mut at_least_one = false;
291        // `iter` needs to be borrowed in `read_push_data_len`, so we have to use `while let` instead
292        // of `for`.
293        while let Some(byte) = iter.next().copied() {
294            use crate::opcodes::{OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4};
295
296            let data_len = if byte <= 75 {
297                usize::from(byte)
298            } else {
299                match byte {
300                    OP_PUSHDATA1 => {
301                        // side effects: may write and break from the loop
302                        read_push_data_len!(&mut iter, PushDataLenLen::One, f)
303                    }
304                    OP_PUSHDATA2 => {
305                        // side effects: may write and break from the loop
306                        read_push_data_len!(&mut iter, PushDataLenLen::Two, f)
307                    }
308                    OP_PUSHDATA4 => {
309                        // side effects: may write and break from the loop
310                        read_push_data_len!(&mut iter, PushDataLenLen::Four, f)
311                    }
312                    _ => 0,
313                }
314            };
315
316            if at_least_one {
317                f.write_str(" ")?;
318            } else {
319                at_least_one = true;
320            }
321            // Write the opcode
322            crate::opcodes::fmt_opcode(byte, f)?;
323            // Write any pushdata
324            if data_len > 0 {
325                f.write_str(" ")?;
326                if data_len <= iter.len() {
327                    for ch in iter.by_ref().take(data_len) {
328                        write!(f, "{:02x}", ch)?;
329                    }
330                } else {
331                    f.write_str("<push past end>")?;
332                    break;
333                }
334            }
335        }
336        Ok(())
337    }
338}
339
340impl<T> fmt::Display for ScriptBuf<T> {
341    #[inline]
342    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_script(), f) }
343}
344
345#[cfg(feature = "hex")]
346impl<T> fmt::LowerHex for Script<T> {
347    #[inline]
348    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
349        fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f)
350    }
351}
352
353#[cfg(feature = "hex")]
354impl<T> fmt::LowerHex for ScriptBuf<T> {
355    #[inline]
356    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_script(), f) }
357}
358
359#[cfg(feature = "hex")]
360impl<T> fmt::UpperHex for Script<T> {
361    #[inline]
362    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363        fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f)
364    }
365}
366
367#[cfg(feature = "hex")]
368impl<T> fmt::UpperHex for ScriptBuf<T> {
369    #[inline]
370    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(self.as_script(), f) }
371}
372
373impl<T> Borrow<Script<T>> for ScriptBuf<T> {
374    #[inline]
375    fn borrow(&self) -> &Script<T> { self }
376}
377
378impl<T> BorrowMut<Script<T>> for ScriptBuf<T> {
379    #[inline]
380    fn borrow_mut(&mut self) -> &mut Script<T> { self }
381}
382
383impl<T: PartialEq> PartialEq<ScriptBuf<T>> for Script<T> {
384    #[inline]
385    fn eq(&self, other: &ScriptBuf<T>) -> bool { self.eq(other.as_script()) }
386}
387
388impl<T: PartialEq> PartialEq<Script<T>> for ScriptBuf<T> {
389    #[inline]
390    fn eq(&self, other: &Script<T>) -> bool { self.as_script().eq(other) }
391}
392
393impl<T: PartialOrd> PartialOrd<Script<T>> for ScriptBuf<T> {
394    #[inline]
395    fn partial_cmp(&self, other: &Script<T>) -> Option<Ordering> {
396        self.as_script().partial_cmp(other)
397    }
398}
399
400impl<T: PartialOrd> PartialOrd<ScriptBuf<T>> for Script<T> {
401    #[inline]
402    fn partial_cmp(&self, other: &ScriptBuf<T>) -> Option<Ordering> {
403        self.partial_cmp(other.as_script())
404    }
405}
406
407#[cfg(feature = "serde")]
408impl<T> serde::Serialize for Script<T> {
409    /// User-facing serialization for `Script`.
410    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
411    where
412        S: serde::Serializer,
413    {
414        if serializer.is_human_readable() {
415            serializer.collect_str(&format_args!("{:x}", self))
416        } else {
417            serializer.serialize_bytes(self.as_bytes())
418        }
419    }
420}
421
422/// Can only deserialize borrowed bytes.
423#[cfg(feature = "serde")]
424impl<'de, T> serde::Deserialize<'de> for &'de Script<T> {
425    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
426    where
427        D: serde::Deserializer<'de>,
428    {
429        struct Visitor<T>(PhantomData<T>);
430        impl<'de, T: 'de> serde::de::Visitor<'de> for Visitor<T> {
431            type Value = &'de Script<T>;
432
433            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
434                formatter.write_str("borrowed bytes")
435            }
436
437            fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
438            where
439                E: serde::de::Error,
440            {
441                Ok(Script::from_bytes(v))
442            }
443        }
444
445        if deserializer.is_human_readable() {
446            use crate::serde::de::Error;
447
448            return Err(D::Error::custom(
449                "deserialization of `&Script` from human-readable formats is not possible",
450            ));
451        }
452
453        deserializer.deserialize_bytes(Visitor(PhantomData))
454    }
455}
456
457#[cfg(feature = "serde")]
458impl<T> serde::Serialize for ScriptBuf<T> {
459    /// User-facing serialization for `Script`.
460    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
461    where
462        S: serde::Serializer,
463    {
464        (**self).serialize(serializer)
465    }
466}
467
468#[cfg(feature = "serde")]
469impl<'de, T> serde::Deserialize<'de> for ScriptBuf<T> {
470    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
471    where
472        D: serde::Deserializer<'de>,
473    {
474        use core::fmt::Formatter;
475
476        if deserializer.is_human_readable() {
477            struct Visitor<T>(PhantomData<T>);
478            impl<T> serde::de::Visitor<'_> for Visitor<T> {
479                type Value = ScriptBuf<T>;
480
481                fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
482                    formatter.write_str("a script hex")
483                }
484
485                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
486                where
487                    E: serde::de::Error,
488                {
489                    let v = hex::decode_to_vec(v).map_err(E::custom)?;
490                    Ok(ScriptBuf::from(v))
491                }
492            }
493            deserializer.deserialize_str(Visitor(PhantomData))
494        } else {
495            struct BytesVisitor<T>(PhantomData<T>);
496
497            impl<T> serde::de::Visitor<'_> for BytesVisitor<T> {
498                type Value = ScriptBuf<T>;
499
500                fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
501                    formatter.write_str("a script Vec<u8>")
502                }
503
504                fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
505                where
506                    E: serde::de::Error,
507                {
508                    Ok(ScriptBuf::from(v.to_vec()))
509                }
510
511                fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
512                where
513                    E: serde::de::Error,
514                {
515                    Ok(ScriptBuf::from(v))
516                }
517            }
518            deserializer.deserialize_byte_buf(BytesVisitor(PhantomData))
519        }
520    }
521}
522
523#[cfg(test)]
524mod tests {
525    #[cfg(feature = "alloc")]
526    use alloc::{format, vec};
527
528    use hashes::{hash160, sha256};
529
530    use super::*;
531
532    // All tests should compile and pass no matter which script type you put here.
533    type Script = ScriptSig;
534    type ScriptBuf = ScriptSigBuf;
535
536    #[test]
537    fn scriptbuf_from_vec_u8() {
538        let vec = vec![0x51, 0x52, 0x53];
539        let script_buf: ScriptBuf = vec.clone().into();
540        let result: Vec<u8> = script_buf.into();
541        assert_eq!(result, vec);
542    }
543
544    #[test]
545    fn scriptbuf_as_ref() {
546        let script_buf = ScriptBuf::from(vec![0x51, 0x52, 0x53]);
547        let script_ref: &[u8] = script_buf.as_ref();
548        assert_eq!(script_ref, &[0x51, 0x52, 0x53]);
549
550        let script_ref: &Script = script_buf.as_ref();
551        assert_eq!(script_ref.as_bytes(), &[0x51, 0x52, 0x53]);
552    }
553
554    #[test]
555    fn scriptbuf_as_mut() {
556        let mut script_buf = ScriptBuf::from(vec![0x51, 0x52, 0x53]);
557
558        let script_mut: &mut [u8] = script_buf.as_mut();
559        script_mut[0] = 0x50;
560        assert_eq!(script_mut, [0x50, 0x52, 0x53]);
561
562        let script_mut: &mut Script = script_buf.as_mut();
563        script_mut.as_mut_bytes()[1] = 0x51;
564        assert_eq!(script_buf.as_bytes(), &[0x50, 0x51, 0x53]);
565    }
566
567    #[test]
568    fn scriptbuf_borrow_mut() {
569        let mut script_buf = ScriptBuf::from(vec![0x51, 0x52, 0x53]);
570        let script_mut: &mut Script = script_buf.borrow_mut();
571        script_mut.as_mut_bytes()[0] = 0x50;
572
573        assert_eq!(script_buf.as_bytes(), &[0x50, 0x52, 0x53]);
574    }
575
576    #[test]
577    #[allow(clippy::useless_asref)]
578    fn script_as_ref() {
579        let script = Script::from_bytes(&[0x51, 0x52, 0x53]);
580        let script_ref: &[u8] = script.as_ref();
581        assert_eq!(script_ref, &[0x51, 0x52, 0x53]);
582
583        let script_ref: &Script = script.as_ref();
584        assert_eq!(script_ref.as_bytes(), &[0x51, 0x52, 0x53]);
585    }
586
587    #[test]
588    #[allow(clippy::useless_asref)]
589    fn script_as_mut() {
590        let bytes = &mut [0x51, 0x52, 0x53];
591        let script = Script::from_bytes_mut(bytes);
592
593        let script_mut: &mut [u8] = script.as_mut();
594        script_mut[0] = 0x50;
595        assert_eq!(script_mut, [0x50, 0x52, 0x53]);
596
597        let script_mut: &mut Script = script.as_mut();
598        script_mut.as_mut_bytes()[1] = 0x51;
599        assert_eq!(script.as_bytes(), &[0x50, 0x51, 0x53]);
600    }
601
602    #[test]
603    fn partial_ord() {
604        let script_small = Script::from_bytes(&[0x51, 0x52, 0x53]);
605        let script_big = Script::from_bytes(&[0x54, 0x55, 0x56]);
606        let script_buf_small = ScriptBuf::from(vec![0x51, 0x52, 0x53]);
607        let script_buf_big = ScriptBuf::from(vec![0x54, 0x55, 0x56]);
608
609        assert!(script_small == &script_buf_small);
610        assert!(script_buf_small == *script_small);
611        assert!(script_small != &script_buf_big);
612        assert!(script_buf_small != *script_big);
613
614        assert!(script_small < &script_buf_big);
615        assert!(script_buf_small < *script_big);
616        assert!(script_big > &script_buf_small);
617        assert!(script_buf_big > *script_small);
618    }
619
620    #[test]
621    fn script_hash_from_script() {
622        let script = RedeemScript::from_bytes(&[0x51; 520]);
623        assert!(ScriptHash::from_script(script).is_ok());
624
625        let script = RedeemScript::from_bytes(&[0x51; 521]);
626        assert!(ScriptHash::from_script(script).is_err());
627    }
628
629    #[test]
630    fn script_hash_from_script_unchecked() {
631        let script = WitnessScript::from_bytes(&[0x51; 521]);
632
633        let got = ScriptHash::from_script_unchecked(script);
634        let want =
635            ScriptHash::from_byte_array(hash160::Hash::hash(script.as_bytes()).to_byte_array());
636
637        assert_eq!(got, want);
638    }
639
640    #[test]
641    fn wscript_hash_from_script() {
642        let script = WitnessScript::from_bytes(&[0x51; 10_000]);
643        assert!(WScriptHash::from_script(script).is_ok());
644
645        let script = WitnessScript::from_bytes(&[0x51; 10_001]);
646        assert!(WScriptHash::from_script(script).is_err());
647    }
648
649    #[test]
650    fn wscript_hash_from_script_unchecked() {
651        let script = WitnessScript::from_bytes(&[0x51; 10_001]);
652
653        let got = WScriptHash::from_script_unchecked(script);
654        let want =
655            WScriptHash::from_byte_array(sha256::Hash::hash(script.as_bytes()).to_byte_array());
656
657        assert_eq!(got, want);
658    }
659
660    #[test]
661    fn try_from_scriptpubkeybuf_for_scripthash() {
662        let script = ScriptPubKeyBuf::from(vec![0x51; 520]);
663        assert!(ScriptHash::try_from(script).is_ok());
664
665        let script = ScriptPubKeyBuf::from(vec![0x51; 521]);
666        assert!(ScriptHash::try_from(script).is_err());
667    }
668
669    #[test]
670    fn try_from_scriptpubkeybuf_ref_for_scripthash() {
671        let script = ScriptPubKeyBuf::from(vec![0x51; 520]);
672        assert!(ScriptHash::try_from(&script).is_ok());
673
674        let script = ScriptPubKeyBuf::from(vec![0x51; 521]);
675        assert!(ScriptHash::try_from(&script).is_err());
676    }
677
678    #[test]
679    fn try_from_script_for_scripthash() {
680        let script = RedeemScript::from_bytes(&[0x51; 520]);
681        assert!(ScriptHash::try_from(script).is_ok());
682
683        let script = RedeemScript::from_bytes(&[0x51; 521]);
684        assert!(ScriptHash::try_from(script).is_err());
685    }
686
687    #[test]
688    fn try_from_scriptbuf_for_wscript_hash() {
689        let script = WitnessScriptBuf::from(vec![0x51; 10_000]);
690        assert!(WScriptHash::try_from(script).is_ok());
691
692        let script = WitnessScriptBuf::from(vec![0x51; 10_001]);
693        assert!(WScriptHash::try_from(script).is_err());
694    }
695
696    #[test]
697    fn try_from_scriptbuf_ref_for_wscript_hash() {
698        let script = WitnessScriptBuf::from(vec![0x51; 10_000]);
699        assert!(WScriptHash::try_from(&script).is_ok());
700
701        let script = WitnessScriptBuf::from(vec![0x51; 10_001]);
702        assert!(WScriptHash::try_from(&script).is_err());
703    }
704
705    #[test]
706    fn try_from_script_for_wscript_hash() {
707        let script = WitnessScript::from_bytes(&[0x51; 10_000]);
708        assert!(WScriptHash::try_from(script).is_ok());
709
710        let script = WitnessScript::from_bytes(&[0x51; 10_001]);
711        assert!(WScriptHash::try_from(script).is_err());
712    }
713
714    #[test]
715    fn script_display() {
716        let script = Script::from_bytes(&[0x00, 0xa1, 0xb2]);
717        assert_eq!(format!("{}", script), "OP_0 OP_LESSTHANOREQUAL OP_CSV");
718
719        #[cfg(feature = "hex")]
720        {
721            assert_eq!(format!("{:x}", script), "00a1b2");
722            assert_eq!(format!("{:X}", script), "00A1B2");
723        }
724        assert!(!format!("{:?}", script).is_empty());
725    }
726
727    #[test]
728    fn script_display_pushdata() {
729        // OP_PUSHDATA1
730        let script = Script::from_bytes(&[0x4c, 0x02, 0xab, 0xcd]);
731        assert_eq!(format!("{}", script), "OP_PUSHDATA1 abcd");
732
733        // OP_PUSHDATA2
734        let script = Script::from_bytes(&[0x4d, 0x02, 0x00, 0x12, 0x34]);
735        assert_eq!(format!("{}", script), "OP_PUSHDATA2 1234");
736
737        // OP_PUSHDATA4
738        let script = Script::from_bytes(&[0x4e, 0x02, 0x00, 0x00, 0x00, 0x56, 0x78]);
739        assert_eq!(format!("{}", script), "OP_PUSHDATA4 5678");
740    }
741
742    #[test]
743    fn scriptbuf_display() {
744        let script_buf = ScriptBuf::from(vec![0x00, 0xa1, 0xb2]);
745        assert_eq!(format!("{}", script_buf), "OP_0 OP_LESSTHANOREQUAL OP_CSV");
746
747        #[cfg(feature = "hex")]
748        {
749            assert_eq!(format!("{:x}", script_buf), "00a1b2");
750            assert_eq!(format!("{:X}", script_buf), "00A1B2");
751        }
752        assert!(!format!("{:?}", script_buf).is_empty());
753    }
754
755    #[test]
756    fn cow_script_to_scriptbuf() {
757        let script = Script::from_bytes(&[0x51, 0x52, 0x53]);
758        let cow_borrowed: Cow<Script> = Cow::Borrowed(script);
759        let script_buf: ScriptBuf = cow_borrowed.into();
760        assert_eq!(script_buf.as_bytes(), &[0x51, 0x52, 0x53]);
761    }
762
763    #[test]
764    fn cow_scriptbuf_to_script() {
765        let cow_owned: Cow<Script> = Cow::Owned(ScriptBuf::from(vec![0x51, 0x52, 0x53]));
766        let script: &Script = cow_owned.borrow();
767        assert_eq!(script.as_bytes(), &[0x51, 0x52, 0x53]);
768    }
769
770    #[test]
771    fn cow_scriptbuf_to_box_script() {
772        let script_buf = ScriptBuf::from(vec![0x51, 0x52, 0x53]);
773        let cow_owned: Cow<Script> = Cow::Owned(script_buf.clone());
774        let boxed_script: Box<Script> = cow_owned.into();
775        let script_buf2 = boxed_script.into_script_buf();
776        assert_eq!(script_buf2, script_buf);
777    }
778
779    #[test]
780    fn cow_owned_to_scriptbuf() {
781        let script_buf = ScriptBuf::from(vec![0x51, 0x52, 0x53]);
782        let cow_owned: Cow<Script> = Cow::Owned(script_buf.clone());
783        let script_buf_2: ScriptBuf = cow_owned.into();
784        assert_eq!(script_buf_2, script_buf);
785    }
786
787    #[test]
788    fn cow_script_to_box_script() {
789        let script = Script::from_bytes(&[0x51, 0x52, 0x53]);
790        let cow_borrowed: Cow<Script> = Cow::Borrowed(script);
791        let boxed_script: Box<Script> = cow_borrowed.into();
792        assert_eq!(boxed_script.as_bytes(), &[0x51, 0x52, 0x53]);
793
794        let cow_owned: Cow<Script> = Cow::from(script.to_owned());
795        assert_eq!(cow_owned.as_ref().as_bytes(), &[0x51, 0x52, 0x53]);
796
797        let cow_from_script: Cow<Script> = Cow::from(script);
798        assert_eq!(cow_from_script.as_ref().as_bytes(), &[0x51, 0x52, 0x53]);
799    }
800
801    #[test]
802    fn redeem_script_size_error() {
803        let script = RedeemScriptBuf::from(vec![0x51; 521]);
804        let result = ScriptHash::try_from(script);
805
806        let err = result.unwrap_err();
807        assert_eq!(err.invalid_size(), 521);
808
809        let err_msg = format!("{}", err);
810        assert!(err_msg.contains("521"));
811    }
812
813    #[test]
814    fn witness_script_size_error() {
815        let script = WitnessScriptBuf::from(vec![0x51; 10_001]);
816        let result = WScriptHash::try_from(script);
817
818        let err = result.unwrap_err();
819        assert_eq!(err.invalid_size(), 10_001);
820
821        let err_msg = format!("{}", err);
822        assert!(err_msg.contains("10001"));
823    }
824
825    #[test]
826    #[cfg(target_has_atomic = "ptr")]
827    fn script_to_arc() {
828        let script = Script::from_bytes(&[0x51, 0x52, 0x53]);
829        let arc_script: Arc<Script> = Arc::from(script);
830
831        assert_eq!(arc_script.as_bytes(), script.as_bytes());
832        assert_eq!(Arc::strong_count(&arc_script), 1);
833    }
834
835    #[test]
836    fn script_to_rc() {
837        let script = Script::from_bytes(&[0x51, 0x52, 0x53]);
838        let rc_script: Rc<Script> = Rc::from(script);
839
840        assert_eq!(rc_script.as_bytes(), script.as_bytes());
841        assert_eq!(Rc::strong_count(&rc_script), 1);
842    }
843
844    #[test]
845    fn pushdata_end_conditions() {
846        let push_past_end_script = Script::from_bytes(&[0x4c, 0x02]);
847        let formatted_script = format!("{}", push_past_end_script);
848        assert!(formatted_script.contains("<push past end>"));
849
850        let unexpected_end_script = Script::from_bytes(&[0x4c]);
851        let formatted_script = format!("{}", unexpected_end_script);
852        assert!(formatted_script.contains("<unexpected end>"));
853    }
854
855    #[test]
856    fn legacy_opcode() {
857        let script = Script::from_bytes(&[0x03, 0xaa, 0xbb, 0xcc]);
858        assert_eq!(format!("{}", script), "OP_PUSHBYTES_3 aabbcc");
859    }
860
861    #[test]
862    #[cfg(feature = "alloc")]
863    #[cfg(feature = "hex")]
864    fn script_to_hex() {
865        let script = Script::from_bytes(&[0xa1, 0xb2, 0xc3]);
866        let hex = alloc::format!("{script:x}");
867        assert_eq!(hex, "a1b2c3");
868    }
869
870    #[test]
871    #[cfg(feature = "alloc")]
872    #[cfg(feature = "hex")]
873    fn scriptbuf_to_hex() {
874        let script = ScriptBuf::from_bytes(vec![0xa1, 0xb2, 0xc3]);
875        let hex = alloc::format!("{script:x}");
876        assert_eq!(hex, "a1b2c3");
877    }
878}