bitcoin/blockdata/script/
witness_program.rs

1//! The segregated witness program 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;
11
12use hashes::Hash as _;
13use internals::array_vec::ArrayVec;
14use secp256k1::{Secp256k1, Verification};
15
16use crate::blockdata::script::witness_version::WitnessVersion;
17use crate::blockdata::script::{PushBytes, Script};
18use crate::crypto::key::{CompressedPublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
19use crate::taproot::TapNodeHash;
20
21/// The minimum byte size of a segregated witness program.
22pub const MIN_SIZE: usize = 2;
23
24/// The maximum byte size of a segregated witness program.
25pub const MAX_SIZE: usize = 40;
26
27/// The P2A program which is given by 0x4e73.
28pub(crate) const P2A_PROGRAM: [u8;2] = [78, 115];
29
30/// The segregated witness program.
31///
32/// The segregated witness program is technically only the program bytes _excluding_ the witness
33/// version, however we maintain length invariants on the `program` that are governed by the version
34/// number, therefore we carry the version number around along with the program bytes.
35#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct WitnessProgram {
37    /// The segwit version associated with this witness program.
38    version: WitnessVersion,
39    /// The witness program (between 2 and 40 bytes).
40    program: ArrayVec<u8, MAX_SIZE>,
41}
42
43impl WitnessProgram {
44    /// Creates a new witness program, copying the content from the given byte slice.
45    pub fn new(version: WitnessVersion, bytes: &[u8]) -> Result<Self, Error> {
46        use Error::*;
47
48        let program_len = bytes.len();
49        if program_len < MIN_SIZE || program_len > MAX_SIZE {
50            return Err(InvalidLength(program_len));
51        }
52
53        // Specific segwit v0 check. These addresses can never spend funds sent to them.
54        if version == WitnessVersion::V0 && (program_len != 20 && program_len != 32) {
55            return Err(InvalidSegwitV0Length(program_len));
56        }
57
58        let program = ArrayVec::from_slice(bytes);
59        Ok(WitnessProgram { version, program })
60    }
61
62    /// Creates a [`WitnessProgram`] from a 20 byte pubkey hash.
63    fn new_p2wpkh(program: [u8; 20]) -> Self {
64        WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
65    }
66
67    /// Creates a [`WitnessProgram`] from a 32 byte script hash.
68    fn new_p2wsh(program: [u8; 32]) -> Self {
69        WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
70    }
71
72    /// Creates a [`WitnessProgram`] from a 32 byte serialize taproot xonly pubkey.
73    fn new_p2tr(program: [u8; 32]) -> Self {
74        WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&program) }
75    }
76
77    /// Creates a [`WitnessProgram`] from `pk` for a P2WPKH output.
78    pub fn p2wpkh(pk: &CompressedPublicKey) -> Self {
79        let hash = pk.wpubkey_hash();
80        WitnessProgram::new_p2wpkh(hash.to_byte_array())
81    }
82
83    /// Creates a [`WitnessProgram`] from `script` for a P2WSH output.
84    pub fn p2wsh(script: &Script) -> Self {
85        let hash = script.wscript_hash();
86        WitnessProgram::new_p2wsh(hash.to_byte_array())
87    }
88
89    /// Creates a pay to taproot address from an untweaked key.
90    pub fn p2tr<C: Verification>(
91        secp: &Secp256k1<C>,
92        internal_key: UntweakedPublicKey,
93        merkle_root: Option<TapNodeHash>,
94    ) -> Self {
95        let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
96        let pubkey = output_key.as_x_only_public_key().serialize();
97        WitnessProgram::new_p2tr(pubkey)
98    }
99
100    /// Creates a pay to taproot address from a pre-tweaked output key.
101    pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
102        let pubkey = output_key.as_x_only_public_key().serialize();
103        WitnessProgram::new_p2tr(pubkey)
104    }
105
106    internals::const_tools::cond_const! {
107        /// Constructs a new pay to anchor address
108        pub const(in rust_v_1_61 = "1.61") fn p2a() -> Self {
109            WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&P2A_PROGRAM)}
110        }
111    }
112
113    /// Returns the witness program version.
114    pub fn version(&self) -> WitnessVersion { self.version }
115
116    /// Returns the witness program.
117    pub fn program(&self) -> &PushBytes {
118        self.program
119            .as_slice()
120            .try_into()
121            .expect("witness programs are always smaller than max size of PushBytes")
122    }
123
124    /// Returns true if this witness program is for a P2WPKH output.
125    pub fn is_p2wpkh(&self) -> bool {
126        self.version == WitnessVersion::V0 && self.program.len() == 20
127    }
128
129    /// Returns true if this witness program is for a P2WPSH output.
130    pub fn is_p2wsh(&self) -> bool {
131        self.version == WitnessVersion::V0 && self.program.len() == 32
132    }
133
134    /// Returns true if this witness program is for a P2TR output.
135    pub fn is_p2tr(&self) -> bool { self.version == WitnessVersion::V1 && self.program.len() == 32 }
136
137    /// Returns true if this is a pay to anchor output.
138    pub fn is_p2a(&self) -> bool {
139        self.version == WitnessVersion::V1 && self.program == P2A_PROGRAM
140    }
141}
142
143/// Witness program error.
144#[derive(Clone, Debug, PartialEq, Eq)]
145#[non_exhaustive]
146pub enum Error {
147    /// The witness program must be between 2 and 40 bytes in length.
148    InvalidLength(usize),
149    /// A v0 witness program must be either of length 20 or 32.
150    InvalidSegwitV0Length(usize),
151}
152
153internals::impl_from_infallible!(Error);
154
155impl fmt::Display for Error {
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157        use Error::*;
158
159        match *self {
160            InvalidLength(len) =>
161                write!(f, "witness program must be between 2 and 40 bytes: length={}", len),
162            InvalidSegwitV0Length(len) =>
163                write!(f, "a v0 witness program must be either 20 or 32 bytes: length={}", len),
164        }
165    }
166}
167
168#[cfg(feature = "std")]
169impl std::error::Error for Error {
170    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
171        use Error::*;
172
173        match *self {
174            InvalidLength(_) | InvalidSegwitV0Length(_) => None,
175        }
176    }
177}