// Rust Bitcoin Library // Written in 2014 by // Andrew Poelstra // // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to // the public domain worldwide. This software is distributed without // any warranty. // // You should have received a copy of the CC0 Public Domain Dedication // along with this software. // If not, see . // //! Bitcoin scripts. //! //! Scripts define Bitcoin's digital signature scheme: a signature is formed //! from a script (the second half of which is defined by a coin to be spent, //! and the first half provided by the spending transaction), and is valid iff //! the script leaves `TRUE` on the stack after being evaluated. Bitcoin's //! script is a stack-based assembly language similar in spirit to Forth. //! //! This module provides the structures and functions needed to support scripts. //! use prelude::*; use io; use core::{fmt, default::Default}; use core::ops::Index; #[cfg(feature = "serde")] use serde; use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash}; use blockdata::opcodes; use consensus::{encode, Decodable, Encodable}; use hashes::{Hash, hex}; use policy::DUST_RELAY_TX_FEE; #[cfg(feature="bitcoinconsensus")] use bitcoinconsensus; #[cfg(feature="bitcoinconsensus")] use core::convert::From; use OutPoint; use util::key::PublicKey; use util::address::WitnessVersion; use util::taproot::{LeafVersion, TapBranchHash, TapLeafHash}; use secp256k1::{Secp256k1, Verification, XOnlyPublicKey}; use schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey}; /// A Bitcoin script. #[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct Script(Box<[u8]>); impl Index for Script where [u8]: Index, { type Output = <[u8] as Index>::Output; #[inline] fn index(&self, index: I) -> &Self::Output { &self.0[index] } } impl AsRef<[u8]> for Script { fn as_ref(&self) -> &[u8] { &self.0 } } 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::Display for Script { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) } } impl fmt::LowerHex for Script { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for &ch in self.0.iter() { write!(f, "{:02x}", ch)?; } Ok(()) } } impl fmt::UpperHex for Script { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for &ch in self.0.iter() { write!(f, "{:02X}", ch)?; } Ok(()) } } impl hex::FromHex for Script { fn from_byte_iter(iter: I) -> Result where I: Iterator> + ExactSizeIterator + DoubleEndedIterator, { Vec::from_byte_iter(iter).map(|v| Script(Box::<[u8]>::from(v))) } } impl ::core::str::FromStr for Script { type Err = hex::Error; fn from_str(s: &str) -> Result { hex::FromHex::from_hex(s) } } /// An object which can be used to construct a script piece by piece. #[derive(PartialEq, Eq, Debug, Clone)] pub struct Builder(Vec, Option); display_from_debug!(Builder); impl Index for Builder where Vec: Index, { type Output = as Index>::Output; #[inline] fn index(&self, index: I) -> &Self::Output { &self.0[index] } } /// Ways that a script might fail. Not everything is split up as /// much as it could be; patches welcome if more detailed errors /// would help you. #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub enum Error { /// Something did a non-minimal push; for more information see /// `https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Push_operators` NonMinimalPush, /// Some opcode expected a parameter, but it was missing or truncated EarlyEndOfScript, /// Tried to read an array off the stack as a number when it was more than 4 bytes NumericOverflow, /// Error validating the script with bitcoinconsensus library BitcoinConsensus(BitcoinConsensusError), /// Can not find the spent output UnknownSpentOutput(OutPoint), /// Can not serialize the spending transaction SerializationError } /// A [`bitcoinconsensus::Error`] alias. Exists to enable the compiler to ensure `bitcoinconsensus` /// feature gating is correct. #[cfg(feature = "bitcoinconsensus")] #[cfg_attr(docsrs, doc(cfg(feature = "bitcoinconsensus")))] pub type BitcoinConsensusError = bitcoinconsensus::Error; /// Dummy error type used when `bitcoinconsensus` feature is not enabled. #[cfg(not(feature = "bitcoinconsensus"))] #[cfg_attr(docsrs, doc(cfg(not(feature = "bitcoinconsensus"))))] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct BitcoinConsensusError { _uninhabited: Uninhabited, } #[cfg(not(feature = "bitcoinconsensus"))] #[cfg_attr(docsrs, doc(cfg(not(feature = "bitcoinconsensus"))))] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] enum Uninhabited {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let str = match *self { Error::NonMinimalPush => "non-minimal datapush", Error::EarlyEndOfScript => "unexpected end of script", Error::NumericOverflow => "numeric overflow (number on stack larger than 4 bytes)", Error::BitcoinConsensus(ref _n) => "bitcoinconsensus verification failed", Error::UnknownSpentOutput(ref _point) => "unknown spent output Transaction::verify()", Error::SerializationError => "can not serialize the spending transaction in Transaction::verify()", }; f.write_str(str) } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl ::std::error::Error for Error {} // Our internal error proves that we only return these two cases from `read_uint_iter`. // Since it's private we don't bother with trait impls besides From. enum UintError { EarlyEndOfScript, NumericOverflow, } impl From for Error { fn from(error: UintError) -> Self { match error { UintError::EarlyEndOfScript => Error::EarlyEndOfScript, UintError::NumericOverflow => Error::NumericOverflow, } } } #[cfg(feature = "bitcoinconsensus")] #[doc(hidden)] impl From for Error { fn from(err: bitcoinconsensus::Error) -> Error { Error::BitcoinConsensus(err) } } /// Helper to encode an integer in script format fn build_scriptint(n: i64) -> Vec { if n == 0 { return vec![] } let neg = n < 0; let mut abs = if neg { -n } else { n } as usize; let mut v = vec![]; while abs > 0xFF { v.push((abs & 0xFF) as u8); abs >>= 8; } // If the number's value causes the sign bit to be set, we need an extra // byte to get the correct value and correct sign bit if abs & 0x80 != 0 { v.push(abs as u8); v.push(if neg { 0x80u8 } else { 0u8 }); } // Otherwise we just set the sign bit ourselves else { abs |= if neg { 0x80 } else { 0 }; v.push(abs as u8); } v } /// Helper to decode an integer in script format /// Notice that this fails on overflow: the result is the same as in /// bitcoind, that only 4-byte signed-magnitude values may be read as /// numbers. They can be added or subtracted (and a long time ago, /// multiplied and divided), and this may result in numbers which /// can't be written out in 4 bytes or less. This is ok! The number /// just can't be read as a number again. /// This is a bit crazy and subtle, but it makes sense: you can load /// 32-bit numbers and do anything with them, which back when mult/div /// was allowed, could result in up to a 64-bit number. We don't want /// overflow since that's surprising --- and we don't want numbers that /// don't fit in 64 bits (for efficiency on modern processors) so we /// simply say, anything in excess of 32 bits is no longer a number. /// This is basically a ranged type implementation. pub fn read_scriptint(v: &[u8]) -> Result { let len = v.len(); if len == 0 { return Ok(0); } if len > 4 { return Err(Error::NumericOverflow); } let (mut ret, sh) = v.iter() .fold((0, 0), |(acc, sh), n| (acc + ((*n as i64) << sh), sh + 8)); if v[len - 1] & 0x80 != 0 { ret &= (1 << (sh - 1)) - 1; ret = -ret; } Ok(ret) } /// This is like "`read_scriptint` then map 0 to false and everything /// else as true", except that the overflow rules don't apply. #[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, } } /// Read a script-encoded unsigned integer /// /// ## Errors /// /// This function returns an error in these cases: /// /// * `data` is shorter than `size` => `EarlyEndOfScript` /// * `size` is greater than `u16::max_value / 8` (8191) => `NumericOverflow` /// * The number being read overflows `usize` => `NumericOverflow` /// /// Note that this does **not** return an error for `size` between `core::size_of::()` /// and `u16::max_value / 8` if there's no overflow. pub fn read_uint(data: &[u8], size: usize) -> Result { read_uint_iter(&mut data.iter(), size).map_err(Into::into) } // We internally use implementation based on iterator so that it automatically advances as needed // Errors are same as above, just different type. fn read_uint_iter(data: &mut ::core::slice::Iter<'_, u8>, size: usize) -> Result { if data.len() < size { Err(UintError::EarlyEndOfScript) } else if size > usize::from(u16::max_value() / 8) { // Casting to u32 would overflow Err(UintError::NumericOverflow) } else { let mut ret = 0; for (i, item) in data.take(size).enumerate() { ret = usize::from(*item) // Casting is safe because we checked above to not repeat the same check in a loop .checked_shl((i * 8) as u32) .ok_or(UintError::NumericOverflow)? .checked_add(ret) .ok_or(UintError::NumericOverflow)?; } Ok(ret) } } impl Script { /// Creates a new empty script. pub fn new() -> Script { Script(vec![].into_boxed_slice()) } /// Generates P2PK-type of scriptPubkey. pub fn new_p2pk(pubkey: &PublicKey) -> Script { Builder::new() .push_key(pubkey) .push_opcode(opcodes::all::OP_CHECKSIG) .into_script() } /// Generates P2PKH-type of scriptPubkey. pub fn new_p2pkh(pubkey_hash: &PubkeyHash) -> Script { Builder::new() .push_opcode(opcodes::all::OP_DUP) .push_opcode(opcodes::all::OP_HASH160) .push_slice(&pubkey_hash[..]) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_CHECKSIG) .into_script() } /// Generates P2SH-type of scriptPubkey with a given hash of the redeem script. pub fn new_p2sh(script_hash: &ScriptHash) -> Script { Builder::new() .push_opcode(opcodes::all::OP_HASH160) .push_slice(&script_hash[..]) .push_opcode(opcodes::all::OP_EQUAL) .into_script() } /// Generates P2WPKH-type of scriptPubkey. #[deprecated(since = "0.28.0", note = "use Script::new_v0_p2wpkh method instead")] pub fn new_v0_wpkh(pubkey_hash: &WPubkeyHash) -> Script { Script::new_v0_p2wpkh(pubkey_hash) } /// Generates P2WPKH-type of scriptPubkey. pub fn new_v0_p2wpkh(pubkey_hash: &WPubkeyHash) -> Script { Script::new_witness_program(WitnessVersion::V0, &pubkey_hash[..]) } /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script. #[deprecated(since = "0.28.0", note = "use Script::new_v0_p2wsh method instead")] pub fn new_v0_wsh(script_hash: &WScriptHash) -> Script { Script::new_v0_p2wsh(script_hash) } /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script. pub fn new_v0_p2wsh(script_hash: &WScriptHash) -> Script { Script::new_witness_program(WitnessVersion::V0, &script_hash[..]) } /// Generates P2TR for script spending path using an internal public key and some optional /// script tree merkle root. pub fn new_v1_p2tr(secp: &Secp256k1, internal_key: UntweakedPublicKey, merkle_root: Option) -> Script { let (output_key, _) = internal_key.tap_tweak(secp, merkle_root); Script::new_witness_program(WitnessVersion::V1, &output_key.serialize()) } /// Generates P2TR for key spending path for a known [`TweakedPublicKey`]. pub fn new_v1_p2tr_tweaked(output_key: TweakedPublicKey) -> Script { Script::new_witness_program(WitnessVersion::V1, &output_key.serialize()) } /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script. pub fn new_witness_program(version: WitnessVersion, program: &[u8]) -> Script { Builder::new() .push_opcode(version.into()) .push_slice(program) .into_script() } /// Generates OP_RETURN-type of scriptPubkey for the given data. pub fn new_op_return(data: &[u8]) -> Script { Builder::new() .push_opcode(opcodes::all::OP_RETURN) .push_slice(data) .into_script() } /// Returns 160-bit hash of the script. pub fn script_hash(&self) -> ScriptHash { ScriptHash::hash(self.as_bytes()) } /// Returns 256-bit hash of the script for P2WSH outputs. pub fn wscript_hash(&self) -> WScriptHash { WScriptHash::hash(self.as_bytes()) } /// Returns the length in bytes of the script. pub fn len(&self) -> usize { self.0.len() } /// Returns whether the script is the empty script. pub fn is_empty(&self) -> bool { self.0.is_empty() } /// Returns the script data as a byte slice. pub fn as_bytes(&self) -> &[u8] { &*self.0 } /// Returns a copy of the script data. pub fn to_bytes(&self) -> Vec { self.0.clone().into_vec() } /// Converts the script into a byte vector. pub fn into_bytes(self) -> Vec { self.0.into_vec() } /// Computes the P2SH output corresponding to this redeem script. pub fn to_p2sh(&self) -> Script { Script::new_p2sh(&self.script_hash()) } /// Returns the script code used for spending a P2WPKH output if this script is a script pubkey /// for a P2WPKH output. The `scriptCode` is described in [BIP143]. /// /// [BIP143]: pub fn p2wpkh_script_code(&self) -> Option