bitcoin_primitives/script/
borrowed.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::ops::{
4    Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
5};
6
7use super::ScriptBuf;
8use crate::prelude::{Box, ToOwned, Vec};
9
10/// Bitcoin script slice.
11///
12/// *[See also the `bitcoin::script` module](super).*
13///
14/// `Script` is a script slice, the most primitive script type. It's usually seen in its borrowed
15/// form `&Script`. It is always encoded as a series of bytes representing the opcodes and data
16/// pushes.
17///
18/// ## Validity
19///
20/// `Script` does not have any validity invariants - it's essentially just a marked slice of
21/// bytes. This is similar to [`Path`](std::path::Path) vs [`OsStr`](std::ffi::OsStr) where they
22/// are trivially cast-able to each-other and `Path` doesn't guarantee being a usable FS path but
23/// having a newtype still has value because of added methods, readability and basic type checking.
24///
25/// Although at least data pushes could be checked not to overflow the script, bad scripts are
26/// allowed to be in a transaction (outputs just become unspendable) and there even are such
27/// transactions in the chain. Thus we must allow such scripts to be placed in the transaction.
28///
29/// ## Slicing safety
30///
31/// Slicing is similar to how `str` works: some ranges may be incorrect and indexing by
32/// `usize` is not supported. However, as opposed to `std`, we have no way of checking
33/// correctness without causing linear complexity so there are **no panics on invalid
34/// ranges!** If you supply an invalid range, you'll get a garbled script.
35///
36/// The range is considered valid if it's at a boundary of instruction. Care must be taken
37/// especially with push operations because you could get a reference to arbitrary
38/// attacker-supplied bytes that look like a valid script.
39///
40/// It is recommended to use `.instructions()` method to get an iterator over script
41/// instructions and work with that instead.
42///
43/// ## Memory safety
44///
45/// The type is `#[repr(transparent)]` for internal purposes only!
46/// No consumer crate may rely on the representation of the struct!
47///
48/// ## References
49///
50///
51/// ### Bitcoin Core References
52///
53/// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410)
54///
55#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
56#[repr(transparent)]
57pub struct Script(pub(in crate::script) [u8]);
58
59impl ToOwned for Script {
60    type Owned = ScriptBuf;
61
62    fn to_owned(&self) -> Self::Owned { ScriptBuf(self.0.to_owned()) }
63}
64
65impl Script {
66    /// Constructs a new empty script.
67    #[inline]
68    pub fn new() -> &'static Script { Script::from_bytes(&[]) }
69
70    /// Treat byte slice as `Script`
71    #[inline]
72    pub fn from_bytes(bytes: &[u8]) -> &Script {
73        // SAFETY: copied from `std`
74        // The pointer was just created from a reference which is still alive.
75        // Casting slice pointer to a transparent struct wrapping that slice is sound (same
76        // layout).
77        unsafe { &*(bytes as *const [u8] as *const Script) }
78    }
79
80    /// Treat mutable byte slice as `Script`
81    #[inline]
82    pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Script {
83        // SAFETY: copied from `std`
84        // The pointer was just created from a reference which is still alive.
85        // Casting slice pointer to a transparent struct wrapping that slice is sound (same
86        // layout).
87        // Function signature prevents callers from accessing `bytes` while the returned reference
88        // is alive.
89        unsafe { &mut *(bytes as *mut [u8] as *mut Script) }
90    }
91
92    /// Returns the script data as a byte slice.
93    #[inline]
94    pub fn as_bytes(&self) -> &[u8] { &self.0 }
95
96    /// Returns the script data as a mutable byte slice.
97    #[inline]
98    pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.0 }
99
100    /// Returns a copy of the script data.
101    #[inline]
102    pub fn to_vec(&self) -> Vec<u8> { self.0.to_owned() }
103
104    /// Returns a copy of the script data.
105    #[inline]
106    #[deprecated(since = "TBD", note = "use to_vec instead")]
107    pub fn to_bytes(&self) -> Vec<u8> { self.to_vec() }
108
109    /// Returns the length in bytes of the script.
110    #[inline]
111    pub fn len(&self) -> usize { self.0.len() }
112
113    /// Returns whether the script is the empty script.
114    #[inline]
115    pub fn is_empty(&self) -> bool { self.0.is_empty() }
116
117    /// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
118    #[must_use = "`self` will be dropped if the result is not used"]
119    pub fn into_script_buf(self: Box<Self>) -> ScriptBuf {
120        let rw = Box::into_raw(self) as *mut [u8];
121        // SAFETY: copied from `std`
122        // The pointer was just created from a box without deallocating
123        // Casting a transparent struct wrapping a slice to the slice pointer is sound (same
124        // layout).
125        let inner = unsafe { Box::from_raw(rw) };
126        ScriptBuf(Vec::from(inner))
127    }
128}
129
130macro_rules! delegate_index {
131    ($($type:ty),* $(,)?) => {
132        $(
133            /// Script subslicing operation - read [slicing safety](#slicing-safety)!
134            impl Index<$type> for Script {
135                type Output = Self;
136
137                #[inline]
138                fn index(&self, index: $type) -> &Self::Output {
139                    Self::from_bytes(&self.0[index])
140                }
141            }
142        )*
143    }
144}
145
146delegate_index!(
147    Range<usize>,
148    RangeFrom<usize>,
149    RangeTo<usize>,
150    RangeFull,
151    RangeInclusive<usize>,
152    RangeToInclusive<usize>,
153    (Bound<usize>, Bound<usize>)
154);