use core::fmt;
use core::ops::{
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
};
use hashes::Hash;
use secp256k1::{Secp256k1, Verification};
use super::PushBytes;
use crate::blockdata::fee_rate::FeeRate;
use crate::blockdata::opcodes::all::*;
use crate::blockdata::opcodes::{self, Opcode};
use crate::blockdata::script::witness_version::WitnessVersion;
use crate::blockdata::script::{
bytes_to_asm_fmt, Builder, Instruction, InstructionIndices, Instructions, ScriptBuf,
ScriptHash, WScriptHash,
};
use crate::consensus::Encodable;
use crate::key::{PublicKey, UntweakedPublicKey, WPubkeyHash};
use crate::policy::DUST_RELAY_TX_FEE;
use crate::prelude::*;
use crate::taproot::{LeafVersion, TapLeafHash, TapNodeHash};
#[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 {
#[inline]
pub fn new() -> &'static Script { Script::from_bytes(&[]) }
#[inline]
pub fn from_bytes(bytes: &[u8]) -> &Script {
unsafe { &*(bytes as *const [u8] as *const Script) }
}
#[inline]
pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Script {
unsafe { &mut *(bytes as *mut [u8] as *mut Script) }
}
#[inline]
pub fn as_bytes(&self) -> &[u8] { &self.0 }
#[inline]
pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.0 }
pub fn builder() -> Builder { Builder::new() }
#[inline]
pub fn script_hash(&self) -> ScriptHash { ScriptHash::hash(self.as_bytes()) }
#[inline]
pub fn wscript_hash(&self) -> WScriptHash { WScriptHash::hash(self.as_bytes()) }
#[inline]
pub fn tapscript_leaf_hash(&self) -> TapLeafHash {
TapLeafHash::from_script(self, LeafVersion::TapScript)
}
#[inline]
pub fn len(&self) -> usize { self.0.len() }
#[inline]
pub fn is_empty(&self) -> bool { self.0.is_empty() }
#[inline]
pub fn to_bytes(&self) -> Vec<u8> { self.0.to_owned() }
#[inline]
pub fn bytes(&self) -> Bytes<'_> { Bytes(self.as_bytes().iter().copied()) }
#[inline]
pub fn to_p2wsh(&self) -> ScriptBuf { ScriptBuf::new_p2wsh(&self.wscript_hash()) }
#[inline]
pub fn to_p2tr<C: Verification>(
&self,
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
) -> ScriptBuf {
let leaf_hash = self.tapscript_leaf_hash();
let merkle_root = TapNodeHash::from(leaf_hash);
ScriptBuf::new_p2tr(secp, internal_key, Some(merkle_root))
}
#[inline]
pub fn witness_version(&self) -> Option<WitnessVersion> {
let script_len = self.0.len();
if !(4..=42).contains(&script_len) {
return None;
}
let ver_opcode = Opcode::from(self.0[0]); let push_opbyte = self.0[1];
if push_opbyte < OP_PUSHBYTES_2.to_u8() || push_opbyte > OP_PUSHBYTES_40.to_u8() {
return None;
}
if script_len - 2 != push_opbyte as usize {
return None;
}
WitnessVersion::try_from(ver_opcode).ok()
}
#[inline]
pub fn is_p2sh(&self) -> bool {
self.0.len() == 23
&& self.0[0] == OP_HASH160.to_u8()
&& self.0[1] == OP_PUSHBYTES_20.to_u8()
&& self.0[22] == OP_EQUAL.to_u8()
}
#[inline]
pub fn is_p2pkh(&self) -> bool {
self.0.len() == 25
&& self.0[0] == OP_DUP.to_u8()
&& self.0[1] == OP_HASH160.to_u8()
&& self.0[2] == OP_PUSHBYTES_20.to_u8()
&& self.0[23] == OP_EQUALVERIFY.to_u8()
&& self.0[24] == OP_CHECKSIG.to_u8()
}
#[inline]
pub fn is_push_only(&self) -> bool {
for inst in self.instructions() {
match inst {
Err(_) => return false,
Ok(Instruction::PushBytes(_)) => {}
Ok(Instruction::Op(op)) if op.to_u8() <= 0x60 => {}
Ok(Instruction::Op(_)) => return false,
}
}
true
}
#[inline]
pub fn is_p2pk(&self) -> bool { self.p2pk_pubkey_bytes().is_some() }
#[inline]
pub fn p2pk_public_key(&self) -> Option<PublicKey> {
PublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
}
#[inline]
pub(in crate::blockdata::script) fn p2pk_pubkey_bytes(&self) -> Option<&[u8]> {
match self.len() {
67 if self.0[0] == OP_PUSHBYTES_65.to_u8() && self.0[66] == OP_CHECKSIG.to_u8() =>
Some(&self.0[1..66]),
35 if self.0[0] == OP_PUSHBYTES_33.to_u8() && self.0[34] == OP_CHECKSIG.to_u8() =>
Some(&self.0[1..34]),
_ => None,
}
}
#[inline]
pub fn is_multisig(&self) -> bool {
let required_sigs;
let mut instructions = self.instructions();
if let Some(Ok(Instruction::Op(op))) = instructions.next() {
if let Some(pushnum) = op.decode_pushnum() {
required_sigs = pushnum;
} else {
return false;
}
} else {
return false;
}
let mut num_pubkeys: u8 = 0;
while let Some(Ok(instruction)) = instructions.next() {
match instruction {
Instruction::PushBytes(_) => {
num_pubkeys += 1;
}
Instruction::Op(op) => {
if let Some(pushnum) = op.decode_pushnum() {
if pushnum != num_pubkeys {
return false;
}
}
break;
}
}
}
if required_sigs > num_pubkeys {
return false;
}
if let Some(Ok(Instruction::Op(op))) = instructions.next() {
if op != OP_CHECKMULTISIG {
return false;
}
} else {
return false;
}
instructions.next().is_none()
}
#[inline]
pub fn is_witness_program(&self) -> bool { self.witness_version().is_some() }
#[inline]
pub fn is_p2wsh(&self) -> bool {
self.0.len() == 34
&& self.witness_version() == Some(WitnessVersion::V0)
&& self.0[1] == OP_PUSHBYTES_32.to_u8()
}
#[inline]
pub fn is_p2wpkh(&self) -> bool {
self.0.len() == 22
&& self.witness_version() == Some(WitnessVersion::V0)
&& self.0[1] == OP_PUSHBYTES_20.to_u8()
}
#[inline]
pub fn is_p2tr(&self) -> bool {
self.0.len() == 34
&& self.witness_version() == Some(WitnessVersion::V1)
&& self.0[1] == OP_PUSHBYTES_32.to_u8()
}
#[inline]
pub fn is_op_return(&self) -> bool {
match self.0.first() {
Some(b) => *b == OP_RETURN.to_u8(),
None => false,
}
}
#[deprecated(
since = "0.32.0",
note = "The method has potentially confusing semantics and is going to be removed, you might want `is_op_return`"
)]
#[inline]
pub fn is_provably_unspendable(&self) -> bool {
use crate::blockdata::opcodes::Class::{IllegalOp, ReturnOp};
match self.0.first() {
Some(b) => {
let first = Opcode::from(*b);
let class = first.classify(opcodes::ClassifyContext::Legacy);
class == ReturnOp || class == IllegalOp
}
None => false,
}
}
pub fn to_p2sh(&self) -> ScriptBuf { ScriptBuf::new_p2sh(&self.script_hash()) }
pub fn p2wpkh_script_code(&self) -> Option<ScriptBuf> {
if self.is_p2wpkh() {
let bytes = &self.0[2..];
let wpkh = WPubkeyHash::from_slice(bytes).expect("length checked in is_p2wpkh()");
Some(ScriptBuf::p2wpkh_script_code(wpkh))
} else {
None
}
}
pub fn redeem_script(&self) -> Option<&Script> {
if self.instructions().any(|i| i.is_err() || i.unwrap().push_bytes().is_none()) {
return None;
}
if let Some(Ok(Instruction::PushBytes(b))) = self.instructions().last() {
Some(Script::from_bytes(b.as_bytes()))
} else {
None
}
}
#[deprecated(since = "0.32.0", note = "use minimal_non_dust and friends")]
pub fn dust_value(&self) -> crate::Amount { self.minimal_non_dust() }
pub fn minimal_non_dust(&self) -> crate::Amount {
self.minimal_non_dust_inner(DUST_RELAY_TX_FEE.into())
}
pub fn minimal_non_dust_custom(&self, dust_relay_fee: FeeRate) -> crate::Amount {
self.minimal_non_dust_inner(dust_relay_fee.to_sat_per_kwu() * 4)
}
fn minimal_non_dust_inner(&self, dust_relay_fee: u64) -> crate::Amount {
let sats = dust_relay_fee
.checked_mul(if self.is_op_return() {
0
} else if self.is_witness_program() {
32 + 4 + 1 + (107 / 4) + 4 + 8 + self.consensus_encode(&mut sink()).expect("sinks don't error") as u64 } else {
32 + 4 + 1 + 107 + 4 + 8 + self.consensus_encode(&mut sink()).expect("sinks don't error") as u64 })
.expect("dust_relay_fee or script length should not be absurdly large")
/ 1000;
crate::Amount::from_sat(sats)
}
pub fn count_sigops(&self) -> usize { self.count_sigops_internal(true) }
pub fn count_sigops_legacy(&self) -> usize { self.count_sigops_internal(false) }
fn count_sigops_internal(&self, accurate: bool) -> usize {
let mut n = 0;
let mut pushnum_cache = None;
for inst in self.instructions() {
match inst {
Ok(Instruction::Op(opcode)) => {
match opcode {
OP_CHECKSIG | OP_CHECKSIGVERIFY => {
n += 1;
}
OP_CHECKMULTISIG | OP_CHECKMULTISIGVERIFY => {
match (accurate, pushnum_cache) {
(true, Some(pushnum)) => {
n += usize::from(pushnum);
}
_ => {
n += 20;
}
}
}
_ => {
pushnum_cache = opcode.decode_pushnum();
}
}
}
Ok(Instruction::PushBytes(_)) => {
pushnum_cache = None;
}
Err(_) => break,
}
}
n
}
#[inline]
pub fn instructions(&self) -> Instructions<'_> {
Instructions { data: self.0.iter(), enforce_minimal: false }
}
#[inline]
pub fn instructions_minimal(&self) -> Instructions<'_> {
Instructions { data: self.0.iter(), enforce_minimal: true }
}
#[inline]
pub fn instruction_indices(&self) -> InstructionIndices<'_> {
InstructionIndices::from_instructions(self.instructions())
}
#[inline]
pub fn instruction_indices_minimal(&self) -> InstructionIndices<'_> {
InstructionIndices::from_instructions(self.instructions_minimal())
}
pub fn fmt_asm(&self, f: &mut dyn fmt::Write) -> fmt::Result {
bytes_to_asm_fmt(self.as_ref(), f)
}
pub fn to_asm_string(&self) -> String {
let mut buf = String::new();
self.fmt_asm(&mut buf).unwrap();
buf
}
pub fn to_hex_string(&self) -> String { self.as_bytes().to_lower_hex_string() }
pub fn first_opcode(&self) -> Option<Opcode> {
self.as_bytes().first().copied().map(From::from)
}
pub(in crate::blockdata::script) fn last_opcode(&self) -> Option<Opcode> {
match self.instructions().last() {
Some(Ok(Instruction::Op(op))) => Some(op),
_ => None,
}
}
pub(crate) fn last_pushdata(&self) -> Option<&PushBytes> {
match self.instructions().last() {
Some(Ok(Instruction::PushBytes(bytes))) => Some(bytes),
_ => None,
}
}
#[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];
let inner = unsafe { Box::from_raw(rw) };
ScriptBuf(Vec::from(inner))
}
}
pub struct Bytes<'a>(core::iter::Copied<core::slice::Iter<'a, u8>>);
impl Iterator for Bytes<'_> {
type Item = u8;
#[inline]
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> { self.0.nth(n) }
}
impl DoubleEndedIterator for Bytes<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> { self.0.next_back() }
#[inline]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> { self.0.nth_back(n) }
}
impl ExactSizeIterator for Bytes<'_> {}
impl core::iter::FusedIterator for Bytes<'_> {}
macro_rules! delegate_index {
($($type:ty),* $(,)?) => {
$(
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>)
);
#[cfg(test)]
mod tests {
use super::*;
use crate::blockdata::script::witness_program::WitnessProgram;
#[test]
fn shortest_witness_program() {
let bytes = [0x00; 2]; let version = WitnessVersion::V15;
let p = WitnessProgram::new(version, &bytes).expect("failed to create witness program");
let script = ScriptBuf::new_witness_program(&p);
assert_eq!(script.witness_version(), Some(version));
}
#[test]
fn longest_witness_program() {
let bytes = [0x00; 40]; let version = WitnessVersion::V16;
let p = WitnessProgram::new(version, &bytes).expect("failed to create witness program");
let script = ScriptBuf::new_witness_program(&p);
assert_eq!(script.witness_version(), Some(version));
}
}