use amplify::confinement::Confined;
use crate::opcodes::*;
use crate::{ScriptBytes, LIB_NAME_BITCOIN};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)]
#[non_exhaustive]
#[repr(u8)]
pub enum OpCode {
#[display("OP_PUSH_BYTES0")]
PushBytes0 = OP_PUSHBYTES_0,
#[display("OP_PUSH_BYTES32")]
PushBytes32 = OP_PUSHBYTES_32,
Reserved = OP_RESERVED,
#[display("OP_RETURN")]
#[strict_type(dumb)]
Return = OP_RETURN,
#[display("OP_PUSH_DATA1")]
PushData1 = OP_PUSHDATA1,
#[display("OP_PUSH_DATA2")]
PushData2 = OP_PUSHDATA2,
#[display("OP_PUSH_DATA3")]
PushData4 = OP_PUSHDATA4,
#[display("OP_PUSHNUM_1")]
PushNum1 = OP_PUSHNUM_1,
#[display("OP_DUP")]
Dup = OP_DUP,
#[display("OP_EQUAL")]
Equal = OP_EQUAL,
#[display("OP_EQUALVERIFY")]
EqualVerify = OP_EQUALVERIFY,
#[display("OP_RIPEMD160")]
Ripemd160 = OP_RIPEMD160,
#[display("OP_SHA1")]
Sha1 = OP_SHA1,
#[display("OP_SHA256")]
Sha256 = OP_SHA256,
#[display("OP_HASH160")]
Hash160 = OP_HASH160,
#[display("OP_HASH256")]
Hash256 = OP_HASH256,
#[display("OP_CHECKSIG")]
CheckSig = OP_CHECKSIG,
#[display("OP_CHECKSIGVERIFY")]
CheckSigVerify = OP_CHECKSIGVERIFY,
}
#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, Index, RangeOps, BorrowSlice, LowerHex, UpperHex)]
#[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct SigScript(
#[from]
#[from(Vec<u8>)]
ScriptBytes,
);
#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, Index, RangeOps, BorrowSlice, LowerHex, UpperHex)]
#[wrapper_mut(DerefMut, IndexMut, RangeMut, BorrowSliceMut)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct ScriptPubkey(
#[from]
#[from(Vec<u8>)]
ScriptBytes,
);
impl ScriptPubkey {
pub fn new() -> Self { Self::default() }
pub fn with_capacity(capacity: usize) -> Self {
Self(ScriptBytes::from(Confined::with_capacity(capacity)))
}
pub fn p2pkh(hash: impl Into<[u8; 20]>) -> Self {
let mut script = Self::with_capacity(25);
script.push_opcode(OpCode::Dup);
script.push_opcode(OpCode::Hash160);
script.push_slice(&hash.into());
script.push_opcode(OpCode::EqualVerify);
script.push_opcode(OpCode::CheckSig);
script
}
pub fn p2sh(hash: impl Into<[u8; 20]>) -> Self {
let mut script = Self::with_capacity(23);
script.push_opcode(OpCode::Hash160);
script.push_slice(&hash.into());
script.push_opcode(OpCode::Equal);
script
}
pub fn op_return(data: &[u8]) -> Self {
let mut script = Self::with_capacity(ScriptBytes::len_for_slice(data.len()) + 1);
script.push_opcode(OpCode::Return);
script.push_slice(data);
script
}
#[inline]
pub fn is_p2pkh(&self) -> bool {
self.0.len() == 25 &&
self.0[0] == OP_DUP &&
self.0[1] == OP_HASH160 &&
self.0[2] == OP_PUSHBYTES_20 &&
self.0[23] == OP_EQUALVERIFY &&
self.0[24] == OP_CHECKSIG
}
#[inline]
pub fn is_p2sh(&self) -> bool {
self.0.len() == 23 &&
self.0[0] == OP_HASH160 &&
self.0[1] == OP_PUSHBYTES_20 &&
self.0[22] == OP_EQUAL
}
pub fn is_op_return(&self) -> bool { self[0] == OpCode::Return as u8 }
pub fn push_opcode(&mut self, op_code: OpCode) {
self.0.push(op_code as u8).expect("script exceeds 4GB");
}
}