use core::convert::Infallible;
use core::fmt;
use core::str::FromStr;
use internals::write_err;
use crate::opcodes::all::*;
use crate::opcodes::Opcode;
use crate::parse_int::{self, ParseIntError};
use crate::script::Instruction;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u8)]
pub enum WitnessVersion {
V0 = 0,
V1 = 1,
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5,
V6 = 6,
V7 = 7,
V8 = 8,
V9 = 9,
V10 = 10,
V11 = 11,
V12 = 12,
V13 = 13,
V14 = 14,
V15 = 15,
V16 = 16,
}
impl WitnessVersion {
pub fn to_num(self) -> u8 {
self as u8
}
}
impl fmt::Display for WitnessVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", *self as u8)
}
}
impl FromStr for WitnessVersion {
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let version: u8 = parse_int::int_from_str(s)?;
Ok(Self::try_from(version)?)
}
}
impl TryFrom<u8> for WitnessVersion {
type Error = TryFromError;
fn try_from(no: u8) -> Result<Self, Self::Error> {
use WitnessVersion::*;
Ok(match no {
0 => V0,
1 => V1,
2 => V2,
3 => V3,
4 => V4,
5 => V5,
6 => V6,
7 => V7,
8 => V8,
9 => V9,
10 => V10,
11 => V11,
12 => V12,
13 => V13,
14 => V14,
15 => V15,
16 => V16,
invalid => return Err(TryFromError { invalid }),
})
}
}
impl TryFrom<Opcode> for WitnessVersion {
type Error = TryFromError;
fn try_from(opcode: Opcode) -> Result<Self, Self::Error> {
match opcode.to_u8() {
0 => Ok(Self::V0),
version if version >= OP_1.to_u8() && version <= OP_16.to_u8() => {
Self::try_from(version - OP_1.to_u8() + 1)
}
invalid => Err(TryFromError { invalid }),
}
}
}
impl TryFrom<Instruction<'_>> for WitnessVersion {
type Error = TryFromInstructionError;
fn try_from(instruction: Instruction) -> Result<Self, Self::Error> {
match instruction {
Instruction::Op(op) => Ok(Self::try_from(op)?),
Instruction::PushBytes(bytes) if bytes.is_empty() => Ok(Self::V0),
Instruction::PushBytes(_) => Err(TryFromInstructionError::DataPush),
}
}
}
impl From<WitnessVersion> for Opcode {
fn from(version: WitnessVersion) -> Self {
match version {
WitnessVersion::V0 => OP_PUSHBYTES_0,
no => Self::from(OP_1.to_u8() + no.to_num() - 1),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FromStrError {
Unparsable(ParseIntError),
Invalid(TryFromError),
}
impl From<Infallible> for FromStrError {
fn from(never: Infallible) -> Self {
match never {}
}
}
impl fmt::Display for FromStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Unparsable(ref e) => write_err!(f, "integer parse error"; e),
Self::Invalid(ref e) => write_err!(f, "invalid version number"; e),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FromStrError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Unparsable(ref e) => Some(e),
Self::Invalid(ref e) => Some(e),
}
}
}
impl From<ParseIntError> for FromStrError {
fn from(e: ParseIntError) -> Self {
Self::Unparsable(e)
}
}
impl From<TryFromError> for FromStrError {
fn from(e: TryFromError) -> Self {
Self::Invalid(e)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum TryFromInstructionError {
TryFrom(TryFromError),
DataPush,
}
impl From<Infallible> for TryFromInstructionError {
fn from(never: Infallible) -> Self {
match never {}
}
}
impl fmt::Display for TryFromInstructionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TryFrom(ref e) => write_err!(f, "opcode is not a valid witness version"; e),
Self::DataPush => write!(f, "non-zero data push opcode is not a valid witness version"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromInstructionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::TryFrom(ref e) => Some(e),
Self::DataPush => None,
}
}
}
impl From<TryFromError> for TryFromInstructionError {
fn from(e: TryFromError) -> Self {
Self::TryFrom(e)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TryFromError {
invalid: u8,
}
impl TryFromError {
pub fn invalid_version(&self) -> u8 {
self.invalid
}
}
impl fmt::Display for TryFromError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid witness script version: {}", self.invalid)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromError {}