Skip to main content

bitcoin_primitives/
witness_version.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! The segregated witness version byte as defined by [BIP-0141].
4//!
5//! > A scriptPubKey (or redeemScript as defined in BIP-0016/P2SH) that consists of a 1-byte push
6//! > opcode (for 0 to 16) followed by a data push between 2 and 40 bytes gets a new special
7//! > meaning. The value of the first push is called the "version byte". The following byte
8//! > vector pushed is called the "witness program".
9//!
10//! [BIP-0141]: <https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki>
11
12use 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]            // Keep public re-exports separate.
21#[doc(no_inline)]
22pub use self::error::{FromStrError, TryFromError};
23
24/// Version of the segregated witness program.
25///
26/// Helps limit possible versions of the witness according to the specification. If a plain `u8`
27/// type was used instead it would mean that the version may be > 16, which would be incorrect.
28///
29/// First byte of `scriptPubkey` in transaction output for transactions starting with opcodes
30/// ranging from 0 to 16 (inclusive).
31#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
32#[repr(u8)]
33pub enum WitnessVersion {
34    /// Initial version of witness program. Used for P2WPKH and P2WSH outputs.
35    V0 = 0,
36    /// Version of witness program used for Taproot P2TR outputs.
37    V1 = 1,
38    /// Future (unsupported) version of witness program.
39    V2 = 2,
40    /// Future (unsupported) version of witness program.
41    V3 = 3,
42    /// Future (unsupported) version of witness program.
43    V4 = 4,
44    /// Future (unsupported) version of witness program.
45    V5 = 5,
46    /// Future (unsupported) version of witness program.
47    V6 = 6,
48    /// Future (unsupported) version of witness program.
49    V7 = 7,
50    /// Future (unsupported) version of witness program.
51    V8 = 8,
52    /// Future (unsupported) version of witness program.
53    V9 = 9,
54    /// Future (unsupported) version of witness program.
55    V10 = 10,
56    /// Future (unsupported) version of witness program.
57    V11 = 11,
58    /// Future (unsupported) version of witness program.
59    V12 = 12,
60    /// Future (unsupported) version of witness program.
61    V13 = 13,
62    /// Future (unsupported) version of witness program.
63    V14 = 14,
64    /// Future (unsupported) version of witness program.
65    V15 = 15,
66    /// Future (unsupported) version of witness program.
67    V16 = 16,
68}
69
70impl WitnessVersion {
71    /// Returns integer version number representation for a given [`WitnessVersion`] value.
72    ///
73    /// NB: this is not the same as an integer representation of the opcode signifying witness
74    /// version in bitcoin script. Thus, there is no function to directly convert witness version
75    /// into a byte since the conversion requires context (bitcoin script or just a version number).
76    pub fn to_num(self) -> u8 { self as u8 }
77}
78
79/// Prints [`WitnessVersion`] number (from 0 to 16) as integer, without any prefix or suffix.
80impl 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
142/// Error types for the segwit version number.
143pub 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    /// Error parsing [`WitnessVersion`] from a string.
151    ///
152    /// [`WitnessVersion`]: super::WitnessVersion
153    #[derive(Clone, Debug, PartialEq, Eq)]
154    #[non_exhaustive]
155    pub enum FromStrError {
156        /// Unable to parse integer from string.
157        Unparsable(ParseIntError),
158        /// String contained an invalid witness version number.
159        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    /// Error attempting to create a [`WitnessVersion`] from an integer.
185    ///
186    /// [`WitnessVersion`]: super::WitnessVersion
187    #[derive(Clone, Debug, PartialEq, Eq)]
188    pub struct TryFromError {
189        /// The invalid non-witness version integer.
190        pub(super) invalid: u8,
191    }
192
193    impl TryFromError {
194        /// Returns the invalid non-witness version integer.
195        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        // Only Opcodes in range OP_1 to OP_16, or 0, are valid.
245        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}