Move script types to primitives

Move the `Script` and `ScriptBuf` types to `primitives`, nothing else.
This commit is contained in:
Tobin C. Harding 2024-10-01 13:31:18 +10:00
parent ec4635904b
commit d649c06238
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
9 changed files with 682 additions and 624 deletions

View File

@ -1,139 +1,25 @@
// SPDX-License-Identifier: CC0-1.0
use core::fmt;
use core::ops::{
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
use internals::ToU64 as _;
use super::witness_version::WitnessVersion;
use super::{
Builder, Instruction, InstructionIndices, Instructions, PushBytes, RedeemScriptSizeError,
ScriptBuf, ScriptHash, WScriptHash, WitnessScriptSizeError,
ScriptHash, WScriptHash, WitnessScriptSizeError,
};
use crate::consensus::Encodable;
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
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::FeeRate;
/// 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::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))
}
}
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
pub use primitives::script::Script;
crate::internal_macros::define_extension_trait! {
/// Extension functionality for the [`Script`] type.
@ -620,29 +506,3 @@ impl DoubleEndedIterator for Bytes<'_> {
impl ExactSizeIterator 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>)
);

View File

@ -57,36 +57,34 @@ mod tests;
pub mod witness_program;
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::ops::{Deref, DerefMut};
use hashes::{hash160, sha256};
use internals::impl_to_hex_from_lower_hex;
use internals::script::{self, PushDataLenLen};
use io::{BufRead, Write};
use primitives::opcodes::all::*;
use primitives::opcodes::Opcode;
use crate::consensus::{encode, Decodable, Encodable};
use crate::constants::{MAX_REDEEM_SCRIPT_SIZE, MAX_WITNESS_SCRIPT_SIZE};
use crate::internal_macros::impl_asref_push_bytes;
use crate::key::WPubkeyHash;
use crate::opcodes::all::*;
use crate::opcodes::{self, Opcode};
use crate::prelude::{Borrow, BorrowMut, Box, Cow, DisplayHex, ToOwned, Vec};
use crate::prelude::Vec;
use crate::OutPoint;
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
pub use self::{
borrowed::*,
borrowed::ScriptExt,
builder::*,
instruction::*,
owned::*,
owned::ScriptBufExt,
push_bytes::*,
};
#[doc(inline)]
pub use primitives::script::*;
pub(crate) use self::borrowed::ScriptExtPriv;
pub(crate) use self::owned::ScriptBufExtPriv;
hashes::hash_newtype! {
/// 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 {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {

View File

@ -3,93 +3,17 @@
#[cfg(doc)]
use core::ops::Deref;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use hex::FromHex;
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::{self, Opcode};
use crate::prelude::{Box, Vec};
use crate::prelude::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::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) }
}
}
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
pub use primitives::script::ScriptBuf;
crate::internal_macros::define_extension_trait! {
/// 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! {
pub(crate) trait ScriptBufExtPriv impl for ScriptBuf {
/// Pretends to convert `&mut ScriptBuf` to `&mut Vec<u8>` so that it can be modified.

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: CC0-1.0
use hex_lit::hex;
use primitives::opcodes;
use super::*;
use crate::address::script_pubkey::{

View File

@ -30,7 +30,7 @@ units = { package = "bitcoin-units", version = "0.1.0", default-features = false
arbitrary = { version = "1", 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]

View File

@ -35,6 +35,8 @@ pub mod locktime;
pub mod opcodes;
pub mod merkle_tree;
pub mod pow;
#[cfg(feature = "alloc")]
pub mod script;
pub mod sequence;
pub mod transaction;
@ -56,5 +58,11 @@ pub use self::{
#[allow(unused_imports)]
mod prelude {
#[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;
}

View File

@ -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>)
);

View File

@ -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)
}
}
}

View File

@ -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))
}
}