Merge rust-bitcoin/rust-bitcoin#4281: Return `ControlBlock` from `Witness::taproot_control_block`

492073f288 Strengthen the type of `taproot_control_block()` (Martin Habovstiak)
e8a42d5851 Unify/reduce usage of `unsafe` (Martin Habovstiak)
d42364bd9d Swap around the fields in `Address` (Martin Habovstiak)
7a115e3cf1 Make `Address` obey sanity rules (Martin Habovstiak)
bc6da1fe07 Swap around the fields in `sha256t::Hash` (Martin Habovstiak)
8ee088df74 Make `sha256t` obey sanity rules (Martin Habovstiak)

Pull request description:

  Well, I thought this PR will be just the last commit... 😅

  Anyway, this implements a bunch of changes to allow returning `ControlBlock` from `Witness` method(s). One cool side effect is that this PR also reduces the number of `unsafe` blocks.

ACKs for top commit:
  apoelstra:
    ACK 492073f28876406f8fe5a07a8a2495c8e0ba1fb3; successfully ran local tests

Tree-SHA512: 11979517cc310abf25644fc93a75deccacae66af8ba2d9b4011fdc3f414b15fac7e748399c7eef492ca850c11b7aacc3f24ec46fccf95e6d57a400212979637e
This commit is contained in:
merge-script 2025-03-28 15:06:43 +00:00
commit a2408e9b0c
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
15 changed files with 606 additions and 347 deletions

View File

@ -158,7 +158,7 @@ pub struct NetworkValidationError {
impl fmt::Display for NetworkValidationError { impl fmt::Display for NetworkValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "address ")?; write!(f, "address ")?;
fmt::Display::fmt(&self.address.0, f)?; fmt::Display::fmt(&self.address.inner(), f)?;
write!(f, " is not valid on {}", self.required) write!(f, " is not valid on {}", self.required)
} }
} }

View File

