Move script types to primitives
Move the `Script` and `ScriptBuf` types to `primitives`, nothing else.
This commit is contained in:
parent
ec4635904b
commit
d649c06238
|
@ -1,139 +1,25 @@
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::{
|
|
||||||
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
|
||||||
};
|
|
||||||
|
|
||||||
use internals::ToU64 as _;
|
use internals::ToU64 as _;
|
||||||
|
|
||||||
use super::witness_version::WitnessVersion;
|
use super::witness_version::WitnessVersion;
|
||||||
use super::{
|
use super::{
|
||||||
Builder, Instruction, InstructionIndices, Instructions, PushBytes, RedeemScriptSizeError,
|
Builder, Instruction, InstructionIndices, Instructions, PushBytes, RedeemScriptSizeError,
|
||||||
ScriptBuf, ScriptHash, WScriptHash, WitnessScriptSizeError,
|
ScriptHash, WScriptHash, WitnessScriptSizeError,
|
||||||
};
|
};
|
||||||
use crate::consensus::Encodable;
|
use crate::consensus::Encodable;
|
||||||
use crate::opcodes::all::*;
|
use crate::opcodes::all::*;
|
||||||
use crate::opcodes::{self, Opcode};
|
use crate::opcodes::{self, Opcode};
|
||||||
use crate::policy::DUST_RELAY_TX_FEE;
|
use crate::policy::DUST_RELAY_TX_FEE;
|
||||||
use crate::prelude::{sink, Box, DisplayHex, String, ToOwned, ToString, Vec};
|
use crate::prelude::{sink, DisplayHex, String, ToString};
|
||||||
use crate::taproot::{LeafVersion, TapLeafHash};
|
use crate::taproot::{LeafVersion, TapLeafHash};
|
||||||
use crate::FeeRate;
|
use crate::FeeRate;
|
||||||
|
|
||||||
/// Bitcoin script slice.
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
///
|
#[doc(inline)]
|
||||||
/// *[See also the `bitcoin::script` module](super).*
|
pub use primitives::script::Script;
|
||||||
///
|
|
||||||
/// `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
|
|
||||||
/// pushes.
|
|
||||||
///
|
|
||||||
/// ## 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
|
|
||||||
/// 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
|
|
||||||
/// transactions in the chain. Thus we must allow such scripts to be placed in the transaction.
|
|
||||||
///
|
|
||||||
/// ## 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
|
|
||||||
/// 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
|
|
||||||
/// 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.
|
|
||||||
///
|
|
||||||
/// ## Memory safety
|
|
||||||
///
|
|
||||||
/// The type is `#[repr(transparent)]` for internal purposes only!
|
|
||||||
/// No consumer crate may rely on the representation of the struct!
|
|
||||||
///
|
|
||||||
/// ## References
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// ### Bitcoin Core References
|
|
||||||
///
|
|
||||||
/// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410)
|
|
||||||
///
|
|
||||||
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Script(pub(in crate::blockdata::script) [u8]);
|
|
||||||
|
|
||||||
impl ToOwned for Script {
|
|
||||||
type Owned = ScriptBuf;
|
|
||||||
|
|
||||||
fn to_owned(&self) -> Self::Owned { ScriptBuf(self.0.to_owned()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Script {
|
|
||||||
/// Creates a new empty script.
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> &'static Script { Script::from_bytes(&[]) }
|
|
||||||
|
|
||||||
/// Treat byte slice as `Script`
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> &Script {
|
|
||||||
// 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) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Treat mutable byte slice as `Script`
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Script {
|
|
||||||
// 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) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the script data as a byte slice.
|
|
||||||
#[inline]
|
|
||||||
pub 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 the length in bytes of the script.
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize { self.0.len() }
|
|
||||||
|
|
||||||
/// Returns whether the script is the empty script.
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
|
||||||
|
|
||||||
/// Returns a copy of the script data.
|
|
||||||
#[inline]
|
|
||||||
pub fn to_bytes(&self) -> Vec<u8> { self.0.to_owned() }
|
|
||||||
|
|
||||||
/// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
|
|
||||||
#[must_use = "`self` will be dropped if the result is not used"]
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::internal_macros::define_extension_trait! {
|
crate::internal_macros::define_extension_trait! {
|
||||||
/// Extension functionality for the [`Script`] type.
|
/// Extension functionality for the [`Script`] type.
|
||||||
|
@ -620,29 +506,3 @@ impl DoubleEndedIterator for Bytes<'_> {
|
||||||
|
|
||||||
impl ExactSizeIterator for Bytes<'_> {}
|
impl ExactSizeIterator for Bytes<'_> {}
|
||||||
impl core::iter::FusedIterator for Bytes<'_> {}
|
impl core::iter::FusedIterator for Bytes<'_> {}
|
||||||
|
|
||||||
macro_rules! delegate_index {
|
|
||||||
($($type:ty),* $(,)?) => {
|
|
||||||
$(
|
|
||||||
/// Script subslicing operation - read [slicing safety](#slicing-safety)!
|
|
||||||
impl Index<$type> for Script {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn index(&self, index: $type) -> &Self::Output {
|
|
||||||
Self::from_bytes(&self.0[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate_index!(
|
|
||||||
Range<usize>,
|
|
||||||
RangeFrom<usize>,
|
|
||||||
RangeTo<usize>,
|
|
||||||
RangeFull,
|
|
||||||
RangeInclusive<usize>,
|
|
||||||
RangeToInclusive<usize>,
|
|
||||||
(Bound<usize>, Bound<usize>)
|
|
||||||
);
|
|
||||||
|
|
|
@ -57,36 +57,34 @@ mod tests;
|
||||||
pub mod witness_program;
|
pub mod witness_program;
|
||||||
pub mod witness_version;
|
pub mod witness_version;
|
||||||
|
|
||||||
use alloc::rc::Rc;
|
|
||||||
#[cfg(target_has_atomic = "ptr")]
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use hashes::{hash160, sha256};
|
use hashes::{hash160, sha256};
|
||||||
use internals::impl_to_hex_from_lower_hex;
|
|
||||||
use internals::script::{self, PushDataLenLen};
|
|
||||||
use io::{BufRead, Write};
|
use io::{BufRead, Write};
|
||||||
|
use primitives::opcodes::all::*;
|
||||||
|
use primitives::opcodes::Opcode;
|
||||||
|
|
||||||
use crate::consensus::{encode, Decodable, Encodable};
|
use crate::consensus::{encode, Decodable, Encodable};
|
||||||
use crate::constants::{MAX_REDEEM_SCRIPT_SIZE, MAX_WITNESS_SCRIPT_SIZE};
|
use crate::constants::{MAX_REDEEM_SCRIPT_SIZE, MAX_WITNESS_SCRIPT_SIZE};
|
||||||
use crate::internal_macros::impl_asref_push_bytes;
|
use crate::internal_macros::impl_asref_push_bytes;
|
||||||
use crate::key::WPubkeyHash;
|
use crate::key::WPubkeyHash;
|
||||||
use crate::opcodes::all::*;
|
use crate::prelude::Vec;
|
||||||
use crate::opcodes::{self, Opcode};
|
|
||||||
use crate::prelude::{Borrow, BorrowMut, Box, Cow, DisplayHex, ToOwned, Vec};
|
|
||||||
use crate::OutPoint;
|
use crate::OutPoint;
|
||||||
|
|
||||||
#[rustfmt::skip] // Keep public re-exports separate.
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
borrowed::*,
|
borrowed::ScriptExt,
|
||||||
builder::*,
|
builder::*,
|
||||||
instruction::*,
|
instruction::*,
|
||||||
owned::*,
|
owned::ScriptBufExt,
|
||||||
push_bytes::*,
|
push_bytes::*,
|
||||||
};
|
};
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use primitives::script::*;
|
||||||
|
|
||||||
|
pub(crate) use self::borrowed::ScriptExtPriv;
|
||||||
|
pub(crate) use self::owned::ScriptBufExtPriv;
|
||||||
|
|
||||||
hashes::hash_newtype! {
|
hashes::hash_newtype! {
|
||||||
/// A hash of Bitcoin Script bytecode.
|
/// A hash of Bitcoin Script bytecode.
|
||||||
|
@ -305,382 +303,6 @@ fn opcode_to_verify(opcode: Option<Opcode>) -> Option<Opcode> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// We keep all the `Script` and `ScriptBuf` impls together since its easier to see side-by-side.
|
|
||||||
|
|
||||||
impl From<ScriptBuf> for Box<Script> {
|
|
||||||
fn from(v: ScriptBuf) -> Self { v.into_boxed_script() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ScriptBuf> for Cow<'_, Script> {
|
|
||||||
fn from(value: ScriptBuf) -> Self { Cow::Owned(value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, Script>> for ScriptBuf {
|
|
||||||
fn from(value: Cow<'a, Script>) -> Self {
|
|
||||||
match value {
|
|
||||||
Cow::Owned(owned) => owned,
|
|
||||||
Cow::Borrowed(borrwed) => borrwed.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, Script>> for Box<Script> {
|
|
||||||
fn from(value: Cow<'a, Script>) -> Self {
|
|
||||||
match value {
|
|
||||||
Cow::Owned(owned) => owned.into(),
|
|
||||||
Cow::Borrowed(borrwed) => borrwed.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Script> for Box<Script> {
|
|
||||||
fn from(value: &'a Script) -> Self { value.to_owned().into() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Script> for ScriptBuf {
|
|
||||||
fn from(value: &'a Script) -> Self { value.to_owned() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Script> for Cow<'a, Script> {
|
|
||||||
fn from(value: &'a Script) -> Self { Cow::Borrowed(value) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Note: This will fail to compile on old Rust for targets that don't support atomics
|
|
||||||
#[cfg(target_has_atomic = "ptr")]
|
|
||||||
impl<'a> From<&'a Script> for Arc<Script> {
|
|
||||||
fn from(value: &'a Script) -> Self {
|
|
||||||
let rw: *const [u8] = Arc::into_raw(Arc::from(&value.0));
|
|
||||||
// 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> {
|
|
||||||
fn from(value: &'a Script) -> Self {
|
|
||||||
let rw: *const [u8] = Rc::into_raw(Rc::from(&value.0));
|
|
||||||
// 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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for ScriptBuf {
|
|
||||||
fn from(v: Vec<u8>) -> Self { ScriptBuf(v) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ScriptBuf> for Vec<u8> {
|
|
||||||
fn from(v: ScriptBuf) -> Self { v.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<Script> for Script {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &Script { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<Script> for ScriptBuf {
|
|
||||||
fn as_ref(&self) -> &Script { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for Script {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for ScriptBuf {
|
|
||||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<Script> for Script {
|
|
||||||
fn as_mut(&mut self) -> &mut Script { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<Script> for ScriptBuf {
|
|
||||||
fn as_mut(&mut self) -> &mut Script { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[u8]> for Script {
|
|
||||||
#[inline]
|
|
||||||
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<[u8]> for ScriptBuf {
|
|
||||||
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Script {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str("Script(")?;
|
|
||||||
fmt::Display::fmt(self, f)?;
|
|
||||||
f.write_str(")")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ScriptBuf {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_script(), f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Script {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
// This has to be a macro because it needs to break the loop
|
|
||||||
macro_rules! read_push_data_len {
|
|
||||||
($iter:expr, $size:path, $formatter:expr) => {
|
|
||||||
match script::read_push_data_len($iter, $size) {
|
|
||||||
Ok(n) => n,
|
|
||||||
Err(_) => {
|
|
||||||
$formatter.write_str("<unexpected end>")?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut iter = self.as_bytes().iter();
|
|
||||||
// Was at least one opcode emitted?
|
|
||||||
let mut at_least_one = false;
|
|
||||||
// `iter` needs to be borrowed in `read_push_data_len`, so we have to use `while let` instead
|
|
||||||
// of `for`.
|
|
||||||
while let Some(byte) = iter.next() {
|
|
||||||
let opcode = Opcode::from(*byte);
|
|
||||||
|
|
||||||
let data_len = if let opcodes::Class::PushBytes(n) =
|
|
||||||
opcode.classify(opcodes::ClassifyContext::Legacy)
|
|
||||||
{
|
|
||||||
n as usize
|
|
||||||
} else {
|
|
||||||
match opcode {
|
|
||||||
OP_PUSHDATA1 => {
|
|
||||||
// side effects: may write and break from the loop
|
|
||||||
read_push_data_len!(&mut iter, PushDataLenLen::One, f)
|
|
||||||
}
|
|
||||||
OP_PUSHDATA2 => {
|
|
||||||
// side effects: may write and break from the loop
|
|
||||||
read_push_data_len!(&mut iter, PushDataLenLen::Two, f)
|
|
||||||
}
|
|
||||||
OP_PUSHDATA4 => {
|
|
||||||
// side effects: may write and break from the loop
|
|
||||||
read_push_data_len!(&mut iter, PushDataLenLen::Four, f)
|
|
||||||
}
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if at_least_one {
|
|
||||||
f.write_str(" ")?;
|
|
||||||
} else {
|
|
||||||
at_least_one = true;
|
|
||||||
}
|
|
||||||
// Write the opcode
|
|
||||||
if opcode == OP_PUSHBYTES_0 {
|
|
||||||
f.write_str("OP_0")?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{:?}", opcode)?;
|
|
||||||
}
|
|
||||||
// Write any pushdata
|
|
||||||
if data_len > 0 {
|
|
||||||
f.write_str(" ")?;
|
|
||||||
if data_len <= iter.len() {
|
|
||||||
for ch in iter.by_ref().take(data_len) {
|
|
||||||
write!(f, "{:02x}", ch)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f.write_str("<push past end>")?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ScriptBuf {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_script(), f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::LowerHex for Script {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl_to_hex_from_lower_hex!(Script, |script: &Script| script.len() * 2);
|
|
||||||
|
|
||||||
impl fmt::LowerHex for ScriptBuf {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_script(), f) }
|
|
||||||
}
|
|
||||||
impl_to_hex_from_lower_hex!(ScriptBuf, |script_buf: &ScriptBuf| script_buf.len() * 2);
|
|
||||||
|
|
||||||
impl fmt::UpperHex for Script {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::UpperHex for ScriptBuf {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(self.as_script(), f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for ScriptBuf {
|
|
||||||
type Target = Script;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { Script::from_bytes(&self.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for ScriptBuf {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target { Script::from_bytes_mut(&mut self.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Borrow<Script> for ScriptBuf {
|
|
||||||
fn borrow(&self) -> &Script { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BorrowMut<Script> for ScriptBuf {
|
|
||||||
fn borrow_mut(&mut self) -> &mut Script { self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<ScriptBuf> for Script {
|
|
||||||
fn eq(&self, other: &ScriptBuf) -> bool { self.eq(other.as_script()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Script> for ScriptBuf {
|
|
||||||
fn eq(&self, other: &Script) -> bool { self.as_script().eq(other) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<Script> for ScriptBuf {
|
|
||||||
fn partial_cmp(&self, other: &Script) -> Option<Ordering> {
|
|
||||||
self.as_script().partial_cmp(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd<ScriptBuf> for Script {
|
|
||||||
fn partial_cmp(&self, other: &ScriptBuf) -> Option<Ordering> {
|
|
||||||
self.partial_cmp(other.as_script())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl serde::Serialize for Script {
|
|
||||||
/// User-facing serialization for `Script`.
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
if serializer.is_human_readable() {
|
|
||||||
serializer.collect_str(&format_args!("{:x}", self))
|
|
||||||
} else {
|
|
||||||
serializer.serialize_bytes(self.as_bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Can only deserialize borrowed bytes.
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for &'de Script {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
if deserializer.is_human_readable() {
|
|
||||||
use crate::serde::de::Error;
|
|
||||||
|
|
||||||
return Err(D::Error::custom(
|
|
||||||
"deserialization of `&Script` from human-readable formats is not possible",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Visitor;
|
|
||||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
|
||||||
type Value = &'de Script;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("borrowed bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(Script::from_bytes(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_bytes(Visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl serde::Serialize for ScriptBuf {
|
|
||||||
/// User-facing serialization for `Script`.
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
(**self).serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for ScriptBuf {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
use core::fmt::Formatter;
|
|
||||||
|
|
||||||
use hex::FromHex;
|
|
||||||
|
|
||||||
if deserializer.is_human_readable() {
|
|
||||||
struct Visitor;
|
|
||||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
|
||||||
type Value = ScriptBuf;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a script hex")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
let v = Vec::from_hex(v).map_err(E::custom)?;
|
|
||||||
Ok(ScriptBuf::from(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_str(Visitor)
|
|
||||||
} else {
|
|
||||||
struct BytesVisitor;
|
|
||||||
|
|
||||||
impl<'de> serde::de::Visitor<'de> for BytesVisitor {
|
|
||||||
type Value = ScriptBuf;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a script Vec<u8>")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(ScriptBuf::from(v.to_vec()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(ScriptBuf::from(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deserializer.deserialize_byte_buf(BytesVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Encodable for Script {
|
impl Encodable for Script {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||||
|
|
|
@ -3,93 +3,17 @@
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
#[cfg(feature = "arbitrary")]
|
|
||||||
use arbitrary::{Arbitrary, Unstructured};
|
|
||||||
use hex::FromHex;
|
use hex::FromHex;
|
||||||
use internals::ToU64 as _;
|
use internals::ToU64 as _;
|
||||||
|
|
||||||
use super::{opcode_to_verify, Builder, Instruction, PushBytes, Script, ScriptExtPriv as _};
|
use super::{opcode_to_verify, Builder, Instruction, PushBytes, ScriptExtPriv as _};
|
||||||
use crate::opcodes::all::*;
|
use crate::opcodes::all::*;
|
||||||
use crate::opcodes::{self, Opcode};
|
use crate::opcodes::{self, Opcode};
|
||||||
use crate::prelude::{Box, Vec};
|
use crate::prelude::Vec;
|
||||||
|
|
||||||
/// An owned, growable script.
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
///
|
#[doc(inline)]
|
||||||
/// `ScriptBuf` is the most common script type that has the ownership over the contents of the
|
pub use primitives::script::ScriptBuf;
|
||||||
/// script. It has a close relationship with its borrowed counterpart, [`Script`].
|
|
||||||
///
|
|
||||||
/// Just as other similar types, this implements [`Deref`], so [deref coercions] apply. Also note
|
|
||||||
/// that all the safety/validity restrictions that apply to [`Script`] apply to `ScriptBuf` as well.
|
|
||||||
///
|
|
||||||
/// [deref coercions]: https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion
|
|
||||||
#[derive(Default, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ScriptBuf(pub(in crate::blockdata::script) Vec<u8>);
|
|
||||||
|
|
||||||
impl ScriptBuf {
|
|
||||||
/// Creates a new empty script.
|
|
||||||
#[inline]
|
|
||||||
pub const fn new() -> Self { ScriptBuf(Vec::new()) }
|
|
||||||
|
|
||||||
/// Creates a new empty script with pre-allocated capacity.
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self { ScriptBuf(Vec::with_capacity(capacity)) }
|
|
||||||
|
|
||||||
/// Pre-allocates at least `additional_len` bytes if needed.
|
|
||||||
///
|
|
||||||
/// Reserves capacity for at least `additional_len` more bytes to be inserted in the given
|
|
||||||
/// script. The script may reserve more space to speculatively avoid frequent reallocations.
|
|
||||||
/// After calling `reserve`, capacity will be greater than or equal to
|
|
||||||
/// `self.len() + additional_len`. Does nothing if capacity is already sufficient.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `isize::MAX bytes`.
|
|
||||||
pub fn reserve(&mut self, additional_len: usize) { self.0.reserve(additional_len); }
|
|
||||||
|
|
||||||
/// Pre-allocates exactly `additional_len` bytes if needed.
|
|
||||||
///
|
|
||||||
/// Unlike `reserve`, this will not deliberately over-allocate to speculatively avoid frequent
|
|
||||||
/// allocations. After calling `reserve_exact`, capacity will be greater than or equal to
|
|
||||||
/// `self.len() + additional`. Does nothing if the capacity is already sufficient.
|
|
||||||
///
|
|
||||||
/// Note that the allocator may give the collection more space than it requests. Therefore,
|
|
||||||
/// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`](Self::reserve)
|
|
||||||
/// if future insertions are expected.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `isize::MAX bytes`.
|
|
||||||
pub fn reserve_exact(&mut self, additional_len: usize) { self.0.reserve_exact(additional_len); }
|
|
||||||
|
|
||||||
/// Returns a reference to unsized script.
|
|
||||||
pub fn as_script(&self) -> &Script { Script::from_bytes(&self.0) }
|
|
||||||
|
|
||||||
/// Returns a mutable reference to unsized script.
|
|
||||||
pub fn as_mut_script(&mut self) -> &mut Script { Script::from_bytes_mut(&mut self.0) }
|
|
||||||
|
|
||||||
/// Converts byte vector into script.
|
|
||||||
///
|
|
||||||
/// This method doesn't (re)allocate.
|
|
||||||
pub fn from_bytes(bytes: Vec<u8>) -> Self { ScriptBuf(bytes) }
|
|
||||||
|
|
||||||
/// Converts the script into a byte vector.
|
|
||||||
///
|
|
||||||
/// This method doesn't (re)allocate.
|
|
||||||
pub fn into_bytes(self) -> Vec<u8> { self.0 }
|
|
||||||
|
|
||||||
/// Converts this `ScriptBuf` into a [boxed](Box) [`Script`].
|
|
||||||
///
|
|
||||||
/// This method reallocates if the capacity is greater than length of the script but should not
|
|
||||||
/// when they are equal. If you know beforehand that you need to create a script of exact size
|
|
||||||
/// use [`reserve_exact`](Self::reserve_exact) before adding data to the script so that the
|
|
||||||
/// reallocation can be avoided.
|
|
||||||
#[must_use = "`self` will be dropped if the result is not used"]
|
|
||||||
#[inline]
|
|
||||||
pub fn into_boxed_script(self) -> Box<Script> {
|
|
||||||
// Copied from PathBuf::into_boxed_path
|
|
||||||
let rw = Box::into_raw(self.0.into_boxed_slice()) as *mut Script;
|
|
||||||
unsafe { Box::from_raw(rw) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::internal_macros::define_extension_trait! {
|
crate::internal_macros::define_extension_trait! {
|
||||||
/// Extension functionality for the [`ScriptBuf`] type.
|
/// Extension functionality for the [`ScriptBuf`] type.
|
||||||
|
@ -157,14 +81,6 @@ crate::internal_macros::define_extension_trait! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "arbitrary")]
|
|
||||||
impl<'a> Arbitrary<'a> for ScriptBuf {
|
|
||||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
|
||||||
let v = Vec::<u8>::arbitrary(u)?;
|
|
||||||
Ok(ScriptBuf(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::internal_macros::define_extension_trait! {
|
crate::internal_macros::define_extension_trait! {
|
||||||
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
|
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
|
||||||
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
|
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
use hex_lit::hex;
|
use hex_lit::hex;
|
||||||
|
use primitives::opcodes;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::address::script_pubkey::{
|
use crate::address::script_pubkey::{
|
||||||
|
|
|
@ -30,7 +30,7 @@ units = { package = "bitcoin-units", version = "0.1.0", default-features = false
|
||||||
|
|
||||||
arbitrary = { version = "1", optional = true }
|
arbitrary = { version = "1", optional = true }
|
||||||
ordered = { version = "0.2.0", optional = true }
|
ordered = { version = "0.2.0", optional = true }
|
||||||
serde = { version = "1.0.103", default-features = false, features = ["derive"], optional = true }
|
serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ pub mod locktime;
|
||||||
pub mod opcodes;
|
pub mod opcodes;
|
||||||
pub mod merkle_tree;
|
pub mod merkle_tree;
|
||||||
pub mod pow;
|
pub mod pow;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub mod script;
|
||||||
pub mod sequence;
|
pub mod sequence;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
|
||||||
|
@ -56,5 +58,11 @@ pub use self::{
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
mod prelude {
|
mod prelude {
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub use alloc::string::{String, ToString};
|
pub use alloc::collections::{BTreeMap, BTreeSet, btree_map, BinaryHeap};
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
pub use alloc::{string::{String, ToString}, vec::Vec, boxed::Box, borrow::{Borrow, BorrowMut, Cow, ToOwned}, slice, rc};
|
||||||
|
|
||||||
|
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
|
||||||
|
pub use alloc::sync;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
use core::ops::{
|
||||||
|
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::ScriptBuf;
|
||||||
|
use crate::prelude::{Box, ToOwned, Vec};
|
||||||
|
|
||||||
|
/// Bitcoin script slice.
|
||||||
|
///
|
||||||
|
/// *[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
|
||||||
|
/// pushes.
|
||||||
|
///
|
||||||
|
/// ## 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
|
||||||
|
/// 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
|
||||||
|
/// transactions in the chain. Thus we must allow such scripts to be placed in the transaction.
|
||||||
|
///
|
||||||
|
/// ## 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
|
||||||
|
/// 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
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// ## Memory safety
|
||||||
|
///
|
||||||
|
/// The type is `#[repr(transparent)]` for internal purposes only!
|
||||||
|
/// No consumer crate may rely on the representation of the struct!
|
||||||
|
///
|
||||||
|
/// ## References
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ### Bitcoin Core References
|
||||||
|
///
|
||||||
|
/// * [CScript definition](https://github.com/bitcoin/bitcoin/blob/d492dc1cdaabdc52b0766bf4cba4bd73178325d0/src/script/script.h#L410)
|
||||||
|
///
|
||||||
|
#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Script(pub(in crate::script) [u8]);
|
||||||
|
|
||||||
|
impl ToOwned for Script {
|
||||||
|
type Owned = ScriptBuf;
|
||||||
|
|
||||||
|
fn to_owned(&self) -> Self::Owned { ScriptBuf(self.0.to_owned()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Script {
|
||||||
|
/// Creates a new empty script.
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> &'static Script { Script::from_bytes(&[]) }
|
||||||
|
|
||||||
|
/// Treat byte slice as `Script`
|
||||||
|
#[inline]
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> &Script {
|
||||||
|
// 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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Treat mutable byte slice as `Script`
|
||||||
|
#[inline]
|
||||||
|
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Script {
|
||||||
|
// 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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the script data as a byte slice.
|
||||||
|
#[inline]
|
||||||
|
pub 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 the length in bytes of the script.
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
|
|
||||||
|
/// Returns whether the script is the empty script.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||||
|
|
||||||
|
/// Returns a copy of the script data.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> { self.0.to_owned() }
|
||||||
|
|
||||||
|
/// Converts a [`Box<Script>`](Box) into a [`ScriptBuf`] without copying or allocating.
|
||||||
|
#[must_use = "`self` will be dropped if the result is not used"]
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! delegate_index {
|
||||||
|
($($type:ty),* $(,)?) => {
|
||||||
|
$(
|
||||||
|
/// Script subslicing operation - read [slicing safety](#slicing-safety)!
|
||||||
|
impl Index<$type> for Script {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: $type) -> &Self::Output {
|
||||||
|
Self::from_bytes(&self.0[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_index!(
|
||||||
|
Range<usize>,
|
||||||
|
RangeFrom<usize>,
|
||||||
|
RangeTo<usize>,
|
||||||
|
RangeFull,
|
||||||
|
RangeInclusive<usize>,
|
||||||
|
RangeToInclusive<usize>,
|
||||||
|
(Bound<usize>, Bound<usize>)
|
||||||
|
);
|
|
@ -0,0 +1,406 @@
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
//! Bitcoin scripts.
|
||||||
|
|
||||||
|
/// FIXME: Make this private.
|
||||||
|
mod borrowed;
|
||||||
|
/// FIXME: Make this private.
|
||||||
|
mod owned;
|
||||||
|
|
||||||
|
use core::cmp::Ordering;
|
||||||
|
use core::fmt;
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use hex::DisplayHex;
|
||||||
|
use internals::impl_to_hex_from_lower_hex;
|
||||||
|
use internals::script::{self, PushDataLenLen};
|
||||||
|
|
||||||
|
use crate::opcodes::all::*;
|
||||||
|
use crate::opcodes::{self, Opcode};
|
||||||
|
use crate::prelude::rc::Rc;
|
||||||
|
#[cfg(target_has_atomic = "ptr")]
|
||||||
|
use crate::prelude::sync::Arc;
|
||||||
|
use crate::prelude::{Borrow, BorrowMut, Box, Cow, ToOwned, Vec};
|
||||||
|
|
||||||
|
#[rustfmt::skip] // Keep public re-exports separate.
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use self::{
|
||||||
|
borrowed::*,
|
||||||
|
owned::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We keep all the `Script` and `ScriptBuf` impls together since its easier to see side-by-side.
|
||||||
|
|
||||||
|
impl From<ScriptBuf> for Box<Script> {
|
||||||
|
fn from(v: ScriptBuf) -> Self { v.into_boxed_script() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ScriptBuf> for Cow<'_, Script> {
|
||||||
|
fn from(value: ScriptBuf) -> Self { Cow::Owned(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Cow<'a, Script>> for ScriptBuf {
|
||||||
|
fn from(value: Cow<'a, Script>) -> Self {
|
||||||
|
match value {
|
||||||
|
Cow::Owned(owned) => owned,
|
||||||
|
Cow::Borrowed(borrwed) => borrwed.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<Cow<'a, Script>> for Box<Script> {
|
||||||
|
fn from(value: Cow<'a, Script>) -> Self {
|
||||||
|
match value {
|
||||||
|
Cow::Owned(owned) => owned.into(),
|
||||||
|
Cow::Borrowed(borrwed) => borrwed.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Script> for Box<Script> {
|
||||||
|
fn from(value: &'a Script) -> Self { value.to_owned().into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Script> for ScriptBuf {
|
||||||
|
fn from(value: &'a Script) -> Self { value.to_owned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Script> for Cow<'a, Script> {
|
||||||
|
fn from(value: &'a Script) -> Self { Cow::Borrowed(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Note: This will fail to compile on old Rust for targets that don't support atomics
|
||||||
|
#[cfg(target_has_atomic = "ptr")]
|
||||||
|
impl<'a> From<&'a Script> for Arc<Script> {
|
||||||
|
fn from(value: &'a Script) -> Self {
|
||||||
|
let rw: *const [u8] = Arc::into_raw(Arc::from(&value.0));
|
||||||
|
// 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> {
|
||||||
|
fn from(value: &'a Script) -> Self {
|
||||||
|
let rw: *const [u8] = Rc::into_raw(Rc::from(&value.0));
|
||||||
|
// 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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for ScriptBuf {
|
||||||
|
fn from(v: Vec<u8>) -> Self { ScriptBuf(v) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ScriptBuf> for Vec<u8> {
|
||||||
|
fn from(v: ScriptBuf) -> Self { v.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<Script> for Script {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Script { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<Script> for ScriptBuf {
|
||||||
|
fn as_ref(&self) -> &Script { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for Script {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for ScriptBuf {
|
||||||
|
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<Script> for Script {
|
||||||
|
fn as_mut(&mut self) -> &mut Script { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<Script> for ScriptBuf {
|
||||||
|
fn as_mut(&mut self) -> &mut Script { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<[u8]> for Script {
|
||||||
|
#[inline]
|
||||||
|
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<[u8]> for ScriptBuf {
|
||||||
|
fn as_mut(&mut self) -> &mut [u8] { self.as_mut_bytes() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Script {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str("Script(")?;
|
||||||
|
fmt::Display::fmt(self, f)?;
|
||||||
|
f.write_str(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ScriptBuf {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_script(), f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Script {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
// This has to be a macro because it needs to break the loop
|
||||||
|
macro_rules! read_push_data_len {
|
||||||
|
($iter:expr, $size:path, $formatter:expr) => {
|
||||||
|
match script::read_push_data_len($iter, $size) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_) => {
|
||||||
|
$formatter.write_str("<unexpected end>")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut iter = self.as_bytes().iter();
|
||||||
|
// Was at least one opcode emitted?
|
||||||
|
let mut at_least_one = false;
|
||||||
|
// `iter` needs to be borrowed in `read_push_data_len`, so we have to use `while let` instead
|
||||||
|
// of `for`.
|
||||||
|
while let Some(byte) = iter.next() {
|
||||||
|
let opcode = Opcode::from(*byte);
|
||||||
|
|
||||||
|
let data_len = if let opcodes::Class::PushBytes(n) =
|
||||||
|
opcode.classify(opcodes::ClassifyContext::Legacy)
|
||||||
|
{
|
||||||
|
n as usize
|
||||||
|
} else {
|
||||||
|
match opcode {
|
||||||
|
OP_PUSHDATA1 => {
|
||||||
|
// side effects: may write and break from the loop
|
||||||
|
read_push_data_len!(&mut iter, PushDataLenLen::One, f)
|
||||||
|
}
|
||||||
|
OP_PUSHDATA2 => {
|
||||||
|
// side effects: may write and break from the loop
|
||||||
|
read_push_data_len!(&mut iter, PushDataLenLen::Two, f)
|
||||||
|
}
|
||||||
|
OP_PUSHDATA4 => {
|
||||||
|
// side effects: may write and break from the loop
|
||||||
|
read_push_data_len!(&mut iter, PushDataLenLen::Four, f)
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if at_least_one {
|
||||||
|
f.write_str(" ")?;
|
||||||
|
} else {
|
||||||
|
at_least_one = true;
|
||||||
|
}
|
||||||
|
// Write the opcode
|
||||||
|
if opcode == OP_PUSHBYTES_0 {
|
||||||
|
f.write_str("OP_0")?;
|
||||||
|
} else {
|
||||||
|
write!(f, "{:?}", opcode)?;
|
||||||
|
}
|
||||||
|
// Write any pushdata
|
||||||
|
if data_len > 0 {
|
||||||
|
f.write_str(" ")?;
|
||||||
|
if data_len <= iter.len() {
|
||||||
|
for ch in iter.by_ref().take(data_len) {
|
||||||
|
write!(f, "{:02x}", ch)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.write_str("<push past end>")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ScriptBuf {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_script(), f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::LowerHex for Script {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::LowerHex::fmt(&self.as_bytes().as_hex(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl_to_hex_from_lower_hex!(Script, |script: &Script| script.len() * 2);
|
||||||
|
|
||||||
|
impl fmt::LowerHex for ScriptBuf {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_script(), f) }
|
||||||
|
}
|
||||||
|
impl_to_hex_from_lower_hex!(ScriptBuf, |script_buf: &ScriptBuf| script_buf.len() * 2);
|
||||||
|
|
||||||
|
impl fmt::UpperHex for Script {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::UpperHex::fmt(&self.as_bytes().as_hex(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::UpperHex for ScriptBuf {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::UpperHex::fmt(self.as_script(), f) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ScriptBuf {
|
||||||
|
type Target = Script;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { Script::from_bytes(&self.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ScriptBuf {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target { Script::from_bytes_mut(&mut self.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<Script> for ScriptBuf {
|
||||||
|
fn borrow(&self) -> &Script { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BorrowMut<Script> for ScriptBuf {
|
||||||
|
fn borrow_mut(&mut self) -> &mut Script { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<ScriptBuf> for Script {
|
||||||
|
fn eq(&self, other: &ScriptBuf) -> bool { self.eq(other.as_script()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Script> for ScriptBuf {
|
||||||
|
fn eq(&self, other: &Script) -> bool { self.as_script().eq(other) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<Script> for ScriptBuf {
|
||||||
|
fn partial_cmp(&self, other: &Script) -> Option<Ordering> {
|
||||||
|
self.as_script().partial_cmp(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd<ScriptBuf> for Script {
|
||||||
|
fn partial_cmp(&self, other: &ScriptBuf) -> Option<Ordering> {
|
||||||
|
self.partial_cmp(other.as_script())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for Script {
|
||||||
|
/// User-facing serialization for `Script`.
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
serializer.collect_str(&format_args!("{:x}", self))
|
||||||
|
} else {
|
||||||
|
serializer.serialize_bytes(self.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can only deserialize borrowed bytes.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for &'de Script {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
use crate::serde::de::Error;
|
||||||
|
|
||||||
|
return Err(D::Error::custom(
|
||||||
|
"deserialization of `&Script` from human-readable formats is not possible",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Visitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||||
|
type Value = &'de Script;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("borrowed bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Ok(Script::from_bytes(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_bytes(Visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for ScriptBuf {
|
||||||
|
/// User-facing serialization for `Script`.
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
(**self).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for ScriptBuf {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use core::fmt::Formatter;
|
||||||
|
|
||||||
|
use hex::FromHex;
|
||||||
|
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
struct Visitor;
|
||||||
|
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||||
|
type Value = ScriptBuf;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a script hex")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
let v = Vec::from_hex(v).map_err(E::custom)?;
|
||||||
|
Ok(ScriptBuf::from(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_str(Visitor)
|
||||||
|
} else {
|
||||||
|
struct BytesVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for BytesVisitor {
|
||||||
|
type Value = ScriptBuf;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a script Vec<u8>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Ok(ScriptBuf::from(v.to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Ok(ScriptBuf::from(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deserializer.deserialize_byte_buf(BytesVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
#[cfg(doc)]
|
||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
|
||||||
|
use super::Script;
|
||||||
|
use crate::prelude::{Box, Vec};
|
||||||
|
|
||||||
|
/// An owned, growable script.
|
||||||
|
///
|
||||||
|
/// `ScriptBuf` is the most common script type that has the ownership over the contents of the
|
||||||
|
/// script. It has a close relationship with its borrowed counterpart, [`Script`].
|
||||||
|
///
|
||||||
|
/// Just as other similar types, this implements [`Deref`], so [deref coercions] apply. Also note
|
||||||
|
/// that all the safety/validity restrictions that apply to [`Script`] apply to `ScriptBuf` as well.
|
||||||
|
///
|
||||||
|
/// [deref coercions]: https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion
|
||||||
|
#[derive(Default, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ScriptBuf(pub(in crate::script) Vec<u8>);
|
||||||
|
|
||||||
|
impl ScriptBuf {
|
||||||
|
/// Creates a new empty script.
|
||||||
|
#[inline]
|
||||||
|
pub const fn new() -> Self { ScriptBuf(Vec::new()) }
|
||||||
|
|
||||||
|
/// Creates a new empty script with pre-allocated capacity.
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self { ScriptBuf(Vec::with_capacity(capacity)) }
|
||||||
|
|
||||||
|
/// Pre-allocates at least `additional_len` bytes if needed.
|
||||||
|
///
|
||||||
|
/// Reserves capacity for at least `additional_len` more bytes to be inserted in the given
|
||||||
|
/// script. The script may reserve more space to speculatively avoid frequent reallocations.
|
||||||
|
/// After calling `reserve`, capacity will be greater than or equal to
|
||||||
|
/// `self.len() + additional_len`. Does nothing if capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `isize::MAX bytes`.
|
||||||
|
pub fn reserve(&mut self, additional_len: usize) { self.0.reserve(additional_len); }
|
||||||
|
|
||||||
|
/// Pre-allocates exactly `additional_len` bytes if needed.
|
||||||
|
///
|
||||||
|
/// Unlike `reserve`, this will not deliberately over-allocate to speculatively avoid frequent
|
||||||
|
/// allocations. After calling `reserve_exact`, capacity will be greater than or equal to
|
||||||
|
/// `self.len() + additional`. Does nothing if the capacity is already sufficient.
|
||||||
|
///
|
||||||
|
/// Note that the allocator may give the collection more space than it requests. Therefore,
|
||||||
|
/// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`](Self::reserve)
|
||||||
|
/// if future insertions are expected.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the new capacity exceeds `isize::MAX bytes`.
|
||||||
|
pub fn reserve_exact(&mut self, additional_len: usize) { self.0.reserve_exact(additional_len); }
|
||||||
|
|
||||||
|
/// Returns a reference to unsized script.
|
||||||
|
pub fn as_script(&self) -> &Script { Script::from_bytes(&self.0) }
|
||||||
|
|
||||||
|
/// Returns a mutable reference to unsized script.
|
||||||
|
pub fn as_mut_script(&mut self) -> &mut Script { Script::from_bytes_mut(&mut self.0) }
|
||||||
|
|
||||||
|
/// Converts byte vector into script.
|
||||||
|
///
|
||||||
|
/// This method doesn't (re)allocate.
|
||||||
|
pub fn from_bytes(bytes: Vec<u8>) -> Self { ScriptBuf(bytes) }
|
||||||
|
|
||||||
|
/// Converts the script into a byte vector.
|
||||||
|
///
|
||||||
|
/// This method doesn't (re)allocate.
|
||||||
|
pub fn into_bytes(self) -> Vec<u8> { self.0 }
|
||||||
|
|
||||||
|
/// Converts this `ScriptBuf` into a [boxed](Box) [`Script`].
|
||||||
|
///
|
||||||
|
/// This method reallocates if the capacity is greater than length of the script but should not
|
||||||
|
/// when they are equal. If you know beforehand that you need to create a script of exact size
|
||||||
|
/// use [`reserve_exact`](Self::reserve_exact) before adding data to the script so that the
|
||||||
|
/// reallocation can be avoided.
|
||||||
|
#[must_use = "`self` will be dropped if the result is not used"]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_boxed_script(self) -> Box<Script> {
|
||||||
|
// Copied from PathBuf::into_boxed_path
|
||||||
|
let rw = Box::into_raw(self.0.into_boxed_slice()) as *mut Script;
|
||||||
|
unsafe { Box::from_raw(rw) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "arbitrary")]
|
||||||
|
impl<'a> Arbitrary<'a> for ScriptBuf {
|
||||||
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
|
let v = Vec::<u8>::arbitrary(u)?;
|
||||||
|
Ok(ScriptBuf(v))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue