use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use encoding::{ByteVecDecoder, DecoderStatus};
use super::{Script, ScriptBufDecoderError, P2A_PROGRAM};
use crate::opcodes::all::{OP_1, OP_1NEGATE, OP_EQUAL, OP_HASH160, OP_RETURN};
use crate::opcodes::{self, Opcode};
use crate::prelude::{Box, Vec};
use crate::script::{Builder, PushBytes, ScriptHash, ScriptHashableTag, WScriptHash};
use crate::witness_version::WitnessVersion;
use crate::ScriptPubKeyBuf;
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct ScriptBuf<T>(PhantomData<T>, Vec<u8>);
impl<T> ScriptBuf<T> {
#[inline]
pub const fn new() -> Self { Self::from_bytes(Vec::new()) }
#[inline]
pub const fn from_bytes(bytes: Vec<u8>) -> Self { Self(PhantomData, bytes) }
#[cfg(feature = "hex")]
pub fn from_hex_prefixed(
s: &str,
) -> Result<Self, encoding::FromHexError<ScriptBufDecoderError>> {
encoding::decode_from_hex(s)
}
#[cfg(feature = "hex")]
pub fn from_hex_no_length_prefix(s: &str) -> Result<Self, hex::DecodeVariableLengthBytesError> {
let v = hex::decode_to_vec(s)?;
Ok(Self::from_bytes(v))
}
#[inline]
pub fn as_script(&self) -> &Script<T> { Script::from_bytes(&self.1) }
#[inline]
pub fn as_mut_script(&mut self) -> &mut Script<T> { Script::from_bytes_mut(&mut self.1) }
#[inline]
pub fn into_bytes(self) -> Vec<u8> { self.1 }
#[must_use]
#[inline]
pub fn into_boxed_script(self) -> Box<Script<T>> {
Script::from_boxed_bytes(self.into_bytes().into_boxed_slice())
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self { Self::from_bytes(Vec::with_capacity(capacity)) }
#[inline]
pub fn reserve(&mut self, additional_len: usize) { self.1.reserve(additional_len); }
#[inline]
pub fn reserve_exact(&mut self, additional_len: usize) { self.1.reserve_exact(additional_len); }
#[inline]
pub fn capacity(&self) -> usize { self.1.capacity() }
#[cfg(feature = "hex")]
#[inline]
#[deprecated(since = "1.0.0-rc.0", note = "use `format!(\"{var:x}\")` instead")]
pub fn to_hex(&self) -> alloc::string::String { alloc::format!("{:x}", self) }
pub fn builder() -> Builder<T> { Builder::new() }
pub fn push_opcode(&mut self, data: Opcode) { self.as_byte_vec().push(data.to_u8()); }
pub fn push_slice<D: AsRef<PushBytes>>(&mut self, data: D) {
let bytes = data.as_ref().as_bytes();
if bytes.len() == 1 {
match bytes[0] {
0x81 => {
self.push_opcode(OP_1NEGATE);
}
1..=16 => {
self.push_opcode(Opcode::from(bytes[0] + (OP_1.to_u8() - 1)));
}
_ => {
self.push_slice_non_minimal(data);
}
}
} else {
self.push_slice_non_minimal(data);
}
}
pub fn push_slice_non_minimal<D: AsRef<PushBytes>>(&mut self, data: D) {
let data = data.as_ref();
self.reserve(Self::reserved_len_for_slice(data.len()));
self.push_slice_no_opt(data);
}
fn reserved_len_for_slice(len: usize) -> usize {
len + match len {
0..=0x4b => 1,
0x4c..=0xff => 2,
0x100..=0xffff => 3,
_ => 5,
}
}
fn as_byte_vec(&mut self) -> ScriptBufAsVec<'_, T> {
let vec = core::mem::take(self).into_bytes();
ScriptBufAsVec(self, vec)
}
fn push_slice_no_opt(&mut self, data: &PushBytes) {
let mut this = self.as_byte_vec();
match data.len() as u64 {
n if n < opcodes::OP_PUSHDATA1.into() => {
this.push(n as u8);
}
n if n < 0x100 => {
this.push(opcodes::OP_PUSHDATA1);
this.push(n as u8);
}
n if n < 0x10000 => {
this.push(opcodes::OP_PUSHDATA2);
this.push((n % 0x100) as u8);
this.push((n / 0x100) as u8);
}
n => {
this.push(opcodes::OP_PUSHDATA4);
this.push((n % 0x100) as u8);
this.push(((n / 0x100) % 0x100) as u8);
this.push(((n / 0x10000) % 0x100) as u8);
this.push((n / 0x0100_0000) as u8);
}
}
this.extend_from_slice(data.as_bytes());
}
}
impl ScriptPubKeyBuf {
pub fn new_op_return<T: AsRef<PushBytes>>(data: T) -> Self {
Builder::new().push_opcode(OP_RETURN).push_slice(data).into_script()
}
pub fn new_p2sh(script_hash: ScriptHash) -> Self {
Builder::new()
.push_opcode(OP_HASH160)
.push_slice(script_hash)
.push_opcode(OP_EQUAL)
.into_script()
}
pub fn new_p2a() -> Self {
super::new_witness_program_unchecked(WitnessVersion::V1, P2A_PROGRAM)
}
}
impl<T: ScriptHashableTag> ScriptBuf<T> {
pub fn new_p2wsh(script_hash: WScriptHash) -> Self {
super::new_witness_program_unchecked(WitnessVersion::V0, script_hash)
}
}
impl<T> Default for ScriptBuf<T> {
fn default() -> Self { Self(PhantomData, Vec::new()) }
}
impl<T> Deref for ScriptBuf<T> {
type Target = Script<T>;
#[inline]
fn deref(&self) -> &Self::Target { self.as_script() }
}
impl<T> DerefMut for ScriptBuf<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut_script() }
}
impl<T> encoding::Decode for ScriptBuf<T> {
type Decoder = ScriptBufDecoder<T>;
}
#[derive(Debug, Clone)]
pub struct ScriptBufDecoder<T>(ByteVecDecoder, PhantomData<T>);
impl<T> ScriptBufDecoder<T> {
pub const fn new() -> Self { Self(ByteVecDecoder::new(), PhantomData) }
}
impl<T> Default for ScriptBufDecoder<T> {
fn default() -> Self { Self::new() }
}
impl<T> encoding::Decoder for ScriptBufDecoder<T> {
type Output = ScriptBuf<T>;
type Error = ScriptBufDecoderError;
#[inline]
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<DecoderStatus, Self::Error> {
self.0.push_bytes(bytes).map_err(ScriptBufDecoderError)
}
#[inline]
fn end(self) -> Result<Self::Output, Self::Error> {
Ok(ScriptBuf::from_bytes(self.0.end().map_err(ScriptBufDecoderError)?))
}
#[inline]
fn read_limit(&self) -> usize { self.0.read_limit() }
}
pub(crate) struct ScriptBufAsVec<'a, T>(&'a mut ScriptBuf<T>, Vec<u8>);
impl<T> core::ops::Deref for ScriptBufAsVec<'_, T> {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target { &self.1 }
}
impl<T> core::ops::DerefMut for ScriptBufAsVec<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.1 }
}
impl<T> Drop for ScriptBufAsVec<'_, T> {
fn drop(&mut self) {
let vec = core::mem::take(&mut self.1);
*(self.0) = ScriptBuf::from_bytes(vec);
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T> Arbitrary<'a> for ScriptBuf<T> {
#[inline]
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let v = Vec::<u8>::arbitrary(u)?;
Ok(Self::from_bytes(v))
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use super::ScriptBuf;
use crate::script::ScriptSigTag as Tag;
#[test]
fn reserved_len_for_slice() {
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0), 1);
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0x4b), 0x4b + 1);
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0x4c), 0x4c + 2);
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0xff), 0xff + 2);
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0x100), 0x100 + 3);
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0xffff), 0xffff + 3);
assert_eq!(ScriptBuf::<Tag>::reserved_len_for_slice(0x10000), 0x10000 + 5);
}
#[test]
fn as_byte_vec_deref_restores() {
let mut script = ScriptBuf::<Tag>::from_bytes(vec![1, 2, 3]);
{
let vec = script.as_byte_vec();
assert_eq!(vec.len(), 3);
assert_eq!(vec.as_slice(), &[1, 2, 3]);
}
assert_eq!(script.as_bytes(), &[1, 2, 3]);
}
}