Skip to main content

bitcoin_primitives/script/
borrowed.rs

1// SPDX-License-Identifier: CC0-1.0
2
3#[cfg(feature = "alloc")]
4#[cfg(feature = "hex")]
5use alloc::string::String;
6use core::marker::PhantomData;
7use core::ops::{
8    Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
9};
10
11#[cfg(feature = "arbitrary")]
12use arbitrary::{Arbitrary, Unstructured};
13use encoding::{Encode, PrefixedBytesEncoder};
14
15use super::{ScriptBuf, P2A_PROGRAM};
16use crate::opcodes::all::{OP_CHECKSIG, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_RETURN};
17use crate::opcodes::{Opcode, OP_PUSHBYTES_2, OP_PUSHBYTES_20, OP_PUSHBYTES_32};
18use crate::prelude::{Box, ToOwned, Vec};
19use crate::script::{
20    Builder, RedeemScriptSizeError, ScriptHash, ScriptHashableTag, WScriptHash,
21    WitnessScriptSizeError,
22};
23use crate::witness_version::WitnessVersion;
24use crate::{ScriptPubKey, WitnessScript};
25
26// Defined in `REPO_DIR/include/newtype.rs`.
27crate::transparent_newtype! {
28    /// Bitcoin script slice.
29    ///
30    /// *[See also the `bitcoin::script` module](super).*
31    ///
32    /// `Script` is a script slice, the most primitive script type. It's usually seen in its borrowed
33    /// form `&Script`. It is always encoded as a series of bytes representing the opcodes and data
34    /// pushes.
35    ///
36    /// # Validity
37    ///
38    /// `Script` does not have any validity invariants - it's essentially just a marked slice of
39    /// bytes. This is similar to [`Path`](std::path::Path) vs [`OsStr`](std::ffi::OsStr) where they
40    /// are trivially cast-able to each-other and `Path` doesn't guarantee being a usable FS path but
41    /// having a newtype still has value because of added methods, readability and basic type checking.
42    ///
43    /// Although at least data pushes could be checked not to overflow the script, bad scripts are
44    /// allowed to be in a transaction (outputs just become unspendable) and there even are such
45    /// transactions in the chain. Thus we must allow such scripts to be placed in the transaction.
46    ///
47    /// # Slicing safety
48    ///
49    /// Slicing is similar to how `str` works: some ranges may be incorrect and indexing by
50    /// `usize` is not supported. However, as opposed to `std`, we have no way of checking
51    /// correctness without causing linear complexity so there are **no panics on invalid
52    /// ranges!** If you supply an invalid range, you'll get a garbled script.
53    ///
54    /// The range is considered valid if it's at a boundary of instruction. Care must be taken
55    /// especially with push operations because you could get a reference to arbitrary
56    /// attacker-supplied bytes that look like a valid script.
57    ///
58    /// It is recommended to use `.instructions()` method to get an iterator over script
59    /// instructions and work with that instead.
60    ///
61    /// # Memory safety
62    ///
63    /// The type is `#[repr(transparent)]` for internal purposes only!
64    /// No consumer crate may rely on the representation of the struct!
65    ///
66    /// # Hexadecimal strings
67    ///
68    /// Scripts are consensus encoded with a length prefix and as a result of this in some places in
69    /// the ecosystem one will encounter hex strings that include the prefix while in other places
70    /// the prefix is excluded. To support parsing and formatting scripts as hex we provide a bunch
71    /// of different APIs and trait implementations. Please see [`examples/script.rs`] for a
72    /// thorough example of all the APIs.
73    ///
74    /// [`examples/script.rs`]: <https://github.com/rust-bitcoin/rust-bitcoin/blob/master/bitcoin/examples/script.rs>
75    ///
76    /// # Bitcoin Core References
77    ///
78    /// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410)
79    ///
80    #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
81    pub struct Script<T>(PhantomData<T>, [u8]);
82
83    impl<T> Script<T> {
84        /// Treat byte slice as `Script`
85        pub const fn from_bytes(bytes: &_) -> &Self;
86
87        /// Treat mutable byte slice as `Script`
88        pub fn from_bytes_mut(bytes: &mut _) -> &mut Self;
89
90        pub(crate) fn from_boxed_bytes(bytes: Box<_>) -> Box<Self>;
91        pub(crate) fn from_rc_bytes(bytes: Rc<_>) -> Rc<Self>;
92        pub(crate) fn from_arc_bytes(bytes: Arc<_>) -> Arc<Self>;
93    }
94}
95
96impl<T: 'static> Default for &Script<T> {
97    #[inline]
98    fn default() -> Self { Script::new() }
99}
100
101impl<T> ToOwned for Script<T> {
102    type Owned = ScriptBuf<T>;
103
104    #[inline]
105    fn to_owned(&self) -> Self::Owned { ScriptBuf::from_bytes(self.to_vec()) }
106}
107
108impl<T> Script<T> {
109    /// Constructs a new empty script.
110    #[inline]
111    pub const fn new() -> &'static Self { Self::from_bytes(&[]) }
112
113    /// Returns the script data as a byte slice.
114    ///
115    /// This is just the script bytes **not** consensus encoding (which includes a length prefix).
116    #[inline]
117    pub const fn as_bytes(&self) -> &[u8] { &self.1 }
118
119    /// Returns the script data as a mutable byte slice.
120    ///
121    /// This is just the script bytes **not** consensus encoding (which includes a length prefix).
122    #[inline]
123    pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.1 }
124
125    /// Returns a copy of the script data.
126    ///
127    /// This is just the script bytes **not** consensus encoding (which includes a length prefix).
128    #[inline]
129    pub fn to_vec(&self) -> Vec<u8> { self.as_bytes().to_owned() }
130
131    /// Returns a copy of the script data.
132    #[inline]
133    #[deprecated(since = "0.101.0", note = "use to_vec instead")]
134    pub fn to_bytes(&self) -> Vec<u8> { self.to_vec() }
135
136    /// Consensus encodes the script as lower-case hex.
137    ///
138    /// Consensus encoding includes a length prefix. To hex encode without the length prefix use
139    /// `to_hex_string_no_length_prefix`.
140    #[cfg(feature = "alloc")]
141    #[cfg(feature = "hex")]
142    pub fn to_hex_string_prefixed(&self) -> String {
143        use hex::{BytesToHexIter, Case};
144
145        let iter = encoding::EncoderByteIter::new(self.encoder());
146        BytesToHexIter::new(iter, Case::Lower).flatten().map(char::from).collect()
147    }
148
149    /// Encodes the script as lower-case hex.
150    ///
151    /// This is **not** consensus encoding. The returned hex string will not include the length
152    /// prefix. See `to_hex_string_prefixed`.
153    #[cfg(feature = "alloc")]
154    #[cfg(feature = "hex")]
155    pub fn to_hex_string_no_length_prefix(&self) -> String {
156        use hex::DisplayHex as _;
157
158        self.as_bytes().to_lower_hex_string()
159    }
160
161    /// Returns the length in bytes of the script.
162    #[inline]
163    pub const fn len(&self) -> usize { self.as_bytes().len() }
164
165    /// Returns whether the script is the empty script.
166    #[inline]
167    pub const fn is_empty(&self) -> bool { self.as_bytes().is_empty() }
168
169    /// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
170    #[must_use]
171    #[inline]
172    pub fn into_script_buf(self: Box<Self>) -> ScriptBuf<T> {
173        let rw = Box::into_raw(self) as *mut [u8];
174        // SAFETY: copied from `std`
175        // The pointer was just created from a box without deallocating
176        // Casting a transparent struct wrapping a slice to the slice pointer is sound (same
177        // layout).
178        let inner = unsafe { Box::from_raw(rw) };
179        ScriptBuf::from_bytes(Vec::from(inner))
180    }
181
182    /// Gets the hex representation of this script.
183    ///
184    /// # Returns
185    ///
186    /// Just the script bytes in hexadecimal **not** consensus encoding of the script i.e., the
187    /// string will not include a length prefix.
188    #[cfg(feature = "hex")]
189    #[inline]
190    #[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
191    pub fn to_hex(&self) -> alloc::string::String { alloc::format!("{:x}", self) }
192
193    /// Constructs a new script builder
194    pub fn builder() -> Builder<T> { Builder::new() }
195
196    /// Returns witness version of the script, if any.
197    ///
198    /// # Returns
199    ///
200    /// The witness version if this script is found to conform to the SegWit rules:
201    ///
202    /// > A scriptPubKey (or redeemScript as defined in BIP-0016/P2SH) that consists of a 1-byte
203    /// > push opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new
204    /// > special meaning. The value of the first push is called the "version byte". The following
205    /// > byte vector pushed is called the "witness program".
206    #[inline]
207    pub fn witness_version(&self) -> Option<WitnessVersion>
208    where
209        T: ScriptHashableTag,
210    {
211        let script_len = self.len();
212        if !(4..=42).contains(&script_len) {
213            return None;
214        }
215
216        let ver_opcode = Opcode::from(self.as_bytes()[0]); // Version 0 or PUSHNUM_1-PUSHNUM_16
217        let push_opbyte = self.as_bytes()[1]; // Second byte push opcode 2-40 bytes
218
219        // If push_opbyte < OP_PUSHBYTES_2 || push_opbyte > OP_PUSHBYTES_40
220        if push_opbyte < 0x02 || push_opbyte > 0x28 {
221            return None;
222        }
223        // Check that the rest of the script has the correct size
224        if script_len - 2 != push_opbyte as usize {
225            return None;
226        }
227
228        WitnessVersion::try_from(ver_opcode).ok()
229    }
230
231    /// Checks whether a script pubkey is a P2WSH output.
232    #[inline]
233    pub fn is_p2wsh(&self) -> bool
234    where
235        T: ScriptHashableTag,
236    {
237        self.len() == 34
238            && self.witness_version() == Some(WitnessVersion::V0)
239            && self.as_bytes()[1] == OP_PUSHBYTES_32.to_u8()
240    }
241
242    /// Checks whether a script pubkey is a P2WPKH output.
243    #[inline]
244    pub fn is_p2wpkh(&self) -> bool
245    where
246        T: ScriptHashableTag,
247    {
248        self.len() == 22
249            && self.witness_version() == Some(WitnessVersion::V0)
250            && self.as_bytes()[1] == OP_PUSHBYTES_20.to_u8()
251    }
252}
253
254impl ScriptPubKey {
255    /// Checks whether a script pubkey is a Segregated Witness (SegWit) program.
256    #[inline]
257    pub fn is_witness_program(&self) -> bool { self.witness_version().is_some() }
258
259    /// Checks whether a script pubkey is a P2SH output.
260    #[inline]
261    pub fn is_p2sh(&self) -> bool {
262        self.len() == 23
263            && self.as_bytes()[0] == OP_HASH160.to_u8()
264            && self.as_bytes()[1] == OP_PUSHBYTES_20.to_u8()
265            && self.as_bytes()[22] == OP_EQUAL.to_u8()
266    }
267
268    /// Checks whether a script pubkey is a P2PKH output.
269    #[inline]
270    pub fn is_p2pkh(&self) -> bool {
271        self.len() == 25
272            && self.as_bytes()[0] == OP_DUP.to_u8()
273            && self.as_bytes()[1] == OP_HASH160.to_u8()
274            && self.as_bytes()[2] == OP_PUSHBYTES_20.to_u8()
275            && self.as_bytes()[23] == OP_EQUALVERIFY.to_u8()
276            && self.as_bytes()[24] == OP_CHECKSIG.to_u8()
277    }
278
279    /// Checks whether a script pubkey is a P2A output.
280    #[inline]
281    pub fn is_p2a(&self) -> bool {
282        self.len() == 4
283            && self.witness_version() == Some(WitnessVersion::V1)
284            && self.as_bytes()[1] == OP_PUSHBYTES_2.to_u8()
285            && self.as_bytes()[2..] == P2A_PROGRAM
286    }
287
288    /// Check if this is a consensus-valid `OP_RETURN` output.
289    ///
290    /// To validate if the `OP_RETURN` obeys Bitcoin Core's current standardness policy, use
291    /// `bitcoin::ScriptPubKeyExt::is_standard_op_return()` instead.
292    #[inline]
293    pub fn is_op_return(&self) -> bool {
294        self.as_bytes().first().is_some_and(|&b| b == OP_RETURN.to_u8())
295    }
296}
297
298impl WitnessScript {
299    /// Returns 256-bit hash of the script for P2WSH outputs.
300    ///
301    /// # Errors
302    ///
303    /// Returns an error if the script exceeds 10,000 bytes.
304    #[inline]
305    pub fn wscript_hash(&self) -> Result<WScriptHash, WitnessScriptSizeError> {
306        WScriptHash::from_script(self)
307    }
308}
309
310impl<T: ScriptHashableTag> Script<T> {
311    /// Returns 160-bit hash of the script for P2SH outputs.
312    ///
313    /// # Errors
314    ///
315    /// Returns an error if the script exceeds 520 bytes.
316    #[inline]
317    pub fn script_hash(&self) -> Result<ScriptHash, RedeemScriptSizeError> {
318        ScriptHash::from_script(self)
319    }
320}
321
322impl<T> Encode for Script<T> {
323    type Encoder<'e>
324        = ScriptEncoder<'e>
325    where
326        Self: 'e;
327
328    fn encoder(&self) -> Self::Encoder<'_> {
329        ScriptEncoder::new(PrefixedBytesEncoder::new(self.as_bytes()))
330    }
331}
332
333encoding::encoder_newtype_exact! {
334    /// The encoder for the [`Script<T>`] type.
335    #[derive(Debug, Clone)]
336    pub struct ScriptEncoder<'e>(PrefixedBytesEncoder<'e>);
337}
338
339#[cfg(feature = "arbitrary")]
340impl<'a, T> Arbitrary<'a> for &'a Script<T> {
341    #[inline]
342    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
343        let v = <&'a [u8]>::arbitrary(u)?;
344        Ok(Script::from_bytes(v))
345    }
346}
347
348macro_rules! delegate_index {
349    ($($type:ty),* $(,)?) => {
350        $(
351            /// Script subslicing operation - read [slicing safety](#slicing-safety)!
352            impl<T> Index<$type> for Script<T> {
353                type Output = Self;
354
355                #[inline]
356                fn index(&self, index: $type) -> &Self::Output {
357                    Self::from_bytes(&self.as_bytes()[index])
358                }
359            }
360        )*
361    }
362}
363
364delegate_index!(
365    Range<usize>,
366    RangeFrom<usize>,
367    RangeTo<usize>,
368    RangeFull,
369    RangeInclusive<usize>,
370    RangeToInclusive<usize>,
371    (Bound<usize>, Bound<usize>)
372);