Skip to main content

tidecoin_primitives/script/
push_bytes.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::fmt;
4use core::ops::{
5    Bound, Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
6    RangeToInclusive,
7};
8
9use crate::prelude::{Borrow, BorrowMut, ToOwned, Vec};
10
11#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
12fn check_limit(_: usize) -> Result<(), PushBytesError> {
13    Ok(())
14}
15
16#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
17fn check_limit(len: usize) -> Result<(), PushBytesError> {
18    if len < 0x0001_0000_0000 {
19        Ok(())
20    } else {
21        Err(PushBytesError { len })
22    }
23}
24
25internals::transparent_newtype! {
26    /// Byte slices that can be pushed into script.
27    #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
28    pub struct PushBytes([u8]);
29
30    impl PushBytes {
31        fn from_slice_unchecked(bytes: &_) -> &Self;
32        fn from_mut_slice_unchecked(bytes: &mut _) -> &mut Self;
33    }
34}
35
36impl PushBytes {
37    /// Constructs an empty `&PushBytes`.
38    pub fn empty() -> &'static Self {
39        Self::from_slice_unchecked(&[])
40    }
41
42    /// Returns the underlying bytes.
43    pub fn as_bytes(&self) -> &[u8] {
44        &self.0
45    }
46
47    /// Returns the underlying mutable bytes.
48    pub fn as_mut_bytes(&mut self) -> &mut [u8] {
49        &mut self.0
50    }
51
52    /// Returns the number of bytes in the slice.
53    pub fn len(&self) -> usize {
54        self.as_bytes().len()
55    }
56
57    /// Returns whether the slice is empty.
58    pub fn is_empty(&self) -> bool {
59        self.as_bytes().is_empty()
60    }
61
62    /// Decodes a minimal script integer with the standard 4-byte bound.
63    ///
64    /// # Errors
65    ///
66    /// Returns [`ScriptIntError`] when the encoding is non-minimal or overflows the 4-byte bound.
67    ///
68    /// # Panics
69    ///
70    /// Panics only if the internal 4-byte bound invariant is violated.
71    pub fn read_scriptint(&self) -> Result<i32, ScriptIntError> {
72        let ret = self.read_scriptint_internal(4)?;
73        Ok(i32::try_from(ret).expect("4 bytes or less fits in i32"))
74    }
75
76    /// Decodes a minimal script integer with the CHECKLOCKTIMEVERIFY 5-byte bound.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`ScriptIntError`] when the encoding is non-minimal or overflows the 5-byte bound.
81    pub fn read_cltv_scriptint(&self) -> Result<i64, ScriptIntError> {
82        self.read_scriptint_internal(5)
83    }
84
85    fn read_scriptint_internal(&self, max_size: usize) -> Result<i64, ScriptIntError> {
86        super::read_scriptnum(self.as_bytes(), true, max_size)
87    }
88}
89
90macro_rules! delegate_index {
91    ($($type:ty),* $(,)?) => {
92        $(
93            impl Index<$type> for PushBytes {
94                type Output = Self;
95
96                #[inline]
97                #[track_caller]
98                fn index(&self, index: $type) -> &Self::Output {
99                    Self::from_slice_unchecked(&self.0[index])
100                }
101            }
102
103            impl IndexMut<$type> for PushBytes {
104                #[inline]
105                #[track_caller]
106                fn index_mut(&mut self, index: $type) -> &mut Self::Output {
107                    Self::from_mut_slice_unchecked(&mut self.0[index])
108                }
109            }
110        )*
111    }
112}
113
114delegate_index!(
115    Range<usize>,
116    RangeFrom<usize>,
117    RangeTo<usize>,
118    RangeFull,
119    RangeInclusive<usize>,
120    RangeToInclusive<usize>,
121    (Bound<usize>, Bound<usize>)
122);
123
124impl Index<usize> for PushBytes {
125    type Output = u8;
126
127    #[inline]
128    #[track_caller]
129    fn index(&self, index: usize) -> &Self::Output {
130        &self.0[index]
131    }
132}
133
134impl IndexMut<usize> for PushBytes {
135    #[inline]
136    #[track_caller]
137    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
138        &mut self.0[index]
139    }
140}
141
142impl<'a> TryFrom<&'a [u8]> for &'a PushBytes {
143    type Error = PushBytesError;
144
145    fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
146        check_limit(bytes.len())?;
147        Ok(PushBytes::from_slice_unchecked(bytes))
148    }
149}
150
151impl<'a> TryFrom<&'a mut [u8]> for &'a mut PushBytes {
152    type Error = PushBytesError;
153
154    fn try_from(bytes: &'a mut [u8]) -> Result<Self, Self::Error> {
155        check_limit(bytes.len())?;
156        Ok(PushBytes::from_mut_slice_unchecked(bytes))
157    }
158}
159
160macro_rules! from_array {
161    ($($len:literal),* $(,)?) => {
162        $(
163            impl<'a> From<&'a [u8; $len]> for &'a PushBytes {
164                fn from(bytes: &'a [u8; $len]) -> Self {
165                    const _: () = [(); 1][($len >= 0x100000000u64) as usize];
166                    PushBytes::from_slice_unchecked(bytes)
167                }
168            }
169
170            impl<'a> From<&'a mut [u8; $len]> for &'a mut PushBytes {
171                fn from(bytes: &'a mut [u8; $len]) -> Self {
172                    PushBytes::from_mut_slice_unchecked(bytes)
173                }
174            }
175
176            impl AsRef<PushBytes> for [u8; $len] {
177                fn as_ref(&self) -> &PushBytes { self.into() }
178            }
179
180            impl AsMut<PushBytes> for [u8; $len] {
181                fn as_mut(&mut self) -> &mut PushBytes { self.into() }
182            }
183
184            impl From<[u8; $len]> for PushBytesBuf {
185                fn from(bytes: [u8; $len]) -> Self { PushBytesBuf(Vec::from(&bytes)) }
186            }
187
188            impl<'a> From<&'a [u8; $len]> for PushBytesBuf {
189                fn from(bytes: &'a [u8; $len]) -> Self { PushBytesBuf(Vec::from(bytes)) }
190            }
191        )*
192    }
193}
194
195// Sizes up to 76 cover common pubkey/signature pushes and preserve prior Tidecoin ergonomics.
196from_array! {
197    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
198    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
199    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
200    71, 72, 73, 74, 75, 76
201}
202
203/// Owned script-push bytes.
204#[derive(Default, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
205pub struct PushBytesBuf(Vec<u8>);
206
207impl PushBytesBuf {
208    /// Constructs an empty buffer.
209    pub const fn new() -> Self {
210        Self(Vec::new())
211    }
212
213    /// Constructs an empty buffer with reserved capacity.
214    pub fn with_capacity(capacity: usize) -> Self {
215        Self(Vec::with_capacity(capacity))
216    }
217
218    /// Reserves additional capacity.
219    pub fn reserve(&mut self, additional_capacity: usize) {
220        self.0.reserve(additional_capacity);
221    }
222
223    /// Appends a byte.
224    ///
225    /// # Errors
226    ///
227    /// Returns [`PushBytesError`] when the buffer would exceed the maximum supported length.
228    pub fn push(&mut self, byte: u8) -> Result<(), PushBytesError> {
229        check_limit(self.0.len().saturating_add(1))?;
230        self.0.push(byte);
231        Ok(())
232    }
233
234    /// Appends a slice.
235    ///
236    /// # Errors
237    ///
238    /// Returns [`PushBytesError`] when the resulting buffer would exceed the maximum supported length.
239    pub fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), PushBytesError> {
240        let len = self.0.len().saturating_add(bytes.len());
241        check_limit(len)?;
242        self.0.extend_from_slice(bytes);
243        Ok(())
244    }
245
246    /// Removes the last byte.
247    pub fn pop(&mut self) -> Option<u8> {
248        self.0.pop()
249    }
250
251    /// Removes the byte at `index`.
252    #[track_caller]
253    pub fn remove(&mut self, index: usize) -> u8 {
254        self.0.remove(index)
255    }
256
257    /// Clears the buffer.
258    pub fn clear(&mut self) {
259        self.0.clear();
260    }
261
262    /// Truncates the buffer.
263    pub fn truncate(&mut self, len: usize) {
264        self.0.truncate(len);
265    }
266
267    /// Returns the underlying borrowed form.
268    pub fn as_push_bytes(&self) -> &PushBytes {
269        PushBytes::from_slice_unchecked(&self.0)
270    }
271
272    /// Returns the underlying mutable borrowed form.
273    pub fn as_mut_push_bytes(&mut self) -> &mut PushBytes {
274        PushBytes::from_mut_slice_unchecked(&mut self.0)
275    }
276
277    pub(crate) fn inner(&self) -> &Vec<u8> {
278        &self.0
279    }
280
281    /// Returns the number of bytes in buffer.
282    pub fn len(&self) -> usize {
283        self.inner().len()
284    }
285
286    /// Returns the number of bytes the buffer can contain without reallocating.
287    pub fn capacity(&self) -> usize {
288        self.inner().capacity()
289    }
290
291    /// Returns whether the buffer is empty.
292    pub fn is_empty(&self) -> bool {
293        self.inner().is_empty()
294    }
295}
296
297impl From<PushBytesBuf> for Vec<u8> {
298    fn from(value: PushBytesBuf) -> Self {
299        value.0
300    }
301}
302
303impl TryFrom<Vec<u8>> for PushBytesBuf {
304    type Error = PushBytesError;
305
306    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
307        let _: &PushBytes = vec.as_slice().try_into()?;
308        Ok(Self(vec))
309    }
310}
311
312impl ToOwned for PushBytes {
313    type Owned = PushBytesBuf;
314
315    fn to_owned(&self) -> Self::Owned {
316        PushBytesBuf(self.0.to_owned())
317    }
318}
319
320impl AsRef<[u8]> for PushBytes {
321    fn as_ref(&self) -> &[u8] {
322        self.as_bytes()
323    }
324}
325
326impl AsMut<[u8]> for PushBytes {
327    fn as_mut(&mut self) -> &mut [u8] {
328        self.as_mut_bytes()
329    }
330}
331
332impl AsRef<Self> for PushBytes {
333    fn as_ref(&self) -> &Self {
334        self
335    }
336}
337
338impl AsMut<Self> for PushBytes {
339    fn as_mut(&mut self) -> &mut Self {
340        self
341    }
342}
343
344impl AsRef<PushBytes> for PushBytesBuf {
345    fn as_ref(&self) -> &PushBytes {
346        self.as_push_bytes()
347    }
348}
349
350impl AsMut<PushBytes> for PushBytesBuf {
351    fn as_mut(&mut self) -> &mut PushBytes {
352        self.as_mut_push_bytes()
353    }
354}
355
356impl AsRef<[u8]> for PushBytesBuf {
357    fn as_ref(&self) -> &[u8] {
358        self.as_push_bytes().as_bytes()
359    }
360}
361
362impl Deref for PushBytesBuf {
363    type Target = PushBytes;
364
365    fn deref(&self) -> &Self::Target {
366        self.as_push_bytes()
367    }
368}
369
370impl DerefMut for PushBytesBuf {
371    fn deref_mut(&mut self) -> &mut Self::Target {
372        self.as_mut_push_bytes()
373    }
374}
375
376impl Borrow<PushBytes> for PushBytesBuf {
377    fn borrow(&self) -> &PushBytes {
378        self.as_push_bytes()
379    }
380}
381
382impl BorrowMut<PushBytes> for PushBytesBuf {
383    fn borrow_mut(&mut self) -> &mut PushBytes {
384        self.as_mut_push_bytes()
385    }
386}
387
388/// Error constructing script push bytes.
389#[derive(Debug, Clone, PartialEq, Eq)]
390pub struct PushBytesError {
391    /// Invalid length.
392    pub len: usize,
393}
394
395impl fmt::Display for PushBytesError {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        write!(
398            f,
399            "attempt to prepare {} bytes to be pushed into script but the limit is 2^32-1",
400            self.len
401        )
402    }
403}
404
405#[cfg(feature = "std")]
406impl std::error::Error for PushBytesError {}
407
408/// Possible errors that can arise from reading a script integer.
409#[derive(Debug, Clone, PartialEq, Eq)]
410#[non_exhaustive]
411pub enum ScriptIntError {
412    /// The result is not in range [-2^31 +1...2^31 -1].
413    NumericOverflow,
414    /// The resulting encoding is non-minimal.
415    NonMinimal,
416}
417
418impl fmt::Display for ScriptIntError {
419    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420        match self {
421            Self::NumericOverflow => f.write_str("script integer outside of valid range"),
422            Self::NonMinimal => f.write_str("non-minimal encoded script integer"),
423        }
424    }
425}
426
427#[cfg(feature = "std")]
428impl std::error::Error for ScriptIntError {}