Skip to content

Commit

Permalink
Merge rust-bitcoin#4089: Script improvements
Browse files Browse the repository at this point in the history
5680b4e Refer to `Script{Buf}` as `Self` where relevant (Martin Habovstiak)
ce55dd5 Make `ScriptHash` & `WScriptHash` obey sanity rule (Martin Habovstiak)
9ec9adc Add a note about Electrum's script hashes (Martin Habovstiak)
82f553a Expose `ScriptBuf`'s `capacity` (Martin Habovstiak)
6b9d439 Remove stale FIXME comments (Martin Habovstiak)
0567e6f Put `#[inline]` on most `Script{Buf}` methods (Martin Habovstiak)
b7e2af1 Implement `Arbitrary` for `&'a Script` (Martin Habovstiak)
bca2864 Move `Deref{Mut}` from common module to `owned` (Martin Habovstiak)
3b15e90 Add `const` to some `Script` methods (Martin Habovstiak)
277223d Make `Script` and `ScriptBuf` obey sanity rules (Martin Habovstiak)

Pull request description:

  This implements various improvements related to `Script`. Please refer to the individual commits for details.

  This is a part of rust-bitcoin#4059

ACKs for top commit:
  tcharding:
    ACK 5680b4e
  apoelstra:
    ACK 5680b4e; successfully ran local tests

Tree-SHA512: 5daa8bf6c0b439a579d31d23944077e4a7fa89e14052003d2b81c745f225147f8f6f693d068e0567830027cefea7dda2516596da632bc817199352fa29af0a9b
  • Loading branch information
apoelstra committed Feb 24, 2025
2 parents 5a0d5d7 + 5680b4e commit 6c286e3
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 51 deletions.
40 changes: 27 additions & 13 deletions primitives/src/script/borrowed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use core::ops::{
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};

#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};

use super::ScriptBuf;
use crate::prelude::{Box, ToOwned, Vec};

Expand Down Expand Up @@ -54,7 +57,7 @@ use crate::prelude::{Box, ToOwned, Vec};
///
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Script(pub(in crate::script) [u8]);
pub struct Script([u8]);

impl Default for &Script {
#[inline]
Expand All @@ -64,47 +67,48 @@ impl Default for &Script {
impl ToOwned for Script {
type Owned = ScriptBuf;

fn to_owned(&self) -> Self::Owned { ScriptBuf(self.0.to_owned()) }
#[inline]
fn to_owned(&self) -> Self::Owned { ScriptBuf::from_bytes(self.to_vec()) }
}

impl Script {
/// Constructs a new empty script.
#[inline]
pub fn new() -> &'static Script { Script::from_bytes(&[]) }
pub const fn new() -> &'static Self { Self::from_bytes(&[]) }

/// Treat byte slice as `Script`
#[inline]
pub fn from_bytes(bytes: &[u8]) -> &Script {
pub const fn from_bytes(bytes: &[u8]) -> &Self {
// SAFETY: copied from `std`
// The pointer was just created from a reference which is still alive.
// Casting slice pointer to a transparent struct wrapping that slice is sound (same
// layout).
unsafe { &*(bytes as *const [u8] as *const Script) }
unsafe { &*(bytes as *const [u8] as *const Self) }
}

/// Treat mutable byte slice as `Script`
#[inline]
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Script {
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
// SAFETY: copied from `std`
// The pointer was just created from a reference which is still alive.
// Casting slice pointer to a transparent struct wrapping that slice is sound (same
// layout).
// Function signature prevents callers from accessing `bytes` while the returned reference
// is alive.
unsafe { &mut *(bytes as *mut [u8] as *mut Script) }
unsafe { &mut *(bytes as *mut [u8] as *mut Self) }
}

/// Returns the script data as a byte slice.
#[inline]
pub fn as_bytes(&self) -> &[u8] { &self.0 }
pub const fn as_bytes(&self) -> &[u8] { &self.0 }

/// Returns the script data as a mutable byte slice.
#[inline]
pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.0 }

/// Returns a copy of the script data.
#[inline]
pub fn to_vec(&self) -> Vec<u8> { self.0.to_owned() }
pub fn to_vec(&self) -> Vec<u8> { self.as_bytes().to_owned() }

/// Returns a copy of the script data.
#[inline]
Expand All @@ -113,22 +117,32 @@ impl Script {

/// Returns the length in bytes of the script.
#[inline]
pub fn len(&self) -> usize { self.0.len() }
pub const fn len(&self) -> usize { self.as_bytes().len() }

/// Returns whether the script is the empty script.
#[inline]
pub fn is_empty(&self) -> bool { self.0.is_empty() }
pub const fn is_empty(&self) -> bool { self.as_bytes().is_empty() }

/// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
#[must_use]
#[inline]
pub fn into_script_buf(self: Box<Self>) -> ScriptBuf {
let rw = Box::into_raw(self) as *mut [u8];
// SAFETY: copied from `std`
// The pointer was just created from a box without deallocating
// Casting a transparent struct wrapping a slice to the slice pointer is sound (same
// layout).
let inner = unsafe { Box::from_raw(rw) };
ScriptBuf(Vec::from(inner))
ScriptBuf::from_bytes(Vec::from(inner))
}
}

#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for &'a Script {
#[inline]
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let v = <&'a [u8]>::arbitrary(u)?;
Ok(Script::from_bytes(v))
}
}

Expand All @@ -141,7 +155,7 @@ macro_rules! delegate_index {

#[inline]
fn index(&self, index: $type) -> &Self::Output {
Self::from_bytes(&self.0[index])
Self::from_bytes(&self.as_bytes()[index])
}
}
)*
Expand Down
Loading

0 comments on commit 6c286e3

Please sign in to comment.