use std::vec;
use amplify::confinement::Confined;
use amplify::{confinement, Bytes32StrRev, Wrapper};
use crate::opcodes::*;
use crate::{
ByteStr, RedeemScript, ScriptBytes, ScriptPubkey, VarIntArray, WScriptHash, LIB_NAME_BITCOIN,
};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display(doc_comments)]
pub enum SegwitError {
InvalidWitnessVersion(u8),
MalformedWitnessVersion,
InvalidWitnessProgramLength(usize),
InvalidSegwitV0ProgramLength(usize),
UncompressedPubkey,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN, tags = repr, into_u8, try_from_u8)]
#[repr(u8)]
pub enum WitnessVer {
#[strict_type(dumb)]
#[display("segwit0")]
V0 = OP_PUSHBYTES_0,
#[display("segwit1")]
V1 = OP_PUSHNUM_1,
#[display("segwit2")]
V2 = OP_PUSHNUM_2,
#[display("segwit3")]
V3 = OP_PUSHNUM_3,
#[display("segwit4")]
V4 = OP_PUSHNUM_4,
#[display("segwit5")]
V5 = OP_PUSHNUM_5,
#[display("segwit6")]
V6 = OP_PUSHNUM_6,
#[display("segwit7")]
V7 = OP_PUSHNUM_7,
#[display("segwit8")]
V8 = OP_PUSHNUM_8,
#[display("segwit9")]
V9 = OP_PUSHNUM_9,
#[display("segwit10")]
V10 = OP_PUSHNUM_10,
#[display("segwit11")]
V11 = OP_PUSHNUM_11,
#[display("segwit12")]
V12 = OP_PUSHNUM_12,
#[display("segwit13")]
V13 = OP_PUSHNUM_13,
#[display("segwit14")]
V14 = OP_PUSHNUM_14,
#[display("segwit15")]
V15 = OP_PUSHNUM_15,
#[display("segwit16")]
V16 = OP_PUSHNUM_16,
}
impl WitnessVer {
pub fn from_op_code(op_code: OpCode) -> Result<Self, SegwitError> {
match op_code as u8 {
0 => Ok(WitnessVer::V0),
OP_PUSHNUM_1 => Ok(WitnessVer::V1),
OP_PUSHNUM_2 => Ok(WitnessVer::V2),
OP_PUSHNUM_3 => Ok(WitnessVer::V3),
OP_PUSHNUM_4 => Ok(WitnessVer::V4),
OP_PUSHNUM_5 => Ok(WitnessVer::V5),
OP_PUSHNUM_6 => Ok(WitnessVer::V6),
OP_PUSHNUM_7 => Ok(WitnessVer::V7),
OP_PUSHNUM_8 => Ok(WitnessVer::V8),
OP_PUSHNUM_9 => Ok(WitnessVer::V9),
OP_PUSHNUM_10 => Ok(WitnessVer::V10),
OP_PUSHNUM_11 => Ok(WitnessVer::V11),
OP_PUSHNUM_12 => Ok(WitnessVer::V12),
OP_PUSHNUM_13 => Ok(WitnessVer::V13),
OP_PUSHNUM_14 => Ok(WitnessVer::V14),
OP_PUSHNUM_15 => Ok(WitnessVer::V15),
OP_PUSHNUM_16 => Ok(WitnessVer::V16),
_ => Err(SegwitError::MalformedWitnessVersion),
}
}
pub fn from_version_no(no: u8) -> Result<Self, SegwitError> {
Ok(match no {
v if v == Self::V0.version_no() => Self::V0,
v if v == Self::V1.version_no() => Self::V1,
v if v == Self::V2.version_no() => Self::V2,
v if v == Self::V3.version_no() => Self::V3,
v if v == Self::V4.version_no() => Self::V4,
v if v == Self::V5.version_no() => Self::V5,
v if v == Self::V6.version_no() => Self::V6,
v if v == Self::V7.version_no() => Self::V7,
v if v == Self::V8.version_no() => Self::V8,
v if v == Self::V9.version_no() => Self::V9,
v if v == Self::V10.version_no() => Self::V10,
v if v == Self::V11.version_no() => Self::V11,
v if v == Self::V12.version_no() => Self::V12,
v if v == Self::V13.version_no() => Self::V13,
v if v == Self::V14.version_no() => Self::V14,
v if v == Self::V15.version_no() => Self::V15,
v if v == Self::V16.version_no() => Self::V16,
_ => return Err(SegwitError::InvalidWitnessVersion(no)),
})
}
pub fn op_code(self) -> OpCode {
OpCode::try_from(self as u8).expect("full range of u8 is covered")
}
pub fn version_no(self) -> u8 {
match self {
WitnessVer::V0 => 0,
WitnessVer::V1 => 1,
WitnessVer::V2 => 2,
WitnessVer::V3 => 3,
WitnessVer::V4 => 4,
WitnessVer::V5 => 5,
WitnessVer::V6 => 6,
WitnessVer::V7 => 7,
WitnessVer::V8 => 8,
WitnessVer::V9 => 9,
WitnessVer::V10 => 10,
WitnessVer::V11 => 11,
WitnessVer::V12 => 12,
WitnessVer::V13 => 13,
WitnessVer::V14 => 14,
WitnessVer::V15 => 15,
WitnessVer::V16 => 16,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(StrictType, StrictEncode, StrictDecode, StrictDumb)]
#[strict_type(lib = LIB_NAME_BITCOIN, dumb = Self::dumb())]
pub struct WitnessProgram {
version: WitnessVer,
program: Confined<Vec<u8>, 2, 40>,
}
impl WitnessProgram {
fn dumb() -> Self { Self::new(strict_dumb!(), vec![0; 32]).unwrap() }
pub fn new(version: WitnessVer, program: Vec<u8>) -> Result<Self, SegwitError> {
let len = program.len();
let program = Confined::try_from(program)
.map_err(|_| SegwitError::InvalidWitnessProgramLength(len))?;
if version == WitnessVer::V0 && (program.len() != 20 && program.len() != 32) {
return Err(SegwitError::InvalidSegwitV0ProgramLength(program.len()));
}
Ok(WitnessProgram { version, program })
}
pub fn version(&self) -> WitnessVer { self.version }
pub fn program(&self) -> &[u8] { &self.program }
}
impl ScriptPubkey {
pub fn p2wpkh(hash: impl Into<[u8; 20]>) -> Self {
Self::with_witness_program_unchecked(WitnessVer::V0, &hash.into())
}
pub fn p2wsh(hash: impl Into<[u8; 32]>) -> Self {
Self::with_witness_program_unchecked(WitnessVer::V0, &hash.into())
}
pub fn is_p2wpkh(&self) -> bool {
self.len() == 22 && self[0] == WitnessVer::V0.op_code() as u8 && self[1] == OP_PUSHBYTES_20
}
pub fn is_p2wsh(&self) -> bool {
self.len() == 34 && self[0] == WitnessVer::V0.op_code() as u8 && self[1] == OP_PUSHBYTES_32
}
pub fn from_witness_program(witness_program: &WitnessProgram) -> Self {
Self::with_witness_program_unchecked(witness_program.version, witness_program.program())
}
pub(crate) fn with_witness_program_unchecked(ver: WitnessVer, prog: &[u8]) -> Self {
let mut script = Self::with_capacity(ScriptBytes::len_for_slice(prog.len()) + 2);
script.push_opcode(ver.op_code());
script.push_slice(prog);
script
}
#[inline]
pub fn is_witness_program(&self) -> bool {
let script_len = self.len();
if !(4..=42).contains(&script_len) {
return false;
}
let Ok(ver_opcode) = OpCode::try_from(self[0]) else {
return false;
};
let push_opbyte = self[1]; WitnessVer::from_op_code(ver_opcode).is_ok()
&& (OP_PUSHBYTES_2..=OP_PUSHBYTES_40).contains(&push_opbyte)
&& script_len - 2 == push_opbyte as usize
}
}
#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, AsSlice, Hex)]
#[wrapper_mut(DerefMut, AsSliceMut)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct WitnessScript(ScriptBytes);
impl TryFrom<Vec<u8>> for WitnessScript {
type Error = confinement::Error;
fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
ScriptBytes::try_from(script_bytes).map(Self)
}
}
impl WitnessScript {
#[inline]
pub fn new() -> Self { Self::default() }
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self(ScriptBytes::from(Confined::with_capacity(capacity)))
}
#[inline]
pub fn from_unsafe(script_bytes: Vec<u8>) -> Self {
Self(ScriptBytes::from_unsafe(script_bytes))
}
#[inline]
pub fn push_opcode(&mut self, op_code: OpCode) { self.0.push(op_code as u8); }
pub fn to_redeem_script(&self) -> RedeemScript {
let script = ScriptPubkey::p2wsh(WScriptHash::from(self));
RedeemScript::from_inner(script.into_inner())
}
pub fn to_script_pubkey(&self) -> ScriptPubkey { ScriptPubkey::p2wsh(WScriptHash::from(self)) }
#[inline]
pub fn as_script_bytes(&self) -> &ScriptBytes { &self.0 }
}
#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
#[wrapper(BorrowSlice, Index, RangeOps, Debug, Hex, Display, FromStr)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct Wtxid(
#[from]
#[from([u8; 32])]
Bytes32StrRev,
);
#[derive(Wrapper, Clone, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, Index, RangeOps)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
pub struct Witness(VarIntArray<ByteStr>);
impl IntoIterator for Witness {
type Item = ByteStr;
type IntoIter = vec::IntoIter<ByteStr>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl Witness {
#[inline]
pub fn new() -> Self { default!() }
#[inline]
pub fn elements(&self) -> impl Iterator<Item = &'_ [u8]> {
self.0.iter().map(|el| el.as_slice())
}
pub fn from_consensus_stack(witness: impl IntoIterator<Item = Vec<u8>>) -> Witness {
let iter = witness.into_iter().map(ByteStr::from);
let stack =
VarIntArray::try_from_iter(iter).expect("witness stack size exceeds 2^32 elements");
Witness(stack)
}
#[inline]
pub(crate) fn as_var_int_array(&self) -> &VarIntArray<ByteStr> { &self.0 }
}
#[cfg(feature = "serde")]
mod _serde {
use serde::{Deserialize, Serialize};
use serde_crate::ser::SerializeSeq;
use serde_crate::{Deserializer, Serializer};
use super::*;
impl Serialize for Witness {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let mut ser = serializer.serialize_seq(Some(self.len()))?;
for el in &self.0 {
ser.serialize_element(&el)?;
}
ser.end()
}
}
impl<'de> Deserialize<'de> for Witness {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
let data = Vec::<ByteStr>::deserialize(deserializer)?;
Ok(Witness::from_consensus_stack(data.into_iter().map(ByteStr::into_vec)))
}
}
}