mod borrowed;
mod builder;
mod instruction;
mod owned;
mod push_bytes;
#[cfg(test)]
mod tests;
pub mod witness_program;
pub mod witness_version;
use alloc::rc::Rc;
#[cfg(any(not(rust_v_1_60), 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 io::{Read, Write};
use crate::blockdata::opcodes::all::*;
use crate::blockdata::opcodes::{self, Opcode};
use crate::consensus::{encode, Decodable, Encodable};
use crate::internal_macros::impl_asref_push_bytes;
use crate::prelude::*;
use crate::OutPoint;
#[rustfmt::skip] #[doc(inline)]
pub use self::{
borrowed::*,
builder::*,
instruction::*,
owned::*,
push_bytes::*,
};
hashes::hash_newtype! {
pub struct ScriptHash(hash160::Hash);
pub struct WScriptHash(sha256::Hash);
}
impl_asref_push_bytes!(ScriptHash, WScriptHash);
impl From<ScriptBuf> for ScriptHash {
fn from(script: ScriptBuf) -> ScriptHash { script.script_hash() }
}
impl From<&ScriptBuf> for ScriptHash {
fn from(script: &ScriptBuf) -> ScriptHash { script.script_hash() }
}
impl From<&Script> for ScriptHash {
fn from(script: &Script) -> ScriptHash { script.script_hash() }
}
impl From<ScriptBuf> for WScriptHash {
fn from(script: ScriptBuf) -> WScriptHash { script.wscript_hash() }
}
impl From<&ScriptBuf> for WScriptHash {
fn from(script: &ScriptBuf) -> WScriptHash { script.wscript_hash() }
}
impl From<&Script> for WScriptHash {
fn from(script: &Script) -> WScriptHash { script.wscript_hash() }
}
pub fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
let mut len = 0;
if n == 0 {
return len;
}
let neg = n < 0;
let mut abs = n.unsigned_abs();
while abs > 0xFF {
out[len] = (abs & 0xFF) as u8;
len += 1;
abs >>= 8;
}
if abs & 0x80 != 0 {
out[len] = abs as u8;
len += 1;
out[len] = if neg { 0x80u8 } else { 0u8 };
len += 1;
}
else {
abs |= if neg { 0x80 } else { 0 };
out[len] = abs as u8;
len += 1;
}
len
}
pub fn read_scriptint(v: &[u8]) -> Result<i64, Error> {
let last = match v.last() {
Some(last) => last,
None => return Ok(0),
};
if v.len() > 4 {
return Err(Error::NumericOverflow);
}
if (*last & 0x7f) == 0 {
if v.len() <= 1 || (v[v.len() - 2] & 0x80) == 0 {
return Err(Error::NonMinimalPush);
}
}
Ok(scriptint_parse(v))
}
pub fn read_scriptint_non_minimal(v: &[u8]) -> Result<i64, Error> {
if v.is_empty() {
return Ok(0);
}
if v.len() > 4 {
return Err(Error::NumericOverflow);
}
Ok(scriptint_parse(v))
}
fn scriptint_parse(v: &[u8]) -> i64 {
let (mut ret, sh) = v.iter().fold((0, 0), |(acc, sh), n| (acc + ((*n as i64) << sh), sh + 8));
if v[v.len() - 1] & 0x80 != 0 {
ret &= (1 << (sh - 1)) - 1;
ret = -ret;
}
ret
}
#[inline]
pub fn read_scriptbool(v: &[u8]) -> bool {
match v.split_last() {
Some((last, rest)) => !((last & !0x80 == 0x00) && rest.iter().all(|&b| b == 0)),
None => false,
}
}
fn read_uint_iter(data: &mut core::slice::Iter<'_, u8>, size: usize) -> Result<usize, UintError> {
if data.len() < size {
Err(UintError::EarlyEndOfScript)
} else if size > usize::from(u16::MAX / 8) {
Err(UintError::NumericOverflow)
} else {
let mut ret = 0;
for (i, item) in data.take(size).enumerate() {
ret = usize::from(*item)
.checked_shl((i * 8) as u32)
.ok_or(UintError::NumericOverflow)?
.checked_add(ret)
.ok_or(UintError::NumericOverflow)?;
}
Ok(ret)
}
}
fn opcode_to_verify(opcode: Option<Opcode>) -> Option<Opcode> {
opcode.and_then(|opcode| match opcode {
OP_EQUAL => Some(OP_EQUALVERIFY),
OP_NUMEQUAL => Some(OP_NUMEQUALVERIFY),
OP_CHECKSIG => Some(OP_CHECKSIGVERIFY),
OP_CHECKMULTISIG => Some(OP_CHECKMULTISIGVERIFY),
_ => None,
})
}
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) }
}
#[cfg(any(not(rust_v_1_60), 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));
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));
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(")?;
self.fmt_asm(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 {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt_asm(f) }
}
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 fmt::LowerHex for ScriptBuf {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_script(), f) }
}
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 {
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())
}
}
}
#[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 {
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> {
crate::consensus::encode::consensus_encode_with_size(&self.0, w)
}
}
impl Encodable for ScriptBuf {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
self.0.consensus_encode(w)
}
}
impl Decodable for ScriptBuf {
#[inline]
fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
r: &mut R,
) -> Result<Self, encode::Error> {
Ok(ScriptBuf(Decodable::consensus_decode_from_finite_reader(r)?))
}
}
pub(super) fn bytes_to_asm_fmt(script: &[u8], f: &mut dyn fmt::Write) -> fmt::Result {
macro_rules! read_push_data_len {
($iter:expr, $len:literal, $formatter:expr) => {
match read_uint_iter($iter, $len) {
Ok(n) => {
n
},
Err(UintError::EarlyEndOfScript) => {
$formatter.write_str("<unexpected end>")?;
break;
}
Err(UintError::NumericOverflow) => {
$formatter.write_str("<push past end>")?;
break;
}
}
}
}
let mut iter = script.iter();
let mut at_least_one = false;
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 => {
read_push_data_len!(&mut iter, 1, f)
}
OP_PUSHDATA2 => {
read_push_data_len!(&mut iter, 2, f)
}
OP_PUSHDATA4 => {
read_push_data_len!(&mut iter, 4, f)
}
_ => 0,
}
};
if at_least_one {
f.write_str(" ")?;
} else {
at_least_one = true;
}
if opcode == OP_PUSHBYTES_0 {
f.write_str("OP_0")?;
} else {
write!(f, "{:?}", opcode)?;
}
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(())
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
NonMinimalPush,
EarlyEndOfScript,
NumericOverflow,
UnknownSpentOutput(OutPoint),
Serialization,
}
internals::impl_from_infallible!(Error);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match *self {
NonMinimalPush => f.write_str("non-minimal datapush"),
EarlyEndOfScript => f.write_str("unexpected end of script"),
NumericOverflow =>
f.write_str("numeric overflow (number on stack larger than 4 bytes)"),
UnknownSpentOutput(ref point) => write!(f, "unknown spent output: {}", point),
Serialization =>
f.write_str("can not serialize the spending transaction in Transaction::verify()"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use Error::*;
match *self {
NonMinimalPush
| EarlyEndOfScript
| NumericOverflow
| UnknownSpentOutput(_)
| Serialization => None,
}
}
}
enum UintError {
EarlyEndOfScript,
NumericOverflow,
}
internals::impl_from_infallible!(UintError);
impl From<UintError> for Error {
fn from(error: UintError) -> Self {
match error {
UintError::EarlyEndOfScript => Error::EarlyEndOfScript,
UintError::NumericOverflow => Error::NumericOverflow,
}
}
}