bitcoin/blockdata/script/
witness_version.rs

1//! The segregated witness version byte as defined by [BIP141].
2//!
3//! > A scriptPubKey (or redeemScript as defined in BIP16/P2SH) that consists of a 1-byte push
4//! > opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new special
5//! > meaning. The value of the first push is called the "version byte". The following byte
6//! > vector pushed is called the "witness program".
7//!
8//! [BIP141]: <https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki>
9
10use 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/// Version of the segregated witness program.
22///
23/// Helps limit possible versions of the witness according to the specification. If a plain `u8`
24/// type was used instead it would mean that the version may be > 16, which would be incorrect.
25///
26/// First byte of `scriptPubkey` in transaction output for transactions starting with opcodes
27/// ranging from 0 to 16 (inclusive).
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
29#[repr(u8)]
30pub enum WitnessVersion {
31    /// Initial version of witness program. Used for P2WPKH and P2WPK outputs
32    V0 = 0,
33    /// Version of witness program used for Taproot P2TR outputs.
34    V1 = 1,
35    /// Future (unsupported) version of witness program.
36    V2 = 2,
37    /// Future (unsupported) version of witness program.
38    V3 = 3,
39    /// Future (unsupported) version of witness program.
40    V4 = 4,
41    /// Future (unsupported) version of witness program.
42    V5 = 5,
43    /// Future (unsupported) version of witness program.
44    V6 = 6,
45    /// Future (unsupported) version of witness program.
46    V7 = 7,
47    /// Future (unsupported) version of witness program.
48    V8 = 8,
49    /// Future (unsupported) version of witness program.
50    V9 = 9,
51    /// Future (unsupported) version of witness program.
52    V10 = 10,
53    /// Future (unsupported) version of witness program.
54    V11 = 11,
55    /// Future (unsupported) version of witness program.
56    V12 = 12,
57    /// Future (unsupported) version of witness program.
58    V13 = 13,
59    /// Future (unsupported) version of witness program.
60    V14 = 14,
61    /// Future (unsupported) version of witness program.
62    V15 = 15,
63    /// Future (unsupported) version of witness program.
64    V16 = 16,
65}
66
67impl WitnessVersion {
68    /// Returns integer version number representation for a given [`WitnessVersion`] value.
69    ///
70    /// NB: this is not the same as an integer representation of the opcode signifying witness
71    /// version in bitcoin script. Thus, there is no function to directly convert witness version
72    /// into a byte since the conversion requires context (bitcoin script or just a version number).
73    pub fn to_num(self) -> u8 { self as u8 }
74
75    /// Converts this witness version to a GF32 field element.
76    pub fn to_fe(self) -> Fe32 {
77        Fe32::try_from(self.to_num()).expect("0-16 are valid fe32 values")
78    }
79}
80
81/// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without any prefix or suffix.
82impl 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/// Error parsing [`WitnessVersion`] from a string.
169#[derive(Clone, Debug, PartialEq, Eq)]
170#[non_exhaustive]
171pub enum FromStrError {
172    /// Unable to parse integer from string.
173    Unparsable(ParseIntError),
174    /// String contained an invalid witness version number.
175    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/// Error attempting to create a [`WitnessVersion`] from an [`Instruction`]
212#[derive(Clone, Debug, PartialEq, Eq)]
213#[non_exhaustive]
214pub enum TryFromInstructionError {
215    /// Cannot not convert OP to a witness version.
216    TryFrom(TryFromError),
217    /// Cannot create a witness version from non-zero data push.
218    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/// Error attempting to create a [`WitnessVersion`] from an integer.
251#[derive(Clone, Debug, PartialEq, Eq)]
252pub struct TryFromError {
253    /// The invalid non-witness version integer.
254    invalid: u8,
255}
256
257impl TryFromError {
258    /// Returns the invalid non-witness version integer.
259    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 {}