1use core::fmt;
13use core::str::FromStr;
14
15use units::parse_int;
16
17use crate::opcodes::all::{OP_1, OP_16};
18use crate::opcodes::{Opcode, OP_PUSHBYTES_0};
19
20#[rustfmt::skip] #[doc(no_inline)]
22pub use self::error::{FromStrError, TryFromError};
23
24#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
32#[repr(u8)]
33pub enum WitnessVersion {
34 V0 = 0,
36 V1 = 1,
38 V2 = 2,
40 V3 = 3,
42 V4 = 4,
44 V5 = 5,
46 V6 = 6,
48 V7 = 7,
50 V8 = 8,
52 V9 = 9,
54 V10 = 10,
56 V11 = 11,
58 V12 = 12,
60 V13 = 13,
62 V14 = 14,
64 V15 = 15,
66 V16 = 16,
68}
69
70impl WitnessVersion {
71 pub fn to_num(self) -> u8 { self as u8 }
77}
78
79impl fmt::Display for WitnessVersion {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as u8) }
82}
83
84impl FromStr for WitnessVersion {
85 type Err = FromStrError;
86
87 fn from_str(s: &str) -> Result<Self, Self::Err> {
88 let version: u8 = parse_int::int_from_str(s).map_err(FromStrError::Unparsable)?;
89 Self::try_from(version).map_err(FromStrError::Invalid)
90 }
91}
92
93impl TryFrom<u8> for WitnessVersion {
94 type Error = TryFromError;
95
96 fn try_from(no: u8) -> Result<Self, Self::Error> {
97 Ok(match no {
98 0 => Self::V0,
99 1 => Self::V1,
100 2 => Self::V2,
101 3 => Self::V3,
102 4 => Self::V4,
103 5 => Self::V5,
104 6 => Self::V6,
105 7 => Self::V7,
106 8 => Self::V8,
107 9 => Self::V9,
108 10 => Self::V10,
109 11 => Self::V11,
110 12 => Self::V12,
111 13 => Self::V13,
112 14 => Self::V14,
113 15 => Self::V15,
114 16 => Self::V16,
115 invalid => return Err(TryFromError { invalid }),
116 })
117 }
118}
119
120impl TryFrom<Opcode> for WitnessVersion {
121 type Error = TryFromError;
122
123 fn try_from(opcode: Opcode) -> Result<Self, Self::Error> {
124 match opcode.to_u8() {
125 0 => Ok(Self::V0),
126 version if version >= OP_1.to_u8() && version <= OP_16.to_u8() =>
127 Self::try_from(version - OP_1.to_u8() + 1),
128 invalid => Err(TryFromError { invalid }),
129 }
130 }
131}
132
133impl From<WitnessVersion> for Opcode {
134 fn from(version: WitnessVersion) -> Self {
135 match version {
136 WitnessVersion::V0 => OP_PUSHBYTES_0,
137 no => Self::from(OP_1.to_u8() + no.to_num() - 1),
138 }
139 }
140}
141
142pub mod error {
144 use core::convert::Infallible;
145 use core::fmt;
146
147 use internals::write_err;
148 use units::parse_int::ParseIntError;
149
150 #[derive(Clone, Debug, PartialEq, Eq)]
154 #[non_exhaustive]
155 pub enum FromStrError {
156 Unparsable(ParseIntError),
158 Invalid(TryFromError),
160 }
161
162 impl From<Infallible> for FromStrError {
163 fn from(never: Infallible) -> Self { match never {} }
164 }
165
166 impl fmt::Display for FromStrError {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 match *self {
169 Self::Unparsable(ref e) => write_err!(f, "integer parse error"; e),
170 Self::Invalid(ref e) => write_err!(f, "invalid version number"; e),
171 }
172 }
173 }
174
175 #[cfg(feature = "std")]
176 impl std::error::Error for FromStrError {
177 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
178 match *self {
179 Self::Unparsable(ref e) => Some(e),
180 Self::Invalid(ref e) => Some(e),
181 }
182 }
183 }
184 #[derive(Clone, Debug, PartialEq, Eq)]
188 pub struct TryFromError {
189 pub(super) invalid: u8,
191 }
192
193 impl TryFromError {
194 pub fn invalid_version(&self) -> u8 { self.invalid }
196 }
197
198 impl fmt::Display for TryFromError {
199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200 write!(f, "invalid witness script version: {}", self.invalid)
201 }
202 }
203
204 #[cfg(feature = "std")]
205 impl std::error::Error for TryFromError {
206 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
207 let Self { invalid: _ } = self;
208 None
209 }
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 #[cfg(feature = "alloc")]
216 use alloc::string::ToString;
217
218 use super::*;
219 use crate::opcodes::OP_PUSHDATA4;
220
221 #[test]
222 fn witness_version_to_num() {
223 assert_eq!(WitnessVersion::V0.to_num(), 0);
224 assert_eq!(WitnessVersion::V1.to_num(), 1);
225 assert_eq!(WitnessVersion::V2.to_num(), 2);
226 assert_eq!(WitnessVersion::V16.to_num(), 16);
227 }
228
229 #[test]
230 #[cfg(feature = "alloc")]
231 fn witness_version_display() {
232 assert_eq!(WitnessVersion::V0.to_string(), "0");
233 assert_eq!(WitnessVersion::V1.to_string(), "1");
234 assert_eq!(WitnessVersion::V10.to_string(), "10");
235 assert_eq!(WitnessVersion::V16.to_string(), "16");
236 }
237
238 #[test]
239 fn witness_version_try_from_opcode() {
240 assert_eq!(WitnessVersion::try_from(OP_PUSHBYTES_0).unwrap(), WitnessVersion::V0);
241 assert_eq!(WitnessVersion::try_from(OP_1).unwrap(), WitnessVersion::V1);
242 assert_eq!(WitnessVersion::try_from(OP_16).unwrap(), WitnessVersion::V16);
243
244 let op = Opcode::from(OP_1.to_u8() - 1);
246 assert_eq!(WitnessVersion::try_from(op).unwrap_err().invalid_version(), OP_1.to_u8() - 1);
247 let op = Opcode::from(0xff);
248 assert_eq!(WitnessVersion::try_from(op).unwrap_err().invalid_version(), 0xff);
249 assert_eq!(
250 WitnessVersion::try_from(Opcode::from(OP_PUSHDATA4)).unwrap_err().invalid_version(),
251 OP_PUSHDATA4
252 );
253 }
254
255 #[test]
256 fn witness_version_into_opcode() {
257 assert_eq!(Opcode::from(WitnessVersion::V0), OP_PUSHBYTES_0);
258 assert_eq!(Opcode::from(WitnessVersion::V1), OP_1);
259 assert_eq!(Opcode::from(WitnessVersion::V16), OP_16);
260 }
261
262 #[test]
263 fn witness_version_opcode_round_trip() {
264 for version in 0u8..=16 {
265 let wv = WitnessVersion::try_from(version).unwrap();
266 let opcode = Opcode::from(wv);
267 assert_eq!(WitnessVersion::try_from(opcode).unwrap(), wv);
268 }
269 }
270}