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
27#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct WitnessProgram {
34 version: WitnessVersion,
36 program: ArrayVec<u8, MAX_SIZE>,
38}
39
40impl WitnessProgram {
41 pub fn new(version: WitnessVersion, bytes: &[u8]) -> Result<Self, Error> {
43 use Error::*;
44
45 let program_len = bytes.len();
46 if program_len < MIN_SIZE || program_len > MAX_SIZE {
47 return Err(InvalidLength(program_len));
48 }
49
50 if version == WitnessVersion::V0 && (program_len != 20 && program_len != 32) {
52 return Err(InvalidSegwitV0Length(program_len));
53 }
54
55 let program = ArrayVec::from_slice(bytes);
56 Ok(WitnessProgram { version, program })
57 }
58
59 fn new_p2wpkh(program: [u8; 20]) -> Self {
61 WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
62 }
63
64 fn new_p2wsh(program: [u8; 32]) -> Self {
66 WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
67 }
68
69 fn new_p2tr(program: [u8; 32]) -> Self {
71 WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&program) }
72 }
73
74 pub fn p2wpkh(pk: &CompressedPublicKey) -> Self {
76 let hash = pk.wpubkey_hash();
77 WitnessProgram::new_p2wpkh(hash.to_byte_array())
78 }
79
80 pub fn p2wsh(script: &Script) -> Self {
82 let hash = script.wscript_hash();
83 WitnessProgram::new_p2wsh(hash.to_byte_array())
84 }
85
86 pub fn p2tr<C: Verification>(
88 secp: &Secp256k1<C>,
89 internal_key: UntweakedPublicKey,
90 merkle_root: Option<TapNodeHash>,
91 ) -> Self {
92 let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
93 let pubkey = output_key.to_inner().serialize();
94 WitnessProgram::new_p2tr(pubkey)
95 }
96
97 pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
99 let pubkey = output_key.to_inner().serialize();
100 WitnessProgram::new_p2tr(pubkey)
101 }
102
103 pub fn version(&self) -> WitnessVersion { self.version }
105
106 pub fn program(&self) -> &PushBytes {
108 self.program
109 .as_slice()
110 .try_into()
111 .expect("witness programs are always smaller than max size of PushBytes")
112 }
113
114 pub fn is_p2wpkh(&self) -> bool {
116 self.version == WitnessVersion::V0 && self.program.len() == 20
117 }
118
119 pub fn is_p2wsh(&self) -> bool {
121 self.version == WitnessVersion::V0 && self.program.len() == 32
122 }
123
124 pub fn is_p2tr(&self) -> bool { self.version == WitnessVersion::V1 && self.program.len() == 32 }
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
130#[non_exhaustive]
131pub enum Error {
132 InvalidLength(usize),
134 InvalidSegwitV0Length(usize),
136}
137
138internals::impl_from_infallible!(Error);
139
140impl fmt::Display for Error {
141 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142 use Error::*;
143
144 match *self {
145 InvalidLength(len) =>
146 write!(f, "witness program must be between 2 and 40 bytes: length={}", len),
147 InvalidSegwitV0Length(len) =>
148 write!(f, "a v0 witness program must be either 20 or 32 bytes: length={}", len),
149 }
150 }
151}
152
153#[cfg(feature = "std")]
154impl std::error::Error for Error {
155 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
156 use Error::*;
157
158 match *self {
159 InvalidLength(_) | InvalidSegwitV0Length(_) => None,
160 }
161 }
162}