bitcoin/blockdata/script/
witness_program.rs1use 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
21pub const MIN_SIZE: usize = 2;
23
24pub const MAX_SIZE: usize = 40;
26
27pub(crate) const P2A_PROGRAM: [u8;2] = [78, 115];
29
30#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct WitnessProgram {
37 version: WitnessVersion,
39 program: ArrayVec<u8, MAX_SIZE>,
41}
42
43impl WitnessProgram {
44 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 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 fn new_p2wpkh(program: [u8; 20]) -> Self {
64 WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
65 }
66
67 fn new_p2wsh(program: [u8; 32]) -> Self {
69 WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
70 }
71
72 fn new_p2tr(program: [u8; 32]) -> Self {
74 WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&program) }
75 }
76
77 pub fn p2wpkh(pk: &CompressedPublicKey) -> Self {
79 let hash = pk.wpubkey_hash();
80 WitnessProgram::new_p2wpkh(hash.to_byte_array())
81 }
82
83 pub fn p2wsh(script: &Script) -> Self {
85 let hash = script.wscript_hash();
86 WitnessProgram::new_p2wsh(hash.to_byte_array())
87 }
88
89 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 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 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 pub fn version(&self) -> WitnessVersion { self.version }
115
116 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 pub fn is_p2wpkh(&self) -> bool {
126 self.version == WitnessVersion::V0 && self.program.len() == 20
127 }
128
129 pub fn is_p2wsh(&self) -> bool {
131 self.version == WitnessVersion::V0 && self.program.len() == 32
132 }
133
134 pub fn is_p2tr(&self) -> bool { self.version == WitnessVersion::V1 && self.program.len() == 32 }
136
137 pub fn is_p2a(&self) -> bool {
139 self.version == WitnessVersion::V1 && self.program == P2A_PROGRAM
140 }
141}
142
143#[derive(Clone, Debug, PartialEq, Eq)]
145#[non_exhaustive]
146pub enum Error {
147 InvalidLength(usize),
149 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}