Skip to main content

bitcoin_primitives/script/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bitcoin scripts.
4
5mod borrowed;
6mod builder;
7mod owned;
8mod push_bytes;
9mod tag;
10#[cfg(test)]
11mod tests;
12
13pub mod error;
14
15use core::cmp::Ordering;
16use core::fmt;
17#[cfg(feature = "serde")]
18use core::marker::PhantomData;
19
20#[cfg(feature = "hex")]
21use hex::DisplayHex;
22use internals::script::{self, PushDataLenLen};
23
24use crate::prelude::rc::Rc;
25#[cfg(target_has_atomic = "ptr")]
26use crate::prelude::sync::Arc;
27use crate::prelude::{Borrow, BorrowMut, Box, Cow, ToOwned, Vec};
28use crate::witness_version::WitnessVersion;
29
30#[rustfmt::skip]                // Keep public re-exports separate.
31#[doc(inline)]
32pub use self::{
33    borrowed::{Script, ScriptEncoder},
34    builder::Builder,
35    owned::{ScriptBuf, ScriptBufDecoder},
36    push_bytes::{PushBytes, PushBytesBuf, PushBytesErrorReport},
37    tag::{Tag, RedeemScriptTag, ScriptPubKeyTag, ScriptSigTag, SignetBlockScriptTag, TapScriptTag, WitnessScriptTag},
38};
39#[doc(no_inline)]
40pub use self::error::{
41    PushBytesError, RedeemScriptSizeError, ScriptBufDecoderError, WitnessScriptSizeError,
42};
43#[doc(inline)]
44pub use crate::hash_types::{ScriptHash, WScriptHash};
45
46/// A P2SH redeem script.
47pub type RedeemScriptBuf = ScriptBuf<RedeemScriptTag>;
48
49/// A reference to a P2SH redeem script.
50pub type RedeemScript = Script<RedeemScriptTag>;
51
52/// A reference to a `scriptPubKey` (locking script).
53pub type ScriptPubKey = Script<ScriptPubKeyTag>;
54
55/// A reference to a script signature (scriptSig).
56pub type ScriptSig = Script<ScriptSigTag>;
57
58/// A `scriptPubKey` (locking script).
59pub type ScriptPubKeyBuf = ScriptBuf<ScriptPubKeyTag>;
60
61/// A `scriptPubKey` decoder.
62pub type ScriptPubKeyBufDecoder = ScriptBufDecoder<ScriptPubKeyTag>;
63
64/// A script signature (scriptSig).
65pub type ScriptSigBuf = ScriptBuf<ScriptSigTag>;
66
67/// A `scriptSig` decoder.
68pub type ScriptSigBufDecoder = ScriptBufDecoder<ScriptSigTag>;
69
70/// A signet block/challenge script.
71pub type SignetBlockScriptBuf = ScriptBuf<SignetBlockScriptTag>;
72
73/// A reference to a signet block/challenge script.
74pub type SignetBlockScript = Script<SignetBlockScriptTag>;
75
76/// A Segwit v1 Taproot script.
77pub type TapScriptBuf = ScriptBuf<TapScriptTag>;
78
79/// A reference to a Segwit v1 Taproot script.
80pub type TapScript = Script<TapScriptTag>;
81
82/// A Segwit v0 witness script.
83pub type WitnessScriptBuf = ScriptBuf<WitnessScriptTag>;
84
85/// A reference to a Segwit v0 witness script.
86pub type WitnessScript = Script<WitnessScriptTag>;
87
88/// The maximum allowed redeem script size for a P2SH output.
89pub const MAX_REDEEM_SCRIPT_SIZE: usize = 520;
90/// The maximum allowed redeem script size of the witness script.
91pub const MAX_WITNESS_SCRIPT_SIZE: usize = 10_000;
92
93/// The P2A program which is given by 0x4e73.
94pub(crate) const P2A_PROGRAM: [u8; 2] = [78, 115];
95
96/// Generates P2WSH-type of scriptPubkey with a given [`WitnessVersion`] and the program bytes.
97///
98/// Does not do any checks on version or program length.
99///
100/// Convenience method used by `new_p2a`, `new_p2wpkh`, `new_p2wsh`, `new_p2tr`, and `new_p2tr_tweaked`.
101// This function is duplicated in addresses and bitcoin. If you make any changes, please update all three.
102pub(crate) fn new_witness_program_unchecked<T: AsRef<PushBytes>, Tg>(
103    version: WitnessVersion,
104    program: T,
105) -> ScriptBuf<Tg> {
106    let program = program.as_ref();
107    debug_assert!(program.len() >= 2 && program.len() <= 40);
108    // In SegWit v0, the program must be either 20 bytes (P2WPKH) or 32 bytes (P2WSH) long.
109    debug_assert!(version != WitnessVersion::V0 || program.len() == 20 || program.len() == 32);
110    Builder::new().push_opcode(version.into()).push_slice(program).into_script()
111}
112
113/// Either a redeem script or a Segwit version 0 scriptpubkey.
114///
115/// In the case of P2SH-wrapped Segwit version outputs, we take a Segwit scriptPubKey
116/// and put it in a redeem script slot. The Bitcoin script interpreter has special
117/// logic to handle this case, which is reflected in our API in several methods
118/// relating to P2SH and signature hashing. These methods take either a normal
119/// P2SH redeem script, or a Segwit version 0 scriptpubkey.
120///
121/// Segwit version 1 (Taproot) and higher do **not** support P2SH-wrapping, and such
122/// scriptPubKeys should not be used with this trait.
123pub trait ScriptHashableTag: sealed::Sealed {}
124
125impl ScriptHashableTag for RedeemScriptTag {}
126impl ScriptHashableTag for ScriptPubKeyTag {}
127
128mod sealed {
129    pub trait Sealed {}
130    impl Sealed for super::RedeemScriptTag {}
131    impl Sealed for super::ScriptPubKeyTag {}
132}
133
134impl<T: ScriptHashableTag> TryFrom<ScriptBuf<T>> for ScriptHash {
135    type Error = RedeemScriptSizeError;
136
137    #[inline]
138    fn try_from(redeem_script: ScriptBuf<T>) -> Result<Self, Self::Error> {
139        Self::from_script(&redeem_script)
140    }
141}
142
143impl<T: ScriptHashableTag> TryFrom<&ScriptBuf<T>> for ScriptHash {
144    type Error = RedeemScriptSizeError;
145
146    #[inline]
147    fn try_from(redeem_script: &ScriptBuf<T>) -> Result<Self, Self::Error> {
148        Self::from_script(redeem_script)
149    }
150}
151
152impl<T: ScriptHashableTag> TryFrom<&Script<T>> for ScriptHash {
153    type Error = RedeemScriptSizeError;
154
155    #[inline]
156    fn try_from(redeem_script: &Script<T>) -> Result<Self, Self::Error> {
157        Self::from_script(redeem_script)
158    }
159}
160
161impl TryFrom<WitnessScriptBuf> for WScriptHash {
162    type Error = WitnessScriptSizeError;
163
164    #[inline]
165    fn try_from(witness_script: WitnessScriptBuf) -> Result<Self, Self::Error> {
166        Self::from_script(&witness_script)
167    }
168}
169
170impl TryFrom<&WitnessScriptBuf> for WScriptHash {
171    type Error = WitnessScriptSizeError;
172
173    #[inline]
174    fn try_from(witness_script: &WitnessScriptBuf) -> Result<Self, Self::Error> {
175        Self::from_script(witness_script)
176    }
177}
178
179impl TryFrom<&WitnessScript> for WScriptHash {
180    type Error = WitnessScriptSizeError;
181
182    #[inline]
183    fn try_from(witness_script: &WitnessScript) -> Result<Self, Self::Error> {
184        Self::from_script(witness_script)
185    }
186}
187
188impl From<WitnessScriptBuf> for SignetBlockScriptBuf {
189    #[inline]
190    fn from(buf: WitnessScriptBuf) -> Self { Self::from_bytes(buf.into_bytes()) }
191}
192
193// We keep all the `Script` and `ScriptBuf` impls together since it's easier to see side-by-side.
194
195impl<T> From<ScriptBuf<T>> for Box<Script<T>> {
196    #[inline]
197    fn from(v: ScriptBuf<T>) -> Self { v.into_boxed_script() }
198}
199
200impl<T> From<ScriptBuf<T>> for Cow<'_, Script<T>> {
201    #[inline]
202    fn from(value: ScriptBuf<T>) -> Self { Cow::Owned(value) }
203}
204
205impl<'a, T> From<Cow<'a, Script<T>>> for ScriptBuf<T> {
206    #[inline]
207    fn from(value: Cow<'a, Script<T>>) -> Self {
208        match value {
209            Cow::Owned(owned) => owned,
210            Cow::Borrowed(borrowed) => borrowed.into(),
211        }
212    }
213}
214
215impl<'a, T> From<Cow<'a, Script<T>>> for Box<Script<T>> {
216    #[inline]
217    fn from(value: Cow<'a, Script<T>>) -> Self {
218        match value {
219            Cow::Owned(owned) => owned.into(),
220            Cow::Borrowed(borrowed) => borrowed.into(),
221        }
222    }
223}
224
225impl<'a, T> From<&'a Script<T>> for Box<Script<T>> {
226    #[inline]
227    fn from(value: &'a Script<T>) -> Self { value.to_owned().into() }
228}
229
230impl<'a, T> From<&'a Script<T>> for ScriptBuf<T> {
231    #[inline]
232    fn from(value: &'a Script<T>) -> Self { value.to_owned() }
233}
234
235impl<'a, T> From<&'a Script<T>> for Cow<'a, Script<T>> {
236    #[inline]
237    fn from(value: &'a Script<T>) -> Self { Cow::Borrowed(value) }
238}
239
240/// Note: This will fail to compile on old Rust for targets that don't support atomics
241#[cfg(target_has_atomic = "ptr")]
242impl<'a, T> From<&'a Script<T>> for Arc<Script<T>> {
243    #[inline]
244    fn from(value: &'a Script<T>) -> Self { Script::from_arc_bytes(Arc::from(value.as_bytes())) }
245}
246
247impl<'a, T> From<&'a Script<T>> for Rc<Script<T>> {
248    #[inline]
249    fn from(value: &'a Script<T>) -> Self { Script::from_rc_bytes(Rc::from(value.as_bytes())) }
250}
251
252impl<T> From<Vec<u8>> for ScriptBuf<T> {
253    #[inline]
254    fn from(v: Vec<u8>) -> Self { Self::from_bytes(v) }
255}
256
257impl<T> From<ScriptBuf<T>> for Vec<u8> {
258    #[inline]
259    fn from(v: ScriptBuf<T>) -> Self { v.into_bytes() }
260}
261
262impl<T> AsRef<Self> for Script<T> {
263    #[inline]
264    fn as_ref(&self) -> &Self { self }
265}
266
267impl<T> AsRef<Script<T>> for ScriptBuf<T> {
268    #[inline]
269    fn as_ref(&self) -> &Script<T> { self }
270}
271
272impl<T> AsRef<[u8]> for Script<T> {
273    #[inline]
274    fn as_ref(&self) -> &[u8] { self.as_bytes() }
275}
276
277impl<T> AsRef<[u8]> for ScriptBuf<T> {
278    #[inline]
279    fn as_ref(&self) -> &[u8] { self.as_bytes() }
280}
281
282impl<T> AsMut<Self> for Script<T> {
283    #[inline]
284    fn as_mut(&mut self) -> &mut Self { self }
285}
286
287impl<T> AsMut<Script<T>> for ScriptBuf<T> {
288    #[inline]
289    fn as_mut(&mut self) -> &mut Script<T> { self }
290}
291
292impl<T> AsMut<[u8]> for Script<T> {
293    #[inline]
294    fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
295}
296
297impl<T> AsMut<[u8]> for ScriptBuf<T> {
298    #[inline]
299    fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
300}
301
302impl<T> fmt::Debug for Script<T> {
303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304        f.write_str("Script(")?;
305        fmt::Display::fmt(self, f)?;
306        f.write_str(")")
307    }
308}
309
310impl<T> fmt::Debug for ScriptBuf<T> {
311    #[inline]
312    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_script(), f) }
313}
314
315impl<T> fmt::Display for Script<T> {
316    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317        // This has to be a macro because it needs to break the loop
318        macro_rules! read_push_data_len {
319            ($iter:expr, $size:path, $formatter:expr) => {
320                match script::read_push_data_len($iter, $size) {
321                    Ok(n) => n,
322                    Err(_) => {
323                        $formatter.write_str("<unexpected end>")?;
324                        break;
325                    }
326                }
327            };
328        }
329
330        let mut iter = self.as_bytes().iter();
331        // Was at least one opcode emitted?
332        let mut at_least_one = false;
333        // `iter` needs to be borrowed in `read_push_data_len`, so we have to use `while let` instead
334        // of `for`.
335        while let Some(byte) = iter.next().copied() {
336            use crate::opcodes::{OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4};
337
338            let data_len = if byte <= 75 {
339                usize::from(byte)
340            } else {
341                match byte {
342                    OP_PUSHDATA1 => {
343                        // side effects: may write and break from the loop
344                        read_push_data_len!(&mut iter, PushDataLenLen::One, f)
345                    }
346                    OP_PUSHDATA2 => {
347                        // side effects: may write and break from the loop
348                        read_push_data_len!(&mut iter, PushDataLenLen::Two, f)
349                    }
350                    OP_PUSHDATA4 => {
351                        // side effects: may write and break from the loop
352                        read_push_data_len!(&mut iter, PushDataLenLen::Four, f)
353                    }
354                    _ => 0,
355                }
356            };
357
358            if at_least_one {
359                f.write_str(" ")?;
360            } else {
361                at_least_one = true;
362            }
363            // Write the opcode
364            crate::opcodes::fmt_opcode(byte, f)?;
365            // Write any pushdata
366            if data_len > 0 {
367                f.write_str(" ")?;
368                if data_len <= iter.len() {
369                    for ch in iter.by_ref().take(data_len) {
370                        write!(f, "{:02x}", ch)?;
371                    }
372                } else {
373                    f.write_str("<push past end>")?;
374                    break;
375                }
376            }
377        }
378        Ok(())
379    }
380}
381
382impl<T> fmt::Display for ScriptBuf<T> {
383    #[inline]
384    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_script(), f) }
385}
386
387#[cfg(feature = "hex")]
388impl<T> fmt::LowerHex for Script<T> {
389    #[inline]
390    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391        fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f)
392    }
393}
394
395#[cfg(feature = "hex")]
396impl<T> fmt::LowerHex for ScriptBuf<T> {
397    #[inline]
398    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_script(), f) }
399}
400
401#[cfg(feature = "hex")]
402impl<T> fmt::UpperHex for Script<T> {
403    #[inline]
404    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405        fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f)
406    }
407}
408
409#[cfg(feature = "hex")]
410impl<T> fmt::UpperHex for ScriptBuf<T> {
411    #[inline]
412    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(self.as_script(), f) }
413}
414
415impl<T> Borrow<Script<T>> for ScriptBuf<T> {
416    #[inline]
417    fn borrow(&self) -> &Script<T> { self }
418}
419
420impl<T> BorrowMut<Script<T>> for ScriptBuf<T> {
421    #[inline]
422    fn borrow_mut(&mut self) -> &mut Script<T> { self }
423}
424
425impl<T: PartialEq> PartialEq<ScriptBuf<T>> for Script<T> {
426    #[inline]
427    fn eq(&self, other: &ScriptBuf<T>) -> bool { self.eq(other.as_script()) }
428}
429
430impl<T: PartialEq> PartialEq<Script<T>> for ScriptBuf<T> {
431    #[inline]
432    fn eq(&self, other: &Script<T>) -> bool { self.as_script().eq(other) }
433}
434
435impl<T: PartialOrd> PartialOrd<Script<T>> for ScriptBuf<T> {
436    #[inline]
437    fn partial_cmp(&self, other: &Script<T>) -> Option<Ordering> {
438        self.as_script().partial_cmp(other)
439    }
440}
441
442impl<T: PartialOrd> PartialOrd<ScriptBuf<T>> for Script<T> {
443    #[inline]
444    fn partial_cmp(&self, other: &ScriptBuf<T>) -> Option<Ordering> {
445        self.partial_cmp(other.as_script())
446    }
447}
448
449#[cfg(feature = "serde")]
450impl<T> serde::Serialize for Script<T> {
451    /// User-facing serialization for `Script`.
452    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
453    where
454        S: serde::Serializer,
455    {
456        if serializer.is_human_readable() {
457            serializer.collect_str(&format_args!("{:x}", self))
458        } else {
459            serializer.serialize_bytes(self.as_bytes())
460        }
461    }
462}
463
464/// Can only deserialize borrowed bytes.
465#[cfg(feature = "serde")]
466impl<'de, T> serde::Deserialize<'de> for &'de Script<T> {
467    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
468    where
469        D: serde::Deserializer<'de>,
470    {
471        struct Visitor<T>(PhantomData<T>);
472        impl<'de, T: 'de> serde::de::Visitor<'de> for Visitor<T> {
473            type Value = &'de Script<T>;
474
475            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
476                formatter.write_str("borrowed bytes")
477            }
478
479            fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
480            where
481                E: serde::de::Error,
482            {
483                Ok(Script::from_bytes(v))
484            }
485        }
486
487        if deserializer.is_human_readable() {
488            use crate::serde::de::Error;
489
490            return Err(D::Error::custom(
491                "deserialization of `&Script` from human-readable formats is not possible",
492            ));
493        }
494
495        deserializer.deserialize_bytes(Visitor(PhantomData))
496    }
497}
498
499#[cfg(feature = "serde")]
500impl<T> serde::Serialize for ScriptBuf<T> {
501    /// User-facing serialization for `Script`.
502    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
503    where
504        S: serde::Serializer,
505    {
506        (**self).serialize(serializer)
507    }
508}
509
510#[cfg(feature = "serde")]
511impl<'de, T> serde::Deserialize<'de> for ScriptBuf<T> {
512    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
513    where
514        D: serde::Deserializer<'de>,
515    {
516        use core::fmt::Formatter;
517
518        if deserializer.is_human_readable() {
519            struct Visitor<T>(PhantomData<T>);
520            impl<T> serde::de::Visitor<'_> for Visitor<T> {
521                type Value = ScriptBuf<T>;
522
523                fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
524                    formatter.write_str("a script hex")
525                }
526
527                fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
528                where
529                    E: serde::de::Error,
530                {
531                    let v = hex::decode_to_vec(v).map_err(E::custom)?;
532                    Ok(ScriptBuf::from(v))
533                }
534            }
535            deserializer.deserialize_str(Visitor(PhantomData))
536        } else {
537            struct BytesVisitor<T>(PhantomData<T>);
538
539            impl<T> serde::de::Visitor<'_> for BytesVisitor<T> {
540                type Value = ScriptBuf<T>;
541
542                fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
543                    formatter.write_str("a script Vec<u8>")
544                }
545
546                fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
547                where
548                    E: serde::de::Error,
549                {
550                    Ok(ScriptBuf::from(v.to_vec()))
551                }
552
553                fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
554                where
555                    E: serde::de::Error,
556                {
557                    Ok(ScriptBuf::from(v))
558                }
559            }
560            deserializer.deserialize_byte_buf(BytesVisitor(PhantomData))
561        }
562    }
563}