Skip to main content

bitcoin_primitives/script/
borrowed.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::marker::PhantomData;
4use core::ops::{
5    Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
6};
7
8#[cfg(feature = "arbitrary")]
9use arbitrary::{Arbitrary, Unstructured};
10use encoding::{BytesEncoder, CompactSizeEncoder, Encodable, Encoder2};
11
12use super::ScriptBuf;
13use crate::prelude::{Box, ToOwned, Vec};
14
15internals::transparent_newtype! {
16    /// Bitcoin script slice.
17    ///
18    /// *[See also the `bitcoin::script` module](super).*
19    ///
20    /// `Script` is a script slice, the most primitive script type. It's usually seen in its borrowed
21    /// form `&Script`. It is always encoded as a series of bytes representing the opcodes and data
22    /// pushes.
23    ///
24    /// # Validity
25    ///
26    /// `Script` does not have any validity invariants - it's essentially just a marked slice of
27    /// bytes. This is similar to [`Path`](std::path::Path) vs [`OsStr`](std::ffi::OsStr) where they
28    /// are trivially cast-able to each-other and `Path` doesn't guarantee being a usable FS path but
29    /// having a newtype still has value because of added methods, readability and basic type checking.
30    ///
31    /// Although at least data pushes could be checked not to overflow the script, bad scripts are
32    /// allowed to be in a transaction (outputs just become unspendable) and there even are such
33    /// transactions in the chain. Thus we must allow such scripts to be placed in the transaction.
34    ///
35    /// # Slicing safety
36    ///
37    /// Slicing is similar to how `str` works: some ranges may be incorrect and indexing by
38    /// `usize` is not supported. However, as opposed to `std`, we have no way of checking
39    /// correctness without causing linear complexity so there are **no panics on invalid
40    /// ranges!** If you supply an invalid range, you'll get a garbled script.
41    ///
42    /// The range is considered valid if it's at a boundary of instruction. Care must be taken
43    /// especially with push operations because you could get a reference to arbitrary
44    /// attacker-supplied bytes that look like a valid script.
45    ///
46    /// It is recommended to use `.instructions()` method to get an iterator over script
47    /// instructions and work with that instead.
48    ///
49    /// # Memory safety
50    ///
51    /// The type is `#[repr(transparent)]` for internal purposes only!
52    /// No consumer crate may rely on the representation of the struct!
53    ///
54    /// # Hexadecimal strings
55    ///
56    /// Scripts are consensus encoded with a length prefix and as a result of this in some places in
57    /// the ecosystem one will encounter hex strings that include the prefix while in other places
58    /// the prefix is excluded. To support parsing and formatting scripts as hex we provide a bunch
59    /// of different APIs and trait implementations. Please see [`examples/script.rs`] for a
60    /// thorough example of all the APIs.
61    ///
62    /// # Bitcoin Core References
63    ///
64    /// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410)
65    ///
66    #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
67    pub struct Script<T>(PhantomData<T>, [u8]);
68
69    impl<T> Script<T> {
70        /// Treat byte slice as `Script`
71        pub const fn from_bytes(bytes: &_) -> &Self;
72
73        /// Treat mutable byte slice as `Script`
74        pub fn from_bytes_mut(bytes: &mut _) -> &mut Self;
75
76        pub(crate) fn from_boxed_bytes(bytes: Box<_>) -> Box<Self>;
77        pub(crate) fn from_rc_bytes(bytes: Rc<_>) -> Rc<Self>;
78        pub(crate) fn from_arc_bytes(bytes: Arc<_>) -> Arc<Self>;
79    }
80}
81
82impl<T: 'static> Default for &Script<T> {
83    #[inline]
84    fn default() -> Self { Script::new() }
85}
86
87impl<T> ToOwned for Script<T> {
88    type Owned = ScriptBuf<T>;
89
90    #[inline]
91    fn to_owned(&self) -> Self::Owned { ScriptBuf::from_bytes(self.to_vec()) }
92}
93
94impl<T> Script<T> {
95    /// Constructs a new empty script.
96    #[inline]
97    pub const fn new() -> &'static Self { Self::from_bytes(&[]) }
98
99    /// Returns the script data as a byte slice.
100    ///
101    /// This is just the script bytes **not** consensus encoding (which includes a length prefix).
102    #[inline]
103    pub const fn as_bytes(&self) -> &[u8] { &self.1 }
104
105    /// Returns the script data as a mutable byte slice.
106    ///
107    /// This is just the script bytes **not** consensus encoding (which includes a length prefix).
108    #[inline]
109    pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.1 }
110
111    /// Returns a copy of the script data.
112    ///
113    /// This is just the script bytes **not** consensus encoding (which includes a length prefix).
114    #[inline]
115    pub fn to_vec(&self) -> Vec<u8> { self.as_bytes().to_owned() }
116
117    /// Returns a copy of the script data.
118    #[inline]
119    #[deprecated(since = "0.101.0", note = "use to_vec instead")]
120    pub fn to_bytes(&self) -> Vec<u8> { self.to_vec() }
121
122    /// Returns the length in bytes of the script.
123    #[inline]
124    pub const fn len(&self) -> usize { self.as_bytes().len() }
125
126    /// Returns whether the script is the empty script.
127    #[inline]
128    pub const fn is_empty(&self) -> bool { self.as_bytes().is_empty() }
129
130    /// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
131    #[must_use]
132    #[inline]
133    pub fn into_script_buf(self: Box<Self>) -> ScriptBuf<T> {
134        let rw = Box::into_raw(self) as *mut [u8];
135        // SAFETY: copied from `std`
136        // The pointer was just created from a box without deallocating
137        // Casting a transparent struct wrapping a slice to the slice pointer is sound (same
138        // layout).
139        let inner = unsafe { Box::from_raw(rw) };
140        ScriptBuf::from_bytes(Vec::from(inner))
141    }
142
143    /// Gets the hex representation of this script.
144    ///
145    /// # Returns
146    ///
147    /// Just the script bytes in hexadecimal **not** consensus encoding of the script i.e., the
148    /// string will not include a length prefix.
149    #[cfg(feature = "alloc")]
150    #[cfg(feature = "hex")]
151    #[inline]
152    #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
153    pub fn to_hex(&self) -> alloc::string::String { alloc::format!("{:x}", self) }
154}
155
156encoding::encoder_newtype_exact! {
157    /// The encoder for the [`Script<T>`] type.
158    pub struct ScriptEncoder<'e>(Encoder2<CompactSizeEncoder, BytesEncoder<'e>>);
159}
160
161impl<T> Encodable for Script<T> {
162    type Encoder<'e>
163        = ScriptEncoder<'e>
164    where
165        Self: 'e;
166
167    fn encoder(&self) -> Self::Encoder<'_> {
168        ScriptEncoder::new(Encoder2::new(
169            CompactSizeEncoder::new(self.as_bytes().len()),
170            BytesEncoder::without_length_prefix(self.as_bytes()),
171        ))
172    }
173}
174
175#[cfg(feature = "arbitrary")]
176impl<'a, T> Arbitrary<'a> for &'a Script<T> {
177    #[inline]
178    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
179        let v = <&'a [u8]>::arbitrary(u)?;
180        Ok(Script::from_bytes(v))
181    }
182}
183
184macro_rules! delegate_index {
185    ($($type:ty),* $(,)?) => {
186        $(
187            /// Script subslicing operation - read [slicing safety](#slicing-safety)!
188            impl<T> Index<$type> for Script<T> {
189                type Output = Self;
190
191                #[inline]
192                fn index(&self, index: $type) -> &Self::Output {
193                    Self::from_bytes(&self.as_bytes()[index])
194                }
195            }
196        )*
197    }
198}
199
200delegate_index!(
201    Range<usize>,
202    RangeFrom<usize>,
203    RangeTo<usize>,
204    RangeFull,
205    RangeInclusive<usize>,
206    RangeToInclusive<usize>,
207    (Bound<usize>, Bound<usize>)
208);
209
210#[cfg(test)]
211mod tests {
212    // All tests should compile and pass no matter which script type you put here.
213    type Script = super::super::ScriptSig;
214
215    #[cfg(feature = "alloc")]
216    use alloc::{borrow::ToOwned, vec};
217
218    #[test]
219    fn script_from_bytes() {
220        let script = Script::from_bytes(&[1, 2, 3]);
221        assert_eq!(script.as_bytes(), [1, 2, 3]);
222    }
223
224    #[test]
225    fn script_from_bytes_mut() {
226        let bytes = &mut [1, 2, 3];
227        let script = Script::from_bytes_mut(bytes);
228        script.as_mut_bytes()[0] = 4;
229        assert_eq!(script.as_mut_bytes(), [4, 2, 3]);
230    }
231
232    #[test]
233    fn script_to_vec() {
234        let script = Script::from_bytes(&[1, 2, 3]);
235        assert_eq!(script.to_vec(), vec![1, 2, 3]);
236    }
237
238    #[test]
239    fn script_len() {
240        let script = Script::from_bytes(&[1, 2, 3]);
241        assert_eq!(script.len(), 3);
242    }
243
244    #[test]
245    fn script_is_empty() {
246        let script: &Script = Default::default();
247        assert!(script.is_empty());
248
249        let script = Script::from_bytes(&[1, 2, 3]);
250        assert!(!script.is_empty());
251    }
252
253    #[test]
254    fn script_to_owned() {
255        let script = Script::from_bytes(&[1, 2, 3]);
256        let script_buf = script.to_owned();
257        assert_eq!(script_buf.as_bytes(), [1, 2, 3]);
258    }
259
260    #[test]
261    fn test_index() {
262        let script = Script::from_bytes(&[1, 2, 3, 4, 5]);
263
264        assert_eq!(script[1..3].as_bytes(), &[2, 3]);
265        assert_eq!(script[2..].as_bytes(), &[3, 4, 5]);
266        assert_eq!(script[..3].as_bytes(), &[1, 2, 3]);
267        assert_eq!(script[..].as_bytes(), &[1, 2, 3, 4, 5]);
268        assert_eq!(script[1..=3].as_bytes(), &[2, 3, 4]);
269        assert_eq!(script[..=2].as_bytes(), &[1, 2, 3]);
270    }
271
272    #[test]
273    #[cfg(feature = "alloc")]
274    fn encode() {
275        // Consensus encoding includes the length of the encoded data
276        // (compact size encoded length prefix).
277        let consensus_encoded: [u8; 6] = [0x05, 1, 2, 3, 4, 5];
278
279        // `from_bytes` does not expect the prefix.
280        let script = Script::from_bytes(&consensus_encoded[1..]);
281
282        let got = encoding::encode_to_vec(script);
283        assert_eq!(got, consensus_encoded);
284    }
285}