bitcoin/blockdata/script/
witness_version.rs1use core::fmt;
11use core::str::FromStr;
12
13use bech32::Fe32;
14use internals::write_err;
15use units::{parse, ParseIntError};
16
17use crate::blockdata::opcodes::all::*;
18use crate::blockdata::opcodes::Opcode;
19use crate::blockdata::script::Instruction;
20
21#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
29#[repr(u8)]
30pub enum WitnessVersion {
31 V0 = 0,
33 V1 = 1,
35 V2 = 2,
37 V3 = 3,
39 V4 = 4,
41 V5 = 5,
43 V6 = 6,
45 V7 = 7,
47 V8 = 8,
49 V9 = 9,
51 V10 = 10,
53 V11 = 11,
55 V12 = 12,
57 V13 = 13,
59 V14 = 14,
61 V15 = 15,
63 V16 = 16,
65}
66
67impl WitnessVersion {
68 pub fn to_num(self) -> u8 { self as u8 }
74
75 pub fn to_fe(self) -> Fe32 {
77 Fe32::try_from(self.to_num()).expect("0-16 are valid fe32 values")
78 }
79}
80
81impl fmt::Display for WitnessVersion {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as u8) }
84}
85
86impl FromStr for WitnessVersion {
87 type Err = FromStrError;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 let version: u8 = parse::int(s)?;
91 Ok(WitnessVersion::try_from(version)?)
92 }
93}
94
95impl TryFrom<bech32::Fe32> for WitnessVersion {
96 type Error = TryFromError;
97
98 fn try_from(value: Fe32) -> Result<Self, Self::Error> { Self::try_from(value.to_u8()) }
99}
100
101impl TryFrom<u8> for WitnessVersion {
102 type Error = TryFromError;
103
104 fn try_from(no: u8) -> Result<Self, Self::Error> {
105 use WitnessVersion::*;
106
107 Ok(match no {
108 0 => V0,
109 1 => V1,
110 2 => V2,
111 3 => V3,
112 4 => V4,
113 5 => V5,
114 6 => V6,
115 7 => V7,
116 8 => V8,
117 9 => V9,
118 10 => V10,
119 11 => V11,
120 12 => V12,
121 13 => V13,
122 14 => V14,
123 15 => V15,
124 16 => V16,
125 invalid => return Err(TryFromError { invalid }),
126 })
127 }
128}
129
130impl TryFrom<Opcode> for WitnessVersion {
131 type Error = TryFromError;
132
133 fn try_from(opcode: Opcode) -> Result<Self, Self::Error> {
134 match opcode.to_u8() {
135 0 => Ok(WitnessVersion::V0),
136 version if version >= OP_PUSHNUM_1.to_u8() && version <= OP_PUSHNUM_16.to_u8() =>
137 WitnessVersion::try_from(version - OP_PUSHNUM_1.to_u8() + 1),
138 invalid => Err(TryFromError { invalid }),
139 }
140 }
141}
142
143impl<'a> TryFrom<Instruction<'a>> for WitnessVersion {
144 type Error = TryFromInstructionError;
145
146 fn try_from(instruction: Instruction) -> Result<Self, Self::Error> {
147 match instruction {
148 Instruction::Op(op) => Ok(WitnessVersion::try_from(op)?),
149 Instruction::PushBytes(bytes) if bytes.is_empty() => Ok(WitnessVersion::V0),
150 Instruction::PushBytes(_) => Err(TryFromInstructionError::DataPush),
151 }
152 }
153}
154
155impl From<WitnessVersion> for Fe32 {
156 fn from(version: WitnessVersion) -> Self { version.to_fe() }
157}
158
159impl From<WitnessVersion> for Opcode {
160 fn from(version: WitnessVersion) -> Opcode {
161 match version {
162 WitnessVersion::V0 => OP_PUSHBYTES_0,
163 no => Opcode::from(OP_PUSHNUM_1.to_u8() + no.to_num() - 1),
164 }
165 }
166}
167
168#[derive(Clone, Debug, PartialEq, Eq)]
170#[non_exhaustive]
171pub enum FromStrError {
172 Unparsable(ParseIntError),
174 Invalid(TryFromError),
176}
177
178internals::impl_from_infallible!(FromStrError);
179
180impl fmt::Display for FromStrError {
181 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182 use FromStrError::*;
183
184 match *self {
185 Unparsable(ref e) => write_err!(f, "integer parse error"; e),
186 Invalid(ref e) => write_err!(f, "invalid version number"; e),
187 }
188 }
189}
190
191#[cfg(feature = "std")]
192impl std::error::Error for FromStrError {
193 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
194 use FromStrError::*;
195
196 match *self {
197 Unparsable(ref e) => Some(e),
198 Invalid(ref e) => Some(e),
199 }
200 }
201}
202
203impl From<ParseIntError> for FromStrError {
204 fn from(e: ParseIntError) -> Self { Self::Unparsable(e) }
205}
206
207impl From<TryFromError> for FromStrError {
208 fn from(e: TryFromError) -> Self { Self::Invalid(e) }
209}
210
211#[derive(Clone, Debug, PartialEq, Eq)]
213#[non_exhaustive]
214pub enum TryFromInstructionError {
215 TryFrom(TryFromError),
217 DataPush,
219}
220
221internals::impl_from_infallible!(TryFromInstructionError);
222
223impl fmt::Display for TryFromInstructionError {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 use TryFromInstructionError::*;
226
227 match *self {
228 TryFrom(ref e) => write_err!(f, "opcode is not a valid witness version"; e),
229 DataPush => write!(f, "non-zero data push opcode is not a valid witness version"),
230 }
231 }
232}
233
234#[cfg(feature = "std")]
235impl std::error::Error for TryFromInstructionError {
236 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
237 use TryFromInstructionError::*;
238
239 match *self {
240 TryFrom(ref e) => Some(e),
241 DataPush => None,
242 }
243 }
244}
245
246impl From<TryFromError> for TryFromInstructionError {
247 fn from(e: TryFromError) -> Self { Self::TryFrom(e) }
248}
249
250#[derive(Clone, Debug, PartialEq, Eq)]
252pub struct TryFromError {
253 invalid: u8,
255}
256
257impl TryFromError {
258 pub fn invalid_version(&self) -> u8 { self.invalid }
260}
261
262impl fmt::Display for TryFromError {
263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264 write!(f, "invalid witness script version: {}", self.invalid)
265 }
266}
267
268#[cfg(feature = "std")]
269impl std::error::Error for TryFromError {}