@ -308,103 +308,108 @@ pub enum AddressData {
}, },
} }
/// A Bitcoin address. internals::transparent_newtype! {
/// /// A Bitcoin address.
/// ### Parsing addresses ///
/// /// ### Parsing addresses
/// When parsing string as an address, one has to pay attention to the network, on which the parsed ///
/// address is supposed to be valid. For the purpose of this validation, `Address` has /// When parsing string as an address, one has to pay attention to the network, on which the parsed
/// [`is_valid_for_network`](Address<NetworkUnchecked>::is_valid_for_network) method. In order to provide more safety, /// address is supposed to be valid. For the purpose of this validation, `Address` has
/// enforced by compiler, `Address` also contains a special marker type, which indicates whether network of the parsed /// [`is_valid_for_network`](Address<NetworkUnchecked>::is_valid_for_network) method. In order to provide more safety,
/// address has been checked. This marker type will prevent from calling certain functions unless the network /// enforced by compiler, `Address` also contains a special marker type, which indicates whether network of the parsed
/// verification has been successfully completed. /// address has been checked. This marker type will prevent from calling certain functions unless the network
/// /// verification has been successfully completed.
/// The result of parsing an address is `Address<NetworkUnchecked>` suggesting that network of the parsed address ///
/// has not yet been verified. To perform this verification, method [`require_network`](Address<NetworkUnchecked>::require_network) /// The result of parsing an address is `Address<NetworkUnchecked>` suggesting that network of the parsed address
/// can be called, providing network on which the address is supposed to be valid. If the verification succeeds, /// has not yet been verified. To perform this verification, method [`require_network`](Address<NetworkUnchecked>::require_network)
/// `Address<NetworkChecked>` is returned. /// can be called, providing network on which the address is supposed to be valid. If the verification succeeds,
/// /// `Address<NetworkChecked>` is returned.
/// The types `Address` and `Address<NetworkChecked>` are synonymous, i. e. they can be used interchangeably. ///
/// /// The types `Address` and `Address<NetworkChecked>` are synonymous, i. e. they can be used interchangeably.
/// ```rust ///
/// use std::str::FromStr; /// ```rust
/// use bitcoin::{Address, Network}; /// use std::str::FromStr;
/// use bitcoin::address::{NetworkUnchecked, NetworkChecked}; /// use bitcoin::{Address, Network};
/// /// use bitcoin::address::{NetworkUnchecked, NetworkChecked};
/// // variant 1 ///
/// let address: Address<NetworkUnchecked> = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf".parse().unwrap(); /// // variant 1
/// let _address: Address<NetworkChecked> = address.require_network(Network::Bitcoin).unwrap(); /// let address: Address<NetworkUnchecked> = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf".parse().unwrap();
/// /// let _address: Address<NetworkChecked> = address.require_network(Network::Bitcoin).unwrap();
/// // variant 2 ///
/// let _address: Address = Address::from_str("32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf").unwrap() /// // variant 2
/// .require_network(Network::Bitcoin).unwrap(); /// let _address: Address = Address::from_str("32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf").unwrap()
/// /// .require_network(Network::Bitcoin).unwrap();
/// // variant 3 ///
/// let _address: Address<NetworkChecked> = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf".parse::<Address<_>>() /// // variant 3
/// .unwrap().require_network(Network::Bitcoin).unwrap(); /// let _address: Address<NetworkChecked> = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf".parse::<Address<_>>()
/// ``` /// .unwrap().require_network(Network::Bitcoin).unwrap();
/// /// ```
/// ### Formatting addresses ///
/// /// ### Formatting addresses
/// To format address into its textual representation, both `Debug` (for usage in programmer-facing, ///
/// debugging context) and `Display` (for user-facing output) can be used, with the following caveats: /// To format address into its textual representation, both `Debug` (for usage in programmer-facing,
/// /// debugging context) and `Display` (for user-facing output) can be used, with the following caveats:
/// 1. `Display` is implemented only for `Address<NetworkChecked>`: ///
/// /// 1. `Display` is implemented only for `Address<NetworkChecked>`:
/// ``` ///
/// # use bitcoin::address::{Address, NetworkChecked}; /// ```
/// let address: Address<NetworkChecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>() /// # use bitcoin::address::{Address, NetworkChecked};
/// .unwrap().assume_checked(); /// let address: Address<NetworkChecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>()
/// assert_eq!(address.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); /// .unwrap().assume_checked();
/// ``` /// assert_eq!(address.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
/// /// ```
/// ```ignore ///
/// # use bitcoin::address::{Address, NetworkChecked}; /// ```ignore
/// let address: Address<NetworkUnchecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>() /// # use bitcoin::address::{Address, NetworkChecked};
/// .unwrap(); /// let address: Address<NetworkUnchecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>()
/// let s = address.to_string(); // does not compile /// .unwrap();
/// ``` /// let s = address.to_string(); // does not compile
/// /// ```
/// 2. `Debug` on `Address<NetworkUnchecked>` does not produce clean address but address wrapped by ///
/// an indicator that its network has not been checked. This is to encourage programmer to properly /// 2. `Debug` on `Address<NetworkUnchecked>` does not produce clean address but address wrapped by
/// check the network and use `Display` in user-facing context. /// an indicator that its network has not been checked. This is to encourage programmer to properly
/// /// check the network and use `Display` in user-facing context.
/// ``` ///
/// # use bitcoin::address::{Address, NetworkUnchecked}; /// ```
/// let address: Address<NetworkUnchecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>() /// # use bitcoin::address::{Address, NetworkUnchecked};
/// .unwrap(); /// let address: Address<NetworkUnchecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>()
/// assert_eq!(format!("{:?}", address), "Address<NetworkUnchecked>(132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM)"); /// .unwrap();
/// ``` /// assert_eq!(format!("{:?}", address), "Address<NetworkUnchecked>(132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM)");
/// /// ```
/// ``` ///
/// # use bitcoin::address::{Address, NetworkChecked}; /// ```
/// let address: Address<NetworkChecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>() /// # use bitcoin::address::{Address, NetworkChecked};
/// .unwrap().assume_checked(); /// let address: Address<NetworkChecked> = "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".parse::<Address<_>>()
/// assert_eq!(format!("{:?}", address), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); /// .unwrap().assume_checked();
/// ``` /// assert_eq!(format!("{:?}", address), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
/// /// ```
/// ### Relevant BIPs ///
/// /// ### Relevant BIPs
/// * [BIP13 - Address Format for pay-to-script-hash](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki) ///
/// * [BIP16 - Pay to Script Hash](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki) /// * [BIP13 - Address Format for pay-to-script-hash](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki)
/// * [BIP141 - Segregated Witness (Consensus layer)](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki) /// * [BIP16 - Pay to Script Hash](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki)
/// * [BIP142 - Address Format for Segregated Witness](https://github.com/bitcoin/bips/blob/master/bip-0142.mediawiki) /// * [BIP141 - Segregated Witness (Consensus layer)](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)
/// * [BIP341 - Taproot: SegWit version 1 spending rules](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) /// * [BIP142 - Address Format for Segregated Witness](https://github.com/bitcoin/bips/blob/master/bip-0142.mediawiki)
/// * [BIP350 - Bech32m format for v1+ witness addresses](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) /// * [BIP341 - Taproot: SegWit version 1 spending rules](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] /// * [BIP350 - Bech32m format for v1+ witness addresses](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
// The `#[repr(transparent)]` attribute is used to guarantee the layout of the `Address` struct. It #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
// is an implementation detail and users should not rely on it in their code. // The `#[repr(transparent)]` attribute is used to guarantee the layout of the `Address` struct. It
#[repr(transparent)] // is an implementation detail and users should not rely on it in their code.
pub struct Address<V = NetworkChecked>(AddressInner, PhantomData<V>) pub struct Address<V = NetworkChecked>(PhantomData<V>, AddressInner)
where where
V: NetworkValidation; V: NetworkValidation;
impl<V> Address<V> {
fn from_inner_ref(inner: &_) -> &Self;
}
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
struct DisplayUnchecked<'a, N: NetworkValidation>(&'a Address<N>); struct DisplayUnchecked<'a, N: NetworkValidation>(&'a Address<N>);
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
impl<N: NetworkValidation> fmt::Display for DisplayUnchecked<'_, N> { impl<N: NetworkValidation> fmt::Display for DisplayUnchecked<'_, N> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0 .0, fmt) } fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0.inner(), fmt) }
} }
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -433,7 +438,7 @@ impl<'de, U: NetworkValidationUnchecked> serde::Deserialize<'de> for Address<U>
{ {
// We know that `U` is only ever `NetworkUnchecked` but the compiler does not. // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
let address = v.parse::<Address<NetworkUnchecked>>().map_err(E::custom)?; let address = v.parse::<Address<NetworkUnchecked>>().map_err(E::custom)?;
Ok(Address(address.0, PhantomData::<U>)) Ok(Address::from_inner(address.into_inner()))
} }
} }
@ -454,18 +459,30 @@ impl<V: NetworkValidation> serde::Serialize for Address<V> {
/// Methods on [`Address`] that can be called on both `Address<NetworkChecked>` and /// Methods on [`Address`] that can be called on both `Address<NetworkChecked>` and
/// `Address<NetworkUnchecked>`. /// `Address<NetworkUnchecked>`.
impl<V: NetworkValidation> Address<V> { impl<V: NetworkValidation> Address<V> {
fn from_inner(inner: AddressInner) -> Self {
Address(PhantomData, inner)
}
fn into_inner(self) -> AddressInner {
self.1
}
fn inner(&self) -> &AddressInner {
&self.1
}
/// Returns a reference to the address as if it was unchecked. /// Returns a reference to the address as if it was unchecked.
pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> { pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
unsafe { &*(self as *const Address<V> as *const Address<NetworkUnchecked>) } Address::from_inner_ref(self.inner())
} }
/// Marks the network of this address as unchecked. /// Marks the network of this address as unchecked.
pub fn into_unchecked(self) -> Address<NetworkUnchecked> { Address(self.0, PhantomData) } pub fn into_unchecked(self) -> Address<NetworkUnchecked> { Address::from_inner(self.into_inner()) }
/// Returns the [`NetworkKind`] of this address. /// Returns the [`NetworkKind`] of this address.
pub fn network_kind(&self) -> NetworkKind { pub fn network_kind(&self) -> NetworkKind {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2pkh { hash: _, ref network } => *network, P2pkh { hash: _, ref network } => *network,
P2sh { hash: _, ref network } => *network, P2sh { hash: _, ref network } => *network,
Segwit { program: _, ref hrp } => NetworkKind::from(*hrp), Segwit { program: _, ref hrp } => NetworkKind::from(*hrp),
@ -481,7 +498,7 @@ impl Address {
#[inline] #[inline]
pub fn p2pkh(pk: impl Into<PubkeyHash>, network: impl Into<NetworkKind>) -> Address { pub fn p2pkh(pk: impl Into<PubkeyHash>, network: impl Into<NetworkKind>) -> Address {
let hash = pk.into(); let hash = pk.into();
Self(AddressInner::P2pkh { hash, network: network.into() }, PhantomData) Self::from_inner(AddressInner::P2pkh { hash, network: network.into() })
} }
/// Constructs a new pay-to-script-hash (P2SH) [`Address`] from a script. /// Constructs a new pay-to-script-hash (P2SH) [`Address`] from a script.
@ -504,7 +521,7 @@ impl Address {
/// The `hash` pre-image (redeem script) must not exceed 520 bytes in length /// The `hash` pre-image (redeem script) must not exceed 520 bytes in length
/// otherwise outputs created from the returned address will be un-spendable. /// otherwise outputs created from the returned address will be un-spendable.
pub fn p2sh_from_hash(hash: ScriptHash, network: impl Into<NetworkKind>) -> Address { pub fn p2sh_from_hash(hash: ScriptHash, network: impl Into<NetworkKind>) -> Address {
Self(AddressInner::P2sh { hash, network: network.into() }, PhantomData) Self::from_inner(AddressInner::P2sh { hash, network: network.into() })
} }
/// Constructs a new pay-to-witness-public-key-hash (P2WPKH) [`Address`] from a public key. /// Constructs a new pay-to-witness-public-key-hash (P2WPKH) [`Address`] from a public key.
@ -577,7 +594,7 @@ impl Address {
/// then you likely do not need this constructor. /// then you likely do not need this constructor.
pub fn from_witness_program(program: WitnessProgram, hrp: impl Into<KnownHrp>) -> Address { pub fn from_witness_program(program: WitnessProgram, hrp: impl Into<KnownHrp>) -> Address {
let inner = AddressInner::Segwit { program, hrp: hrp.into() }; let inner = AddressInner::Segwit { program, hrp: hrp.into() };
Address(inner, PhantomData) Address::from_inner(inner)
} }
/// Gets the address type of the [`Address`]. /// Gets the address type of the [`Address`].
@ -587,7 +604,7 @@ impl Address {
/// None if unknown, non-standard or related to the future witness version. /// None if unknown, non-standard or related to the future witness version.
#[inline] #[inline]
pub fn address_type(&self) -> Option<AddressType> { pub fn address_type(&self) -> Option<AddressType> {
match self.0 { match *self.inner() {
AddressInner::P2pkh { .. } => Some(AddressType::P2pkh), AddressInner::P2pkh { .. } => Some(AddressType::P2pkh),
AddressInner::P2sh { .. } => Some(AddressType::P2sh), AddressInner::P2sh { .. } => Some(AddressType::P2sh),
AddressInner::Segwit { ref program, hrp: _ } => AddressInner::Segwit { ref program, hrp: _ } =>
@ -609,7 +626,7 @@ impl Address {
pub fn to_address_data(&self) -> AddressData { pub fn to_address_data(&self) -> AddressData {
use AddressData::*; use AddressData::*;
match self.0 { match *self.inner() {
AddressInner::P2pkh { hash, network: _ } => P2pkh { pubkey_hash: hash }, AddressInner::P2pkh { hash, network: _ } => P2pkh { pubkey_hash: hash },
AddressInner::P2sh { hash, network: _ } => P2sh { script_hash: hash }, AddressInner::P2sh { hash, network: _ } => P2sh { script_hash: hash },
AddressInner::Segwit { program, hrp: _ } => Segwit { witness_program: program }, AddressInner::Segwit { program, hrp: _ } => Segwit { witness_program: program },
@ -620,7 +637,7 @@ impl Address {
pub fn pubkey_hash(&self) -> Option<PubkeyHash> { pub fn pubkey_hash(&self) -> Option<PubkeyHash> {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2pkh { ref hash, network: _ } => Some(*hash), P2pkh { ref hash, network: _ } => Some(*hash),
_ => None, _ => None,
} }
@ -630,7 +647,7 @@ impl Address {
pub fn script_hash(&self) -> Option<ScriptHash> { pub fn script_hash(&self) -> Option<ScriptHash> {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2sh { ref hash, network: _ } => Some(*hash), P2sh { ref hash, network: _ } => Some(*hash),
_ => None, _ => None,
} }
@ -640,7 +657,7 @@ impl Address {
pub fn witness_program(&self) -> Option<WitnessProgram> { pub fn witness_program(&self) -> Option<WitnessProgram> {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
Segwit { ref program, hrp: _ } => Some(*program), Segwit { ref program, hrp: _ } => Some(*program),
_ => None, _ => None,
} }
@ -689,7 +706,7 @@ impl Address {
/// Generates a script pubkey spending to this address. /// Generates a script pubkey spending to this address.
pub fn script_pubkey(&self) -> ScriptBuf { pub fn script_pubkey(&self) -> ScriptBuf {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2pkh { hash, network: _ } => ScriptBuf::new_p2pkh(hash), P2pkh { hash, network: _ } => ScriptBuf::new_p2pkh(hash),
P2sh { hash, network: _ } => ScriptBuf::new_p2sh(hash), P2sh { hash, network: _ } => ScriptBuf::new_p2sh(hash),
Segwit { ref program, hrp: _ } => { Segwit { ref program, hrp: _ } => {
@ -756,7 +773,7 @@ impl Address {
/// This function doesn't make any allocations. /// This function doesn't make any allocations.
pub fn matches_script_pubkey(&self, script: &Script) -> bool { pub fn matches_script_pubkey(&self, script: &Script) -> bool {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2pkh { ref hash, network: _ } if script.is_p2pkh() => P2pkh { ref hash, network: _ } if script.is_p2pkh() =>
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash), &script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
P2sh { ref hash, network: _ } if script.is_p2sh() => P2sh { ref hash, network: _ } if script.is_p2sh() =>
@ -777,7 +794,7 @@ impl Address {
/// - For SegWit addresses, the payload is the witness program. /// - For SegWit addresses, the payload is the witness program.
fn payload_as_bytes(&self) -> &[u8] { fn payload_as_bytes(&self) -> &[u8] {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2sh { ref hash, network: _ } => hash.as_ref(), P2sh { ref hash, network: _ } => hash.as_ref(),
P2pkh { ref hash, network: _ } => hash.as_ref(), P2pkh { ref hash, network: _ } => hash.as_ref(),
Segwit { ref program, hrp: _ } => program.program().as_bytes(), Segwit { ref program, hrp: _ } => program.program().as_bytes(),
@ -791,7 +808,7 @@ impl Address<NetworkUnchecked> {
/// ///
/// This function is dangerous in case the address is not a valid checked address. /// This function is dangerous in case the address is not a valid checked address.
pub fn assume_checked_ref(&self) -> &Address { pub fn assume_checked_ref(&self) -> &Address {
unsafe { &*(self as *const Address<NetworkUnchecked> as *const Address) } Address::from_inner_ref(self.inner())
} }
/// Parsed addresses do not always have *one* network. The problem is that legacy testnet, /// Parsed addresses do not always have *one* network. The problem is that legacy testnet,
@ -817,7 +834,7 @@ impl Address<NetworkUnchecked> {
/// ``` /// ```
pub fn is_valid_for_network(&self, n: Network) -> bool { pub fn is_valid_for_network(&self, n: Network) -> bool {
use AddressInner::*; use AddressInner::*;
match self.0 { match *self.inner() {
P2pkh { hash: _, ref network } => *network == NetworkKind::from(n), P2pkh { hash: _, ref network } => *network == NetworkKind::from(n),
P2sh { hash: _, ref network } => *network == NetworkKind::from(n), P2sh { hash: _, ref network } => *network == NetworkKind::from(n),
Segwit { program: _, ref hrp } => *hrp == KnownHrp::from_network(n), Segwit { program: _, ref hrp } => *hrp == KnownHrp::from_network(n),
@ -882,7 +899,7 @@ impl Address<NetworkUnchecked> {
/// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses) /// For details about this mechanism, see section [*Parsing addresses*](Address#parsing-addresses)
/// on [`Address`]. /// on [`Address`].
#[inline] #[inline]
pub fn assume_checked(self) -> Address { Address(self.0, PhantomData) } pub fn assume_checked(self) -> Address { Address::from_inner(self.into_inner()) }
/// Parse a bech32 Address string /// Parse a bech32 Address string
pub fn from_bech32_str(s: &str) -> Result<Address<NetworkUnchecked>, Bech32Error> { pub fn from_bech32_str(s: &str) -> Result<Address<NetworkUnchecked>, Bech32Error> {
@ -894,7 +911,7 @@ impl Address<NetworkUnchecked> {
let hrp = KnownHrp::from_hrp(hrp)?; let hrp = KnownHrp::from_hrp(hrp)?;
let inner = AddressInner::Segwit { program, hrp }; let inner = AddressInner::Segwit { program, hrp };
Ok(Address(inner, PhantomData)) Ok(Address::from_inner(inner))
} }
/// Parse a base58 Address string /// Parse a base58 Address string
@ -927,7 +944,7 @@ impl Address<NetworkUnchecked> {
invalid => return Err(InvalidLegacyPrefixError { invalid }.into()), invalid => return Err(InvalidLegacyPrefixError { invalid }.into()),
}; };
Ok(Address(inner, PhantomData)) Ok(Address::from_inner(inner))
} }
} }
@ -938,16 +955,16 @@ impl From<Address> for ScriptBuf {
// Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should // Alternate formatting `{:#}` is used to return uppercase version of bech32 addresses which should
// be used in QR codes, see [`Address::to_qr_uri`]. // be used in QR codes, see [`Address::to_qr_uri`].
impl fmt::Display for Address { impl fmt::Display for Address {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, fmt) } fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.inner(), fmt) }
} }
impl<V: NetworkValidation> fmt::Debug for Address<V> { impl<V: NetworkValidation> fmt::Debug for Address<V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if V::IS_CHECKED { if V::IS_CHECKED {
fmt::Display::fmt(&self.0, f) fmt::Display::fmt(&self.inner(), f)
} else { } else {
write!(f, "Address<NetworkUnchecked>(")?; write!(f, "Address<NetworkUnchecked>(")?;
fmt::Display::fmt(&self.0, f)?; fmt::Display::fmt(&self.inner(), f)?;
write!(f, ")") write!(f, ")")
} }
} }
@ -975,10 +992,10 @@ impl<U: NetworkValidationUnchecked> FromStr for Address<U> {
if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) { if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) {
let address = Address::from_bech32_str(s)?; let address = Address::from_bech32_str(s)?;
// We know that `U` is only ever `NetworkUnchecked` but the compiler does not. // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
Ok(Address(address.0, PhantomData::<U>)) Ok(Address::from_inner(address.into_inner()))
} else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) { } else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) {
let address = Address::from_base58_str(s)?; let address = Address::from_base58_str(s)?;
Ok(Address(address.0, PhantomData::<U>)) Ok(Address::from_inner(address.into_inner()))
} else { } else {
let hrp = match s.rfind('1') { let hrp = match s.rfind('1') {
Some(pos) => &s[..pos], Some(pos) => &s[..pos],

View File

@ -35,33 +35,29 @@ mod primitive {
} }
} }
/// Byte slices that can be in Bitcoin script. internals::transparent_newtype! {
/// /// Byte slices that can be in Bitcoin script.
/// The encoding of Bitcoin script restricts data pushes to be less than 2^32 bytes long. ///
/// This type represents slices that are guaranteed to be within the limit so they can be put in /// The encoding of Bitcoin script restricts data pushes to be less than 2^32 bytes long.
/// the script safely. /// This type represents slices that are guaranteed to be within the limit so they can be put in
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] /// the script safely.
#[repr(transparent)] #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct PushBytes([u8]); pub struct PushBytes([u8]);
impl PushBytes {
/// Constructs a new `&PushBytes` without checking the length.
///
/// The caller is responsible for checking that the length is less than the 2^32.
fn from_slice_unchecked(bytes: &_) -> &Self;
/// Constructs a new `&mut PushBytes` without checking the length.
///
/// The caller is responsible for checking that the length is less than the 2^32.
fn from_mut_slice_unchecked(bytes: &mut _) -> &mut Self;
}
}
impl PushBytes { impl PushBytes {
/// Constructs a new `&PushBytes` without checking the length.
///
/// The caller is responsible for checking that the length is less than the 2^32.
fn from_slice_unchecked(bytes: &[u8]) -> &Self {
// SAFETY: The conversion is sound because &[u8] and &PushBytes
// have the same layout (because of #[repr(transparent)] on PushBytes).
unsafe { &*(bytes as *const [u8] as *const PushBytes) }
}
/// Constructs a new `&mut PushBytes` without checking the length.
///
/// The caller is responsible for checking that the length is less than the 2^32.
fn from_mut_slice_unchecked(bytes: &mut [u8]) -> &mut Self {
// SAFETY: The conversion is sound because &mut [u8] and &mut PushBytes
// have the same layout (because of #[repr(transparent)] on PushBytes).
unsafe { &mut *(bytes as *mut [u8] as *mut PushBytes) }
}
/// Constructs an empty `&PushBytes`. /// Constructs an empty `&PushBytes`.
pub fn empty() -> &'static Self { Self::from_slice_unchecked(&[]) } pub fn empty() -> &'static Self { Self::from_slice_unchecked(&[]) }

View File

@ -10,15 +10,15 @@ use io::{BufRead, Write};
use crate::consensus::encode::{self, Error, ReadExt, WriteExt, MAX_VEC_SIZE}; use crate::consensus::encode::{self, Error, ReadExt, WriteExt, MAX_VEC_SIZE};
use crate::consensus::{Decodable, Encodable}; use crate::consensus::{Decodable, Encodable};
use crate::crypto::ecdsa; use crate::crypto::ecdsa;
use crate::crypto::key::SerializedXOnlyPublicKey;
use crate::prelude::Vec; use crate::prelude::Vec;
#[cfg(doc)] #[cfg(doc)]
use crate::script::ScriptExt as _; use crate::script::ScriptExt as _;
use crate::taproot::{ use crate::taproot::{self, ControlBlock, LeafScript, TAPROOT_ANNEX_PREFIX, TaprootMerkleBranch};
self, ControlBlock, LeafScript, LeafVersion, TAPROOT_ANNEX_PREFIX, TAPROOT_CONTROL_BASE_SIZE,
TAPROOT_LEAF_MASK, TaprootMerkleBranch,
};
use crate::Script; use crate::Script;
type BorrowedControlBlock<'a> = ControlBlock<&'a TaprootMerkleBranch, &'a SerializedXOnlyPublicKey>;
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)] #[doc(inline)]
pub use primitives::witness::{Iter, Witness}; pub use primitives::witness::{Iter, Witness};
@ -176,9 +176,8 @@ crate::internal_macros::define_extension_trait! {
/// version. /// version.
fn taproot_leaf_script(&self) -> Option<LeafScript<&Script>> { fn taproot_leaf_script(&self) -> Option<LeafScript<&Script>> {
match P2TrSpend::from_witness(self) { match P2TrSpend::from_witness(self) {
Some(P2TrSpend::Script { leaf_script, control_block, .. }) if control_block.len() >= TAPROOT_CONTROL_BASE_SIZE => { Some(P2TrSpend::Script { leaf_script, control_block, .. }) => {
let version = LeafVersion::from_consensus(control_block[0] & TAPROOT_LEAF_MASK).ok()?; Some(LeafScript { version: control_block.leaf_version, script: leaf_script, })
Some(LeafScript { version, script: leaf_script, })
}, },
_ => None, _ => None,
} }
@ -191,7 +190,7 @@ crate::internal_macros::define_extension_trait! {
/// byte of the last element being equal to 0x50. /// byte of the last element being equal to 0x50.
/// ///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness. /// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
fn taproot_control_block(&self) -> Option<&[u8]> { fn taproot_control_block(&self) -> Option<BorrowedControlBlock<'_>> {
match P2TrSpend::from_witness(self) { match P2TrSpend::from_witness(self) {
Some(P2TrSpend::Script { control_block, .. }) => Some(control_block), Some(P2TrSpend::Script { control_block, .. }) => Some(control_block),
_ => None, _ => None,
@ -236,7 +235,7 @@ enum P2TrSpend<'a> {
}, },
Script { Script {
leaf_script: &'a Script, leaf_script: &'a Script,
control_block: &'a [u8], control_block: BorrowedControlBlock<'a>,
annex: Option<&'a [u8]>, annex: Option<&'a [u8]>,
}, },
} }
@ -275,17 +274,21 @@ impl<'a> P2TrSpend<'a> {
// last one does NOT start with TAPROOT_ANNEX_PREFIX. This is handled in the catchall // last one does NOT start with TAPROOT_ANNEX_PREFIX. This is handled in the catchall
// arm. // arm.
3.. if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => { 3.. if witness.last().expect("len > 0").starts_with(&[TAPROOT_ANNEX_PREFIX]) => {
let control_block = witness.get_back(1).expect("len > 1");
let control_block = BorrowedControlBlock::decode_borrowed(control_block).ok()?;
let spend = P2TrSpend::Script { let spend = P2TrSpend::Script {
leaf_script: Script::from_bytes(witness.get_back(2).expect("len > 2")), leaf_script: Script::from_bytes(witness.get_back(2).expect("len > 2")),
control_block: witness.get_back(1).expect("len > 1"), control_block,
annex: witness.last(), annex: witness.last(),
}; };
Some(spend) Some(spend)
} }
_ => { _ => {
let control_block = witness.last().expect("len > 0");
let control_block = BorrowedControlBlock::decode_borrowed(control_block).ok()?;
let spend = P2TrSpend::Script { let spend = P2TrSpend::Script {
leaf_script: Script::from_bytes(witness.get_back(1).expect("len > 1")), leaf_script: Script::from_bytes(witness.get_back(1).expect("len > 1")),
control_block: witness.last().expect("len > 0"), control_block,
annex: None, annex: None,
}; };
Some(spend) Some(spend)
@ -324,6 +327,7 @@ mod test {
use crate::consensus::{deserialize, encode, serialize}; use crate::consensus::{deserialize, encode, serialize};
use crate::hex::DisplayHex; use crate::hex::DisplayHex;
use crate::sighash::EcdsaSighashType; use crate::sighash::EcdsaSighashType;
use crate::taproot::LeafVersion;
use crate::Transaction; use crate::Transaction;
#[test] #[test]
@ -383,7 +387,7 @@ mod test {
#[test] #[test]
fn get_tapscript() { fn get_tapscript() {
let tapscript = hex!("deadbeef"); let tapscript = hex!("deadbeef");
let control_block = hex!("02"); let control_block = hex!("c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// annex starting with 0x50 causes the branching logic. // annex starting with 0x50 causes the branching logic.
let annex = hex!("50"); let annex = hex!("50");
@ -431,7 +435,8 @@ mod test {
#[test] #[test]
fn get_control_block() { fn get_control_block() {
let tapscript = hex!("deadbeef"); let tapscript = hex!("deadbeef");
let control_block = hex!("02"); let control_block = hex!("c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
let expected_control_block = BorrowedControlBlock::decode_borrowed(&control_block).unwrap();
// annex starting with 0x50 causes the branching logic. // annex starting with 0x50 causes the branching logic.
let annex = hex!("50"); let annex = hex!("50");
let signature = vec![0xff; 64]; let signature = vec![0xff; 64];
@ -441,15 +446,15 @@ mod test {
let witness_key_spend_annex = Witness::from([&*signature, &annex]); let witness_key_spend_annex = Witness::from([&*signature, &annex]);
// With or without annex, the tapscript should be returned. // With or without annex, the tapscript should be returned.
assert_eq!(witness.taproot_control_block(), Some(&control_block[..])); assert_eq!(witness.taproot_control_block().unwrap(), expected_control_block);
assert_eq!(witness_annex.taproot_control_block(), Some(&control_block[..])); assert_eq!(witness_annex.taproot_control_block().unwrap(), expected_control_block);
assert!(witness_key_spend_annex.taproot_control_block().is_none()) assert!(witness_key_spend_annex.taproot_control_block().is_none())
} }
#[test] #[test]
fn get_annex() { fn get_annex() {
let tapscript = hex!("deadbeef"); let tapscript = hex!("deadbeef");
let control_block = hex!("02"); let control_block = hex!("c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// annex starting with 0x50 causes the branching logic. // annex starting with 0x50 causes the branching logic.
let annex = hex!("50"); let annex = hex!("50");

View File

@ -26,6 +26,7 @@ use crate::taproot::{TapNodeHash, TapTweakHash};
#[rustfmt::skip] // Keep public re-exports separate. #[rustfmt::skip] // Keep public re-exports separate.
pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey}; pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey};
pub use serialized_x_only::SerializedXOnlyPublicKey;
#[cfg(feature = "rand-std")] #[cfg(feature = "rand-std")]
pub use secp256k1::rand; pub use secp256k1::rand;
@ -1222,6 +1223,63 @@ impl fmt::Display for InvalidWifCompressionFlagError {
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl std::error::Error for InvalidWifCompressionFlagError {} impl std::error::Error for InvalidWifCompressionFlagError {}
mod serialized_x_only {
internals::transparent_newtype! {
/// An array of bytes that's semantically an x-only public but was **not** validated.
///
/// This can be useful when validation is not desired but semantics of the bytes should be
/// preserved. The validation can still happen using `to_validated()` method.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct SerializedXOnlyPublicKey([u8; 32]);
impl SerializedXOnlyPublicKey {
pub(crate) fn from_bytes_ref(bytes: &_) -> Self;
}
}
impl SerializedXOnlyPublicKey {
/// Marks the supplied bytes as a serialized x-only public key.
pub const fn from_byte_array(bytes: [u8; 32]) -> Self {
Self(bytes)
}
/// Returns the raw bytes.
pub const fn to_byte_array(self) -> [u8; 32] {
self.0
}
/// Returns a reference to the raw bytes.
pub const fn as_byte_array(&self) -> &[u8; 32] {
&self.0
}
}
}
impl SerializedXOnlyPublicKey {
/// Returns `XOnlyPublicKey` if the bytes are valid.
pub fn to_validated(self) -> Result<XOnlyPublicKey, secp256k1::Error> {
XOnlyPublicKey::from_byte_array(self.as_byte_array())
}
}
impl AsRef<[u8; 32]> for SerializedXOnlyPublicKey {
fn as_ref(&self) -> &[u8; 32] {
self.as_byte_array()
}
}
impl From<&SerializedXOnlyPublicKey> for SerializedXOnlyPublicKey {
fn from(borrowed: &SerializedXOnlyPublicKey) -> Self {
*borrowed
}
}
impl fmt::Debug for SerializedXOnlyPublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.as_byte_array().as_hex(), f)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -9,10 +9,16 @@ pub use privacy_boundary::TaprootMerkleBranch;
mod privacy_boundary { mod privacy_boundary {
use super::*; use super::*;
/// The Merkle proof for inclusion of a tree in a Taproot tree hash. internals::transparent_newtype! {
#[repr(transparent)] /// The Merkle proof for inclusion of a tree in a Taproot tree hash.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TaprootMerkleBranch([TapNodeHash]); pub struct TaprootMerkleBranch([TapNodeHash]);
impl TaprootMerkleBranch {
pub(super) const fn from_hashes_unchecked(hashes: &_) -> &Self;
pub(super) fn from_mut_hashes_unchecked(hashes: &mut _) -> &mut Self;
}
}
impl TaprootMerkleBranch { impl TaprootMerkleBranch {
/// Returns a reference to the slice of hashes. /// Returns a reference to the slice of hashes.
@ -22,18 +28,6 @@ mod privacy_boundary {
/// Returns a reference to the mutable slice of hashes. /// Returns a reference to the mutable slice of hashes.
#[inline] #[inline]
pub fn as_mut_slice(&mut self) -> &mut [TapNodeHash] { &mut self.0 } pub fn as_mut_slice(&mut self) -> &mut [TapNodeHash] { &mut self.0 }
pub(super) const fn from_hashes_unchecked(hashes: &[TapNodeHash]) -> &Self {
unsafe {
&*(hashes as *const _ as *const Self)
}
}
pub(super) fn from_mut_hashes_unchecked(hashes: &mut [TapNodeHash]) -> &mut Self {
unsafe {
&mut *(hashes as *mut _ as *mut Self)
}
}
} }
} }

View File

@ -22,7 +22,7 @@ use io::Write;
use secp256k1::{Scalar, Secp256k1}; use secp256k1::{Scalar, Secp256k1};
use crate::consensus::Encodable; use crate::consensus::Encodable;
use crate::crypto::key::{TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey}; use crate::crypto::key::{SerializedXOnlyPublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey};
use crate::prelude::{BTreeMap, BTreeSet, BinaryHeap, Vec}; use crate::prelude::{BTreeMap, BTreeSet, BinaryHeap, Vec};
use crate::{Script, ScriptBuf}; use crate::{Script, ScriptBuf};
@ -1141,13 +1141,13 @@ impl<'leaf> ScriptLeaf<'leaf> {
/// Control block data structure used in Tapscript satisfaction. /// Control block data structure used in Tapscript satisfaction.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ControlBlock<Branch = TaprootMerkleBranchBuf> where Branch: ?Sized { pub struct ControlBlock<Branch = TaprootMerkleBranchBuf, Key = UntweakedPublicKey> where Branch: ?Sized {
/// The tapleaf version. /// The tapleaf version.
pub leaf_version: LeafVersion, pub leaf_version: LeafVersion,
/// The parity of the output key (NOT THE INTERNAL KEY WHICH IS ALWAYS XONLY). /// The parity of the output key (NOT THE INTERNAL KEY WHICH IS ALWAYS XONLY).
pub output_key_parity: secp256k1::Parity, pub output_key_parity: secp256k1::Parity,
/// The internal key. /// The internal key.
pub internal_key: UntweakedPublicKey, pub internal_key: Key,
/// The Merkle proof of a script associated with this leaf. /// The Merkle proof of a script associated with this leaf.
pub merkle_branch: Branch, pub merkle_branch: Branch,
} }
@ -1166,6 +1166,24 @@ impl ControlBlock {
/// - [`TaprootError::InvalidInternalKey`] if internal key is invalid (first 32 bytes after the parity byte). /// - [`TaprootError::InvalidInternalKey`] if internal key is invalid (first 32 bytes after the parity byte).
/// - [`TaprootError::InvalidMerkleTreeDepth`] if Merkle tree is too deep (more than 128 levels). /// - [`TaprootError::InvalidMerkleTreeDepth`] if Merkle tree is too deep (more than 128 levels).
pub fn decode(sl: &[u8]) -> Result<ControlBlock, TaprootError> { pub fn decode(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
use alloc::borrow::ToOwned;
let ControlBlock {
leaf_version,
output_key_parity,
internal_key,
merkle_branch,
} = ControlBlock::<&TaprootMerkleBranch, &SerializedXOnlyPublicKey>::decode_borrowed(sl)?;
let internal_key = internal_key.to_validated().map_err(TaprootError::InvalidInternalKey)?;
let merkle_branch = merkle_branch.to_owned();
Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch })
}
}
impl<B, K> ControlBlock<B, K> {
pub(crate) fn decode_borrowed<'a>(sl: &'a [u8]) -> Result<Self, TaprootError> where B: From<&'a TaprootMerkleBranch>, K: From<&'a SerializedXOnlyPublicKey> {
let (base, merkle_branch) = sl.split_first_chunk::<TAPROOT_CONTROL_BASE_SIZE>() let (base, merkle_branch) = sl.split_first_chunk::<TAPROOT_CONTROL_BASE_SIZE>()
.ok_or(InvalidControlBlockSizeError(sl.len()))?; .ok_or(InvalidControlBlockSizeError(sl.len()))?;
@ -1177,9 +1195,8 @@ impl ControlBlock {
}; };
let leaf_version = LeafVersion::from_consensus(first & TAPROOT_LEAF_MASK)?; let leaf_version = LeafVersion::from_consensus(first & TAPROOT_LEAF_MASK)?;
let internal_key = UntweakedPublicKey::from_byte_array(internal_key) let internal_key = SerializedXOnlyPublicKey::from_bytes_ref(internal_key).into();
.map_err(TaprootError::InvalidInternalKey)?; let merkle_branch = TaprootMerkleBranch::decode(merkle_branch)?.into();
let merkle_branch = TaprootMerkleBranchBuf::decode(merkle_branch)?;
Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch }) Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch })
} }
} }

View File

@ -117,10 +117,21 @@ pub(crate) use general_hash_type;
macro_rules! hash_type_no_default { macro_rules! hash_type_no_default {
($bits:expr, $reverse:expr, $doc:literal) => { ($bits:expr, $reverse:expr, $doc:literal) => {
#[doc = $doc] internals::transparent_newtype! {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[doc = $doc]
#[repr(transparent)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Hash([u8; $bits / 8]); pub struct Hash([u8; $bits / 8]);
impl Hash {
/// Zero cost conversion between a fixed length byte array shared reference and
/// a shared reference to this Hash type.
pub fn from_bytes_ref(bytes: &_) -> &Self;
/// Zero cost conversion between a fixed length byte array exclusive reference and
/// an exclusive reference to this Hash type.
pub fn from_bytes_mut(bytes: &mut _) -> &mut Self;
}
}
impl Hash { impl Hash {
const fn internal_new(arr: [u8; $bits / 8]) -> Self { Hash(arr) } const fn internal_new(arr: [u8; $bits / 8]) -> Self { Hash(arr) }
@ -130,20 +141,6 @@ macro_rules! hash_type_no_default {
Self::internal_new(bytes) Self::internal_new(bytes)
} }
/// Zero cost conversion between a fixed length byte array shared reference and
/// a shared reference to this Hash type.
pub fn from_bytes_ref(bytes: &[u8; $bits / 8]) -> &Self {
// Safety: Sound because Self is #[repr(transparent)] containing [u8; $bits / 8]
unsafe { &*(bytes as *const _ as *const Self) }
}
/// Zero cost conversion between a fixed length byte array exclusive reference and
/// an exclusive reference to this Hash type.
pub fn from_bytes_mut(bytes: &mut [u8; $bits / 8]) -> &mut Self {
// Safety: Sound because Self is #[repr(transparent)] containing [u8; $bits / 8]
unsafe { &mut *(bytes as *mut _ as *mut Self) }
}
/// Copies a byte slice into a hash object. /// Copies a byte slice into a hash object.
#[deprecated(since = "0.15.0", note = "use `from_byte_array` instead")] #[deprecated(since = "0.15.0", note = "use `from_byte_array` instead")]
#[allow(deprecated_in_future)] // Because of `FromSliceError`. #[allow(deprecated_in_future)] // Because of `FromSliceError`.

View File

@ -43,33 +43,30 @@ pub trait Tag {
const MIDSTATE: sha256::Midstate; const MIDSTATE: sha256::Midstate;
} }
/// Output of the SHA256t hash function. internals::transparent_newtype! {
#[repr(transparent)] /// Output of the SHA256t hash function.
pub struct Hash<T>([u8; 32], PhantomData<T>); pub struct Hash<T>(PhantomData<T>, [u8; 32]);
impl<T> Hash<T> {
/// Zero cost conversion between a fixed length byte array shared reference and
/// a shared reference to this Hash type.
pub fn from_bytes_ref(bytes: &_) -> &Self;
/// Zero cost conversion between a fixed length byte array exclusive reference and
/// an exclusive reference to this Hash type.
pub fn from_bytes_mut(bytes: &mut _) -> &mut Self;
}
}
impl<T> Hash<T> impl<T> Hash<T>
where where
T: Tag, T: Tag,
{ {
const fn internal_new(arr: [u8; 32]) -> Self { Hash(arr, PhantomData) } const fn internal_new(arr: [u8; 32]) -> Self { Hash(PhantomData, arr) }
/// Constructs a new hash from the underlying byte array. /// Constructs a new hash from the underlying byte array.
pub const fn from_byte_array(bytes: [u8; 32]) -> Self { Self::internal_new(bytes) } pub const fn from_byte_array(bytes: [u8; 32]) -> Self { Self::internal_new(bytes) }
/// Zero cost conversion between a fixed length byte array shared reference and
/// a shared reference to this Hash type.
pub fn from_bytes_ref(bytes: &[u8; 32]) -> &Self {
// Safety: Sound because Self is #[repr(transparent)] containing [u8; 32]
unsafe { &*(bytes as *const _ as *const Self) }
}
/// Zero cost conversion between a fixed length byte array exclusive reference and
/// an exclusive reference to this Hash type.
pub fn from_bytes_mut(bytes: &mut [u8; 32]) -> &mut Self {
// Safety: Sound because Self is #[repr(transparent)] containing [u8; 32]
unsafe { &mut *(bytes as *mut _ as *mut Self) }
}
/// Copies a byte slice into a hash object. /// Copies a byte slice into a hash object.
#[deprecated(since = "0.15.0", note = "use `from_byte_array` instead")] #[deprecated(since = "0.15.0", note = "use `from_byte_array` instead")]
#[allow(deprecated_in_future)] // Because of `FromSliceError`. #[allow(deprecated_in_future)] // Because of `FromSliceError`.
@ -117,10 +114,10 @@ where
} }
/// Returns the underlying byte array. /// Returns the underlying byte array.
pub const fn to_byte_array(self) -> [u8; 32] { self.0 } pub const fn to_byte_array(self) -> [u8; 32] { self.1 }
/// Returns a reference to the underlying byte array. /// Returns a reference to the underlying byte array.
pub const fn as_byte_array(&self) -> &[u8; 32] { &self.0 } pub const fn as_byte_array(&self) -> &[u8; 32] { &self.1 }
} }
impl<T: Tag> Copy for Hash<T> {} impl<T: Tag> Copy for Hash<T> {}
@ -128,7 +125,7 @@ impl<T: Tag> Clone for Hash<T> {
fn clone(&self) -> Self { *self } fn clone(&self) -> Self { *self }
} }
impl<T: Tag> PartialEq for Hash<T> { impl<T: Tag> PartialEq for Hash<T> {
fn eq(&self, other: &Hash<T>) -> bool { self.0 == other.0 } fn eq(&self, other: &Hash<T>) -> bool { self.as_byte_array() == other.as_byte_array() }
} }
impl<T: Tag> Eq for Hash<T> {} impl<T: Tag> Eq for Hash<T> {}
impl<T: Tag> PartialOrd for Hash<T> { impl<T: Tag> PartialOrd for Hash<T> {
@ -137,10 +134,10 @@ impl<T: Tag> PartialOrd for Hash<T> {
} }
} }
impl<T: Tag> Ord for Hash<T> { impl<T: Tag> Ord for Hash<T> {
fn cmp(&self, other: &Hash<T>) -> cmp::Ordering { cmp::Ord::cmp(&self.0, &other.0) } fn cmp(&self, other: &Hash<T>) -> cmp::Ordering { cmp::Ord::cmp(&self.as_byte_array(), &other.as_byte_array()) }
} }
impl<T: Tag> core::hash::Hash for Hash<T> { impl<T: Tag> core::hash::Hash for Hash<T> {
fn hash<H: core::hash::Hasher>(&self, h: &mut H) { self.0.hash(h) } fn hash<H: core::hash::Hasher>(&self, h: &mut H) { self.as_byte_array().hash(h) }
} }
crate::internal_macros::hash_trait_impls!(256, false, T: Tag); crate::internal_macros::hash_trait_impls!(256, false, T: Tag);

View File

@ -35,6 +35,12 @@ pub mod rust_version {
include!(concat!(env!("OUT_DIR"), "/rust_version.rs")); include!(concat!(env!("OUT_DIR"), "/rust_version.rs"));
} }
#[doc(hidden)]
pub mod _export {
#[cfg(feature = "alloc")]
pub extern crate alloc;
}
pub mod array; pub mod array;
pub mod array_vec; pub mod array_vec;
pub mod compact_size; pub mod compact_size;

View File

@ -37,3 +37,211 @@ macro_rules! impl_to_hex_from_lower_hex {
} }
}; };
} }
/// Creates a transparent wrapper around an inner type and soundly implements reference casts.
///
/// This macro takes care of several issues related to newtypes that need to allow casting their
/// inner types to themselves:
///
/// * It makes sure to put repr(transparent) on the type
/// * It optionally implements conversions from `&`, `&mut`, `Box`, `Rc`, `Arc`
/// * It makes sure to put `#[inline]` on all of these conversions since they are trivial
/// * It makes sure the reference cast is const
/// * It makes sure the `Arc` conversion is conditioned on `target_has_atomic = "ptr"`
///
/// Usage: just type the struct inside the macro as you would implementing it manually except leave
/// `#[repr(transparent)]` out. Then add an impl block for the just-defined type containing function
/// declarations that take a reference/smart pointer to `_` (use literal underscore; e.g. `&_` for
/// shared references) and return `Self` behind the appropriate "pointer" type. Do not write the
/// body, just semicolon.
///
/// The `alloc` types MUST NOT have import paths and don't need imports.
#[macro_export]
macro_rules! transparent_newtype {
(
$(#[$($struct_attr:tt)*])*
$vis:vis struct $newtype:tt$(<$gen:ident $(= $default:ty)?>)?($($fields:tt)+) $(where $($where_ty:ty: $bound:path),* $(,)?)?;
impl$(<$gen2:tt>)? $newtype2:ident$(<$gen3:tt>)? {
$(
$(#[$($fn_attr:tt)*])*
$fn_vis:vis $(const)? fn $fn:ident($fn_arg_name:ident: $($fn_arg_ty:tt)+) -> $fn_ret_ty:ty;
)*
}
) => {
$crate::_check_tts_eq!($newtype2, $newtype, "the type name in the impl block doesn't match the struct name");
$(
// WARNING: renaming has to be disabled for soundness!
// If it weren't it'd be possible to make the type inside struct not match the one passed
// to functions. In principle we could also omit the generics but that'd be confusing for
// readers.
$crate::_check_tts_eq!($gen2, $gen, "the name of the left generic parameter in impl block doesn't match the one on struct");
$crate::_check_tts_eq!($gen3, $gen, "the name of the right generic parameter in impl block doesn't match the one on struct");
)?
$(#[$($struct_attr)*])*
#[repr(transparent)]
$vis struct $newtype$(<$gen $(= $default)?>)?($($fields)+) $(where $($where_ty: $bound),*)?;
impl$(<$gen2>)? $newtype$(<$gen3>)? $(where $($where_ty: $bound),*)? {
$crate::_transparent_ref_conversions! {
$crate::_transparent_newtype_inner_type!($($fields)+);
$(
$(#[$($fn_attr)*])*
$fn_vis fn $fn($fn_arg_name: $($fn_arg_ty)+) -> $fn_ret_ty;
)+
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! _transparent_ref_conversions {
(
$inner:ty;
$(
$(#[$($fn_attr:tt)*])*
$fn_vis:vis $(const)? fn $fn:ident($fn_arg_name:ident: $($fn_arg_ty:tt)+) -> $fn_ret_ty:ty;
)+
) => {
$(
$crate::_transparent_ref_conversion! {
$inner;
$(#[$($fn_attr)*])*
$fn_vis fn $fn($fn_arg_name: $($fn_arg_ty)+) -> $fn_ret_ty;
}
)+
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! _transparent_ref_conversion {
(
$inner:ty;
$(#[$($from_ref_attr:tt)*])*
$from_ref_vis:vis fn $from_ref:ident($from_ref_arg_name:ident: &_) -> $fn_ret_ty:ty;
) => {
#[inline]
$(#[$($from_ref_attr)*])*
$from_ref_vis const fn $from_ref($from_ref_arg_name: &$inner) -> &Self {
// SAFETY: the pointer is created by casting a pointer that is pointing to an object
// with the same layout and validity invariants and the previous pointer was created
// directly from a reference. (Notice repr(transparent).)
// The lifetime of the input reference matches the lifetime of the returned reference.
unsafe { &*($from_ref_arg_name as *const $inner as *const Self) }
}
};
(
$inner:ty;
$(#[$($from_mut_attr:tt)*])*
$from_mut_vis:vis fn $from_mut:ident($from_mut_arg_name:ident: &mut _) -> $fn_ret_ty:ty;
) => {
#[inline]
$(#[$($from_mut_attr)*])*
$from_mut_vis fn $from_mut($from_mut_arg_name: &mut $inner) -> &mut Self {
// SAFETY: the pointer is created by casting a pointer that is pointing to an object
// with the same layout and validity invariants and the previous pointer was created
// directly from a reference. (Notice repr(transparent).)
// The lifetime of the input reference matches the lifetime of the returned reference.
unsafe { &mut *($from_mut_arg_name as *mut $inner as *mut Self) }
}
};
(
$inner:ty;
$(#[$($from_box_attr:tt)*])*
$from_box_vis:vis fn $from_box:ident($from_box_arg_name:ident: Box<_>) -> $fn_ret_ty:ty;
) => {
$crate::_emit_alloc! {
$(#[$($from_box_attr)*])*
#[inline]
$from_box_vis fn $from_box($from_box_arg_name: $crate::_export::alloc::boxed::Box<$inner>) -> $crate::_export::alloc::boxed::Box<Self> {
let ptr = $crate::_export::alloc::boxed::Box::into_raw($from_box_arg_name);
// SAFETY: the pointer is created by casting a pointer that is pointing to an object
// with the same layout and validity invariants and the previous pointer was created
// directly from box. (Notice repr(transparent).)
unsafe { $crate::_export::alloc::boxed::Box::from_raw(ptr as *mut Self) }
}
}
};
(
$inner:ty;
$(#[$($from_rc_attr:tt)*])*
$from_rc_vis:vis fn $from_rc:ident($from_rc_arg_name:ident: Rc<_>) -> $fn_ret_ty:ty;
) => {
$crate::_emit_alloc! {
$(#[$($from_rc_attr)*])*
#[inline]
$from_rc_vis fn $from_rc($from_rc_arg_name: $crate::_export::alloc::rc::Rc<$inner>) -> $crate::_export::alloc::rc::Rc<Self> {
let ptr = $crate::_export::alloc::rc::Rc::into_raw($from_rc_arg_name);
// SAFETY: the pointer is created by casting a pointer that is pointing to an object
// with the same layout and validity invariants and the previous pointer was created
// directly from box. (Notice repr(transparent).)
unsafe { $crate::_export::alloc::rc::Rc::from_raw(ptr as *mut Self) }
}
}
};
(
$inner:ty;
$(#[$($from_arc_attr:tt)*])*
$from_arc_vis:vis fn $from_arc:ident($from_arc_arg_name:ident: Arc<_>) -> $fn_ret_ty:ty;
) => {
$crate::_emit_alloc! {
$(#[$($from_arc_attr)*])*
#[cfg(target_has_atomic = "ptr")]
#[inline]
$from_arc_vis fn $from_arc($from_arc_arg_name: $crate::_export::alloc::sync::Arc<$inner>) -> $crate::_export::alloc::sync::Arc<Self> {
let ptr = $crate::_export::alloc::sync::Arc::into_raw($from_arc_arg_name);
// SAFETY: the pointer is created by casting a pointer that is pointing to an object
// with the same layout and validity invariants and the previous pointer was created
// directly from box. (Notice repr(transparent).)
unsafe { $crate::_export::alloc::sync::Arc::from_raw(ptr as *mut Self) }
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! _check_tts_eq {
($left:tt, $right:tt, $message:literal) => {
macro_rules! token_eq {
($right) => {};
($any:tt) => { compile_error!($message) };
}
token_eq!($left);
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! _transparent_newtype_inner_type {
($(#[$($field_attr:tt)*])* $inner:ty) => {
$inner
};
($(#[$($phantom_attr:tt)*])* PhantomData<$phantom:ty>, $(#[$($field_attr:tt)*])* $inner:ty) => {
$inner
};
}
/// Emits given tokens only if the `alloc` feature **in this crate** is enabled.
///
/// (The feature is currently enabled.)
#[cfg(feature = "alloc")]
#[doc(hidden)]
#[macro_export]
macro_rules! _emit_alloc {
($($tokens:tt)*) => { $($tokens)* };
}
/// Emits given tokens only if the `alloc` feature **in this crate** is enabled.
///
/// (The feature is currently disabled.)
#[cfg(not(feature = "alloc"))]
#[doc(hidden)]
#[macro_export]
macro_rules! _emit_alloc {
($($tokens:tt)*) => {};
}

View File

@ -1,35 +1,26 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use internals::rust_version; use internals::rust_version;
/// A bridging wrapper providing the I/O traits for types that already implement `std` I/O traits. internals::transparent_newtype! {
#[repr(transparent)] /// A bridging wrapper providing the I/O traits for types that already implement `std` I/O traits.
#[derive(Debug)] #[derive(Debug)]
pub struct FromStd<T>(T); pub struct FromStd<T>(T);
impl<T> FromStd<T> {
/// Wraps a mutable reference to I/O type.
pub fn new_mut(inner: &mut _) -> &mut Self;
/// Wraps a boxed I/O type.
pub fn new_boxed(inner: Box<_>) -> Box<Self>;
}
}
impl<T> FromStd<T> { impl<T> FromStd<T> {
/// Wraps an I/O type. /// Wraps an I/O type.
#[inline] #[inline]
pub const fn new(inner: T) -> Self { Self(inner) } pub const fn new(inner: T) -> Self { Self(inner) }
/// Wraps a mutable reference to I/O type.
#[inline]
pub fn new_mut(inner: &mut T) -> &mut Self {
// SAFETY: the type is repr(transparent) and the lifetimes match
unsafe { &mut *(inner as *mut _ as *mut Self) }
}
/// Wraps a boxed I/O type.
#[cfg(feature = "alloc")]
#[inline]
pub fn new_boxed(inner: Box<T>) -> Box<Self> {
// SAFETY: the type is repr(transparent) and the pointer is created from Box
unsafe { Box::from_raw(Box::into_raw(inner) as *mut Self) }
}
/// Returns the wrapped value. /// Returns the wrapped value.
#[inline] #[inline]
pub fn into_inner(self) -> T { self.0 } pub fn into_inner(self) -> T { self.0 }
@ -117,31 +108,25 @@ impl<T: std::io::Write> std::io::Write for FromStd<T> {
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { self.0.write_all(buf) } fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { self.0.write_all(buf) }
} }
/// A bridging wrapper providing the std traits for types that already implement our traits. internals::transparent_newtype! {
#[repr(transparent)] /// A bridging wrapper providing the std traits for types that already implement our traits.
#[derive(Debug)] #[derive(Debug)]
pub struct ToStd<T>(T); pub struct ToStd<T>(T);
impl<T> ToStd<T> {
/// Wraps a mutable reference to I/O type.
pub fn new_mut(inner: &mut _) -> &mut Self;
/// Wraps a boxed I/O type.
pub fn new_boxed(inner: Box<_>) -> Box<Self>;
}
}
impl<T> ToStd<T> { impl<T> ToStd<T> {
/// Wraps an I/O type. /// Wraps an I/O type.
#[inline] #[inline]
pub const fn new(inner: T) -> Self { Self(inner) } pub const fn new(inner: T) -> Self { Self(inner) }
/// Wraps a mutable reference to I/O type.
#[inline]
pub fn new_mut(inner: &mut T) -> &mut Self {
// SAFETY: the type is repr(transparent) and the lifetimes match
unsafe { &mut *(inner as *mut _ as *mut Self) }
}
/// Wraps a boxed I/O type.
#[cfg(feature = "alloc")]
#[inline]
pub fn new_boxed(inner: Box<T>) -> Box<Self> {
// SAFETY: the type is repr(transparent) and the pointer is created from Box
unsafe { Box::from_raw(Box::into_raw(inner) as *mut Self) }
}
/// Returns the wrapped value. /// Returns the wrapped value.
#[inline] #[inline]
pub fn into_inner(self) -> T { self.0 } pub fn into_inner(self) -> T { self.0 }

View File

@ -10,54 +10,67 @@ use arbitrary::{Arbitrary, Unstructured};
use super::ScriptBuf; use super::ScriptBuf;
use crate::prelude::{Box, ToOwned, Vec}; use crate::prelude::{Box, ToOwned, Vec};
/// Bitcoin script slice. internals::transparent_newtype! {
/// /// Bitcoin script slice.
/// *[See also the `bitcoin::script` module](super).* ///
/// /// *[See also the `bitcoin::script` module](super).*
/// `Script` is a script slice, the most primitive script type. It's usually seen in its borrowed ///
/// form `&Script`. It is always encoded as a series of bytes representing the opcodes and data /// `Script` is a script slice, the most primitive script type. It's usually seen in its borrowed
/// pushes. /// form `&Script`. It is always encoded as a series of bytes representing the opcodes and data
/// /// pushes.
/// ## Validity ///
/// /// ## Validity
/// `Script` does not have any validity invariants - it's essentially just a marked slice of ///
/// bytes. This is similar to [`Path`](std::path::Path) vs [`OsStr`](std::ffi::OsStr) where they /// `Script` does not have any validity invariants - it's essentially just a marked slice of
/// are trivially cast-able to each-other and `Path` doesn't guarantee being a usable FS path but /// bytes. This is similar to [`Path`](std::path::Path) vs [`OsStr`](std::ffi::OsStr) where they
/// having a newtype still has value because of added methods, readability and basic type checking. /// are trivially cast-able to each-other and `Path` doesn't guarantee being a usable FS path but
/// /// having a newtype still has value because of added methods, readability and basic type checking.
/// Although at least data pushes could be checked not to overflow the script, bad scripts are ///
/// allowed to be in a transaction (outputs just become unspendable) and there even are such /// Although at least data pushes could be checked not to overflow the script, bad scripts are
/// transactions in the chain. Thus we must allow such scripts to be placed in the transaction. /// allowed to be in a transaction (outputs just become unspendable) and there even are such
/// /// transactions in the chain. Thus we must allow such scripts to be placed in the transaction.
/// ## Slicing safety ///
/// /// ## Slicing safety
/// Slicing is similar to how `str` works: some ranges may be incorrect and indexing by ///
/// `usize` is not supported. However, as opposed to `std`, we have no way of checking /// Slicing is similar to how `str` works: some ranges may be incorrect and indexing by
/// correctness without causing linear complexity so there are **no panics on invalid /// `usize` is not supported. However, as opposed to `std`, we have no way of checking
/// ranges!** If you supply an invalid range, you'll get a garbled script. /// correctness without causing linear complexity so there are **no panics on invalid
/// /// ranges!** If you supply an invalid range, you'll get a garbled script.
/// The range is considered valid if it's at a boundary of instruction. Care must be taken ///
/// especially with push operations because you could get a reference to arbitrary /// The range is considered valid if it's at a boundary of instruction. Care must be taken
/// attacker-supplied bytes that look like a valid script. /// especially with push operations because you could get a reference to arbitrary
/// /// attacker-supplied bytes that look like a valid script.
/// It is recommended to use `.instructions()` method to get an iterator over script ///
/// instructions and work with that instead. /// It is recommended to use `.instructions()` method to get an iterator over script
/// /// instructions and work with that instead.
/// ## Memory safety ///
/// /// ## Memory safety
/// The type is `#[repr(transparent)]` for internal purposes only! ///
/// No consumer crate may rely on the representation of the struct! /// The type is `#[repr(transparent)]` for internal purposes only!
/// /// No consumer crate may rely on the representation of the struct!
/// ## References ///
/// /// ## References
/// ///
/// ### Bitcoin Core References ///
/// /// ### Bitcoin Core References
/// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410) ///
/// /// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410)
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] ///
#[repr(transparent)] #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Script([u8]); pub struct Script([u8]);
impl Script {
/// Treat byte slice as `Script`
pub const fn from_bytes(bytes: &_) -> &Self;
/// Treat mutable byte slice as `Script`
pub fn from_bytes_mut(bytes: &mut _) -> &mut Self;
pub(crate) fn from_boxed_bytes(bytes: Box<_>) -> Box<Self>;
pub(crate) fn from_rc_bytes(bytes: Rc<_>) -> Rc<Self>;
pub(crate) fn from_arc_bytes(bytes: Arc<_>) -> Arc<Self>;
}
}
impl Default for &Script { impl Default for &Script {
#[inline] #[inline]
@ -76,28 +89,6 @@ impl Script {
#[inline] #[inline]
pub const fn new() -> &'static Self { Self::from_bytes(&[]) } pub const fn new() -> &'static Self { Self::from_bytes(&[]) }
/// Treat byte slice as `Script`
#[inline]
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 Self) }
}
/// Treat mutable byte slice as `Script`
#[inline]
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 Self) }
}
/// Returns the script data as a byte slice. /// Returns the script data as a byte slice.
#[inline] #[inline]
pub const fn as_bytes(&self) -> &[u8] { &self.0 } pub const fn as_bytes(&self) -> &[u8] { &self.0 }

View File

@ -277,24 +277,14 @@ impl<'a> From<&'a Script> for Cow<'a, Script> {
impl<'a> From<&'a Script> for Arc<Script> { impl<'a> From<&'a Script> for Arc<Script> {
#[inline] #[inline]
fn from(value: &'a Script) -> Self { fn from(value: &'a Script) -> Self {
let rw: *const [u8] = Arc::into_raw(Arc::from(value.as_bytes())); Script::from_arc_bytes(Arc::from(value.as_bytes()))
// SAFETY: copied from `std`
// The pointer was just created from an Arc without deallocating
// Casting a slice to a transparent struct wrapping that slice is sound (same
// layout).
unsafe { Arc::from_raw(rw as *const Script) }
} }
} }
impl<'a> From<&'a Script> for Rc<Script> { impl<'a> From<&'a Script> for Rc<Script> {
#[inline] #[inline]
fn from(value: &'a Script) -> Self { fn from(value: &'a Script) -> Self {
let rw: *const [u8] = Rc::into_raw(Rc::from(value.as_bytes())); Script::from_rc_bytes(Rc::from(value.as_bytes()))
// SAFETY: copied from `std`
// The pointer was just created from an Rc without deallocating
// Casting a slice to a transparent struct wrapping that slice is sound (same
// layout).
unsafe { Rc::from_raw(rw as *const Script) }
} }
} }

View File

@ -54,9 +54,7 @@ impl ScriptBuf {
#[must_use] #[must_use]
#[inline] #[inline]
pub fn into_boxed_script(self) -> Box<Script> { pub fn into_boxed_script(self) -> Box<Script> {
// Copied from PathBuf::into_boxed_path Script::from_boxed_bytes(self.into_bytes().into_boxed_slice())
let rw = Box::into_raw(self.into_bytes().into_boxed_slice()) as *mut Script;
unsafe { Box::from_raw(rw) }
} }
/// Constructs a new empty script with pre-allocated capacity. /// Constructs a new empty script with pre-allocated capacity.