1use amplify::Wrapper;
17use bitcoin::blockdata::script;
18use bitcoin::blockdata::witness::Witness;
19use bitcoin::{secp256k1, Script};
20#[cfg(feature = "miniscript")]
21use miniscript::descriptor::DescriptorType;
22#[cfg(feature = "miniscript")]
23use miniscript::{Descriptor, MiniscriptKey, ToPublicKey};
24
25use crate::{LockScript, PubkeyScript, RedeemScript, ScriptSet, SigScript, WitnessScript};
26
27#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display, Hash)]
29#[repr(u8)]
30pub enum ConvertInfo {
31 #[display("bare")]
37 Bare,
38
39 #[display("hashed")]
47 Hashed,
48
49 #[display("nested")]
63 NestedV0,
64
65 #[display("segwit")]
72 SegWitV0,
73
74 #[display("taproot")]
76 Taproot,
77}
78
79#[cfg(feature = "miniscript")]
80impl<Pk> From<&Descriptor<Pk>> for ConvertInfo
81where
82 Pk: MiniscriptKey + ToPublicKey,
83{
84 fn from(descriptor: &Descriptor<Pk>) -> Self {
85 match (descriptor.desc_type(), descriptor) {
86 (DescriptorType::Bare, _) => ConvertInfo::Bare,
87 (DescriptorType::Sh, _)
88 | (DescriptorType::ShSortedMulti, _)
89 | (DescriptorType::Pkh, _) => ConvertInfo::Hashed,
90 (DescriptorType::Wpkh, _)
91 | (DescriptorType::WshSortedMulti, _)
92 | (DescriptorType::Wsh, _) => ConvertInfo::SegWitV0,
93 (DescriptorType::ShWsh, _)
94 | (DescriptorType::ShWpkh, _)
95 | (DescriptorType::ShWshSortedMulti, _) => ConvertInfo::NestedV0,
96 (_, Descriptor::Tr(_)) => ConvertInfo::Taproot,
97 _ => unreachable!("taproot descriptor type for non-taproot descriptor"),
98 }
99 }
100}
101
102#[cfg(feature = "miniscript")]
103impl<Pk> From<Descriptor<Pk>> for ConvertInfo
104where
105 Pk: MiniscriptKey + ToPublicKey,
106{
107 #[inline]
108 fn from(descriptor: Descriptor<Pk>) -> Self { Self::from(&descriptor) }
109}
110
111impl ConvertInfo {
112 #[inline]
114 pub fn is_segwit(self) -> bool { !matches!(self, ConvertInfo::Bare | ConvertInfo::Hashed) }
115
116 #[inline]
118 pub fn is_taproot(self) -> bool { !matches!(self, ConvertInfo::Taproot { .. }) }
119}
120
121#[derive(
124 Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error
125)]
126#[display(doc_comments)]
127pub enum LockScriptError {
128 UncompressedPubkeyInWitness(bitcoin::PublicKey),
130
131 Taproot,
133}
134
135pub trait ToLockScript {
139 fn to_lock_script(&self, strategy: ConvertInfo) -> Result<LockScript, LockScriptError>;
143}
144
145pub trait ToPubkeyScript {
149 fn to_pubkey_script(&self, strategy: ConvertInfo) -> Option<PubkeyScript>;
154}
155
156pub trait ToScripts
159where
160 Self: ToPubkeyScript,
161{
162 fn to_scripts(&self, strategy: ConvertInfo) -> Option<ScriptSet> {
165 Some(ScriptSet {
166 pubkey_script: self.to_pubkey_script(strategy)?,
167 sig_script: self.to_sig_script(strategy)?,
168 witness: self.to_witness(strategy),
169 })
170 }
171
172 fn to_sig_script(&self, strategy: ConvertInfo) -> Option<SigScript>;
175
176 fn to_witness(&self, strategy: ConvertInfo) -> Option<Witness>;
179}
180
181impl ToPubkeyScript for WitnessScript {
182 fn to_pubkey_script(&self, strategy: ConvertInfo) -> Option<PubkeyScript> {
191 match strategy {
192 ConvertInfo::Bare => None,
193 ConvertInfo::Hashed => None,
194 ConvertInfo::NestedV0 => Some(RedeemScript::from(self.clone()).to_p2sh()),
195 ConvertInfo::SegWitV0 => Some(Script::new_v0_p2wsh(&self.script_hash()).into()),
196 ConvertInfo::Taproot => None,
197 }
198 }
199}
200
201impl ToPubkeyScript for RedeemScript {
202 fn to_pubkey_script(&self, strategy: ConvertInfo) -> Option<PubkeyScript> {
209 match strategy {
210 ConvertInfo::Bare => None,
211 ConvertInfo::Hashed => Some(self.to_p2sh()),
212 ConvertInfo::NestedV0 => Some(self.to_p2sh()),
213 ConvertInfo::SegWitV0 => None,
214 ConvertInfo::Taproot => None,
215 }
216 }
217}
218
219impl ToPubkeyScript for LockScript {
220 fn to_pubkey_script(&self, strategy: ConvertInfo) -> Option<PubkeyScript> {
222 Some(match strategy {
223 ConvertInfo::Bare => self.to_inner().into(),
224 ConvertInfo::Hashed => Script::new_p2sh(&self.script_hash()).into(),
225 ConvertInfo::SegWitV0 => Script::new_v0_p2wsh(&self.wscript_hash()).into(),
226 ConvertInfo::NestedV0 => WitnessScript::from(self.clone()).to_p2sh_wsh(),
227 ConvertInfo::Taproot => return None,
228 })
229 }
230}
231
232impl ToScripts for LockScript {
233 fn to_sig_script(&self, strategy: ConvertInfo) -> Option<SigScript> {
235 Some(match strategy {
236 ConvertInfo::Bare => SigScript::default(),
239 ConvertInfo::Hashed => script::Builder::new()
240 .push_slice(WitnessScript::from(self.clone()).as_bytes())
241 .into_script()
242 .into(),
243 ConvertInfo::NestedV0 => {
244 RedeemScript::from(WitnessScript::from(self.clone())).into()
248 }
249 _ => SigScript::default(),
253 })
254 }
255
256 fn to_witness(&self, strategy: ConvertInfo) -> Option<Witness> {
257 match strategy {
258 ConvertInfo::Bare | ConvertInfo::Hashed => None,
259 ConvertInfo::SegWitV0 | ConvertInfo::NestedV0 => {
260 let witness_script = WitnessScript::from(self.clone());
261 Some(Witness::from_vec(vec![witness_script.to_bytes()]))
262 }
263 ConvertInfo::Taproot => None,
264 }
265 }
266}
267
268impl ToPubkeyScript for bitcoin::PublicKey {
269 fn to_pubkey_script(&self, strategy: ConvertInfo) -> Option<PubkeyScript> {
270 match strategy {
271 ConvertInfo::Bare => Some(Script::new_p2pk(self).into()),
272 ConvertInfo::Hashed => Some(Script::new_p2pkh(&self.pubkey_hash()).into()),
273 ConvertInfo::NestedV0 | ConvertInfo::SegWitV0 if !self.compressed => None,
275 ConvertInfo::NestedV0 | ConvertInfo::SegWitV0 => self.inner.to_pubkey_script(strategy),
276 ConvertInfo::Taproot => None,
278 }
279 }
280}
281
282impl ToScripts for bitcoin::PublicKey {
283 fn to_sig_script(&self, strategy: ConvertInfo) -> Option<SigScript> {
284 Some(match strategy {
285 ConvertInfo::Bare => SigScript::default(),
288 ConvertInfo::Hashed => script::Builder::new()
289 .push_slice(&self.to_bytes())
290 .into_script()
291 .into(),
292 ConvertInfo::NestedV0 => {
293 let redeem_script =
294 LockScript::from(self.to_pubkey_script(ConvertInfo::SegWitV0)?.into_inner());
295 script::Builder::new()
296 .push_slice(redeem_script.as_bytes())
297 .into_script()
298 .into()
299 }
300 _ => SigScript::default(),
304 })
305 }
306
307 fn to_witness(&self, strategy: ConvertInfo) -> Option<Witness> {
308 match strategy {
309 ConvertInfo::Bare | ConvertInfo::Hashed => None,
310 ConvertInfo::SegWitV0 | ConvertInfo::NestedV0 => {
311 Some(Witness::from_vec(vec![self.to_bytes()]))
312 }
313 ConvertInfo::Taproot => None,
315 }
316 }
317}
318
319impl ToPubkeyScript for secp256k1::PublicKey {
320 fn to_pubkey_script(&self, strategy: ConvertInfo) -> Option<PubkeyScript> {
322 let pk = bitcoin::PublicKey::new(*self);
323 Some(
324 match strategy {
325 ConvertInfo::Bare => Script::new_p2pk(&pk),
326 ConvertInfo::Hashed => Script::new_p2pkh(&pk.pubkey_hash()),
327 ConvertInfo::SegWitV0 => Script::new_v0_p2wpkh(&pk.wpubkey_hash()?),
328 ConvertInfo::NestedV0 => {
329 let pubkey_script = Script::new_p2pkh(&pk.pubkey_hash());
330 let redeem_script = RedeemScript::from_inner(pubkey_script);
331 Script::new_p2sh(&redeem_script.script_hash())
332 }
333 ConvertInfo::Taproot => return None,
334 }
335 .into(),
336 )
337 }
338}
339
340impl ToScripts for secp256k1::PublicKey {
341 #[inline]
342 fn to_sig_script(&self, strategy: ConvertInfo) -> Option<SigScript> {
343 bitcoin::PublicKey::new(*self).to_sig_script(strategy)
344 }
345
346 #[inline]
347 fn to_witness(&self, strategy: ConvertInfo) -> Option<Witness> {
348 bitcoin::PublicKey::new(*self).to_witness(strategy)
349 }
350}
351
352pub trait ToP2pkh {
354 fn to_p2pkh(&self) -> Option<PubkeyScript>;
356 fn to_p2wpkh(&self) -> Option<PubkeyScript>;
358 fn to_p2sh_wpkh(&self) -> Option<PubkeyScript>;
360}
361
362#[cfg(feature = "miniscript")]
363impl<T> ToP2pkh for T
364where
365 T: ToPublicKey,
366{
367 fn to_p2pkh(&self) -> Option<PubkeyScript> {
368 self.to_public_key().to_pubkey_script(ConvertInfo::Hashed)
369 }
370
371 fn to_p2wpkh(&self) -> Option<PubkeyScript> {
372 self.to_public_key().to_pubkey_script(ConvertInfo::SegWitV0)
373 }
374
375 fn to_p2sh_wpkh(&self) -> Option<PubkeyScript> {
376 self.to_public_key().to_pubkey_script(ConvertInfo::NestedV0)
377 }
378}