1use std::cmp::Ordering;
24use std::collections::BTreeSet;
25use std::fmt::{Debug, Display};
26use std::hash::Hash;
27use std::iter;
28use std::num::ParseIntError;
29use std::str::FromStr;
30
31use amplify::Wrapper;
32use bc::{
33 CompressedPk, ControlBlock, InternalPk, LeafScript, LegacyPk, PubkeyHash, RedeemScript,
34 ScriptPubkey, TapNodeHash, WitnessScript, XOnlyPk,
35};
36use indexmap::IndexMap;
37
38use crate::{
39 Address, AddressNetwork, AddressParseError, ControlBlockFactory, DerivationIndex, IdxBase,
40 IndexParseError, NormalIndex, TapTree, XpubAccount, XpubDerivable,
41};
42
43#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug, Display, From)]
44#[wrapper(FromStr)]
45#[display(inner)]
46pub struct Keychain(u8);
47
48impl From<Keychain> for NormalIndex {
49 #[inline]
50 fn from(keychain: Keychain) -> Self { NormalIndex::from(keychain.0) }
51}
52
53impl From<Keychain> for DerivationIndex {
54 #[inline]
55 fn from(keychain: Keychain) -> Self { DerivationIndex::Normal(keychain.into()) }
56}
57
58impl Keychain {
59 pub const OUTER: Self = Keychain(0);
60 pub const INNER: Self = Keychain(1);
61
62 pub const fn with(idx: u8) -> Self { Keychain(idx) }
63}
64
65impl IdxBase for Keychain {
66 #[inline]
67 fn is_hardened(&self) -> bool { false }
68
69 #[inline]
70 fn child_number(&self) -> u32 { self.0 as u32 }
71
72 #[inline]
73 fn index(&self) -> u32 { self.0 as u32 }
74}
75
76#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)]
77#[display("&{keychain}/{index}")]
78pub struct Terminal {
79 pub keychain: Keychain,
80 pub index: NormalIndex,
81}
82
83impl Terminal {
84 pub fn new(keychain: impl Into<Keychain>, index: NormalIndex) -> Self {
85 Terminal {
86 keychain: keychain.into(),
87 index,
88 }
89 }
90 pub fn change(index: NormalIndex) -> Self { Self::new(1, index) }
91}
92
93#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
94#[display(doc_comments)]
95pub enum TerminalParseError {
96 NoKeychain,
98
99 #[from]
101 InvalidKeychain(ParseIntError),
102
103 #[from]
104 Index(IndexParseError),
105
106 InvalidComponents(String),
109}
110
111impl FromStr for Terminal {
112 type Err = TerminalParseError;
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 let mut iter = s.split('/');
116 match (iter.next(), iter.next(), iter.next()) {
117 (Some(keychain), Some(index), None) => {
118 if !keychain.starts_with('&') {
119 return Err(TerminalParseError::NoKeychain);
120 }
121 Ok(Terminal::new(
122 Keychain::from_str(keychain.trim_start_matches('&'))?,
123 index.parse()?,
124 ))
125 }
126 _ => Err(TerminalParseError::InvalidComponents(s.to_owned())),
127 }
128 }
129}
130
131#[cfg(feature = "serde")]
132mod _serde {
133 use serde::de::Error;
134 use serde::{Deserialize, Deserializer, Serialize, Serializer};
135
136 use super::*;
137
138 impl Serialize for Keychain {
139 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140 where S: Serializer {
141 if serializer.is_human_readable() {
142 self.0.to_string().serialize(serializer)
143 } else {
144 self.0.serialize(serializer)
145 }
146 }
147 }
148
149 impl<'de> Deserialize<'de> for Keychain {
150 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151 where D: Deserializer<'de> {
152 if deserializer.is_human_readable() {
153 let s = String::deserialize(deserializer)?;
154 Self::from_str(&s).map_err(D::Error::custom)
155 } else {
156 Ok(Self(u8::deserialize(deserializer)?))
157 }
158 }
159 }
160
161 impl Serialize for Terminal {
162 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
163 where S: Serializer {
164 if serializer.is_human_readable() {
165 self.to_string().serialize(serializer)
166 } else {
167 let tuple = (self.keychain, self.index);
168 tuple.serialize(serializer)
169 }
170 }
171 }
172
173 impl<'de> Deserialize<'de> for Terminal {
174 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
175 where D: Deserializer<'de> {
176 if deserializer.is_human_readable() {
177 let s = String::deserialize(deserializer)?;
178 Self::from_str(&s).map_err(D::Error::custom)
179 } else {
180 let d = <(Keychain, NormalIndex)>::deserialize(deserializer)?;
181 Ok(Self {
182 keychain: d.0,
183 index: d.1,
184 })
185 }
186 }
187 }
188}
189
190#[derive(Clone, Eq, PartialEq, Hash, Debug)]
191#[non_exhaustive]
192pub enum DerivedScript {
193 Bare(ScriptPubkey),
194 Bip13(RedeemScript),
195 Segwit(WitnessScript),
196 NestedKey(CompressedPk),
197 NestedScript(WitnessScript),
198 TaprootKeyOnly(InternalPk),
199 TaprootScript(InternalPk, TapTree),
200}
201
202impl DerivedScript {
203 pub fn to_script_pubkey(&self) -> ScriptPubkey {
204 match self {
205 DerivedScript::Bare(script_pubkey) => script_pubkey.clone(),
206 DerivedScript::Bip13(redeem_script) => redeem_script.to_script_pubkey(),
207 DerivedScript::Segwit(witness_script) => witness_script.to_script_pubkey(),
208 DerivedScript::NestedKey(_) => self
209 .to_redeem_script()
210 .expect("redeedm script must be defined for ShWpkh")
211 .to_script_pubkey(),
212
213 DerivedScript::NestedScript(witness_script) => {
214 witness_script.to_redeem_script().to_script_pubkey()
215 }
216 DerivedScript::TaprootKeyOnly(internal_key) => {
217 ScriptPubkey::p2tr_key_only(*internal_key)
218 }
219 DerivedScript::TaprootScript(internal_pk, tap_tree) => {
220 internal_pk.to_output_pk(Some(tap_tree.merkle_root())).0.to_script_pubkey()
221 }
222 }
223 }
224
225 pub fn to_redeem_script(&self) -> Option<RedeemScript> {
226 match self {
227 DerivedScript::Bare(_) => None,
228 DerivedScript::Bip13(redeem_script) => Some(redeem_script.clone()),
229 DerivedScript::Segwit(_) => None,
230 DerivedScript::NestedKey(pk) => Some(RedeemScript::from_checked(
231 ScriptPubkey::p2pkh(PubkeyHash::from(*pk)).into_inner().into_vec(),
232 )),
233 DerivedScript::NestedScript(witness_script) => Some(witness_script.to_redeem_script()),
234 DerivedScript::TaprootKeyOnly(_) => None,
235 DerivedScript::TaprootScript(_, _) => None,
236 }
237 }
238 pub fn as_witness_script(&self) -> Option<&WitnessScript> {
239 match self {
240 DerivedScript::Bare(_) => None,
241 DerivedScript::Bip13(_) => None,
242 DerivedScript::NestedKey(_) => None,
243 DerivedScript::Segwit(witness_script) | DerivedScript::NestedScript(witness_script) => {
244 Some(witness_script)
245 }
246 DerivedScript::TaprootKeyOnly(_) => None,
247 DerivedScript::TaprootScript(_, _) => None,
248 }
249 }
250 pub fn to_witness_script(&self) -> Option<WitnessScript> { self.as_witness_script().cloned() }
251
252 pub fn to_internal_pk(&self) -> Option<InternalPk> {
253 match self {
254 DerivedScript::Bare(_)
255 | DerivedScript::Bip13(_)
256 | DerivedScript::Segwit(_)
257 | DerivedScript::NestedKey(_)
258 | DerivedScript::NestedScript(_) => None,
259 DerivedScript::TaprootKeyOnly(internal_key) => Some(*internal_key),
260 DerivedScript::TaprootScript(internal_key, _) => Some(*internal_key),
261 }
262 }
263
264 pub fn as_tap_tree(&self) -> Option<&TapTree> {
265 match self {
266 DerivedScript::Bare(_)
267 | DerivedScript::Bip13(_)
268 | DerivedScript::Segwit(_)
269 | DerivedScript::NestedKey(_)
270 | DerivedScript::NestedScript(_)
271 | DerivedScript::TaprootKeyOnly(_) => None,
272 DerivedScript::TaprootScript(_, tap_tree) => Some(tap_tree),
273 }
274 }
275
276 pub fn to_tap_tree(&self) -> Option<TapTree> { self.as_tap_tree().cloned() }
277
278 pub fn to_leaf_scripts(&self) -> IndexMap<ControlBlock, LeafScript> {
279 let (Some(internal_pk), Some(tap_tree)) = (self.to_internal_pk(), self.to_tap_tree())
280 else {
281 return empty!();
282 };
283 ControlBlockFactory::with(internal_pk, tap_tree).collect()
284 }
285
286 #[inline]
287 pub fn to_tap_root(&self) -> Option<TapNodeHash> {
288 self.to_tap_tree().as_ref().map(TapTree::merkle_root)
289 }
290}
291
292#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)]
293#[cfg_attr(
294 feature = "serde",
295 derive(serde::Serialize, serde::Deserialize),
296 serde(rename_all = "camelCase")
297)]
298#[display("{addr}{terminal}")]
299pub struct DerivedAddr {
300 pub addr: Address,
301 pub terminal: Terminal,
302}
303
304impl Ord for DerivedAddr {
305 fn cmp(&self, other: &Self) -> Ordering { self.terminal.cmp(&other.terminal) }
306}
307
308impl PartialOrd for DerivedAddr {
309 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
310}
311
312impl DerivedAddr {
313 pub fn new(addr: Address, keychain: Keychain, index: NormalIndex) -> Self {
314 DerivedAddr {
315 addr,
316 terminal: Terminal::new(keychain, index),
317 }
318 }
319}
320
321#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
322#[display(inner)]
323pub enum DerivedAddrParseError {
324 #[display("address must be followed by a & and derivation information")]
325 NoSeparator,
326
327 #[from]
328 Address(AddressParseError),
329
330 #[from]
331 Terminal(TerminalParseError),
332}
333
334impl FromStr for DerivedAddr {
335 type Err = DerivedAddrParseError;
336
337 fn from_str(s: &str) -> Result<Self, Self::Err> {
338 let pos = s.find('&').ok_or(DerivedAddrParseError::NoSeparator)?;
339 let (addr, terminal) = s.split_at(pos);
340 Ok(DerivedAddr {
341 addr: addr.parse()?,
342 terminal: terminal.parse()?,
343 })
344 }
345}
346
347pub trait Derive<D> {
348 fn default_keychain(&self) -> Keychain;
351
352 fn keychains(&self) -> BTreeSet<Keychain>;
353
354 fn derive(
355 &self,
356 keychain: impl Into<Keychain>,
357 index: impl Into<NormalIndex>,
358 ) -> impl Iterator<Item = D>;
359
360 fn derive_range(
361 &self,
362 keychain: impl Into<Keychain>,
363 from: impl Into<NormalIndex>,
364 to: impl Into<NormalIndex>,
365 ) -> impl Iterator<Item = D> {
366 let from = from.into().child_number();
367 let to = to.into().child_number();
368 let keychain = keychain.into();
369 (from..to)
370 .flat_map(move |index| self.derive(keychain, NormalIndex::normal_unchecked(index)))
371 }
372}
373
374pub trait DeriveKey<D>: Derive<D> + Clone + Eq + Hash + Debug + Display {
375 fn xpub_spec(&self) -> &XpubAccount;
376}
377
378pub trait DeriveLegacy: DeriveKey<LegacyPk> {}
379impl<T: DeriveKey<LegacyPk>> DeriveLegacy for T {}
380
381pub trait DeriveCompr: DeriveKey<CompressedPk> {}
382impl<T: DeriveKey<CompressedPk>> DeriveCompr for T {}
383
384pub trait DeriveXOnly: DeriveKey<XOnlyPk> {}
385impl<T: DeriveKey<XOnlyPk>> DeriveXOnly for T {}
386
387pub trait DeriveScripts: Derive<DerivedScript> {
388 fn derive_address(
393 &self,
394 network: AddressNetwork,
395 keychain: impl Into<Keychain>,
396 index: impl Into<NormalIndex>,
397 ) -> impl Iterator<Item = Address> {
398 self.derive(keychain, index)
399 .flat_map(move |spk| Address::with(&spk.to_script_pubkey(), network).ok())
400 }
401
402 fn derive_address_range(
407 &self,
408 network: AddressNetwork,
409 keychain: impl Into<Keychain>,
410 from: impl Into<NormalIndex>,
411 to: impl Into<NormalIndex>,
412 ) -> impl Iterator<Item = Address> {
413 self.derive_range(keychain, from, to)
414 .flat_map(move |spk| Address::with(&spk.to_script_pubkey(), network).ok())
415 }
416}
417impl<T: Derive<DerivedScript>> DeriveScripts for T {}
418
419impl DeriveKey<LegacyPk> for XpubDerivable {
420 fn xpub_spec(&self) -> &XpubAccount { self.spec() }
421}
422
423impl DeriveKey<CompressedPk> for XpubDerivable {
424 fn xpub_spec(&self) -> &XpubAccount { self.spec() }
425}
426
427impl DeriveKey<XOnlyPk> for XpubDerivable {
428 fn xpub_spec(&self) -> &XpubAccount { self.spec() }
429}
430
431impl Derive<LegacyPk> for XpubDerivable {
432 #[inline]
433 fn default_keychain(&self) -> Keychain { self.keychains.first() }
434
435 #[inline]
436 fn keychains(&self) -> BTreeSet<Keychain> { self.keychains.to_set() }
437
438 fn derive(
439 &self,
440 keychain: impl Into<Keychain>,
441 index: impl Into<NormalIndex>,
442 ) -> impl Iterator<Item = LegacyPk> {
443 iter::once(self.xpub().derive_pub([keychain.into().into(), index.into()]).to_legacy_pk())
444 }
445}
446
447impl Derive<CompressedPk> for XpubDerivable {
448 #[inline]
449 fn default_keychain(&self) -> Keychain { self.keychains.first() }
450
451 #[inline]
452 fn keychains(&self) -> BTreeSet<Keychain> { self.keychains.to_set() }
453
454 fn derive(
455 &self,
456 keychain: impl Into<Keychain>,
457 index: impl Into<NormalIndex>,
458 ) -> impl Iterator<Item = CompressedPk> {
459 iter::once(self.xpub().derive_pub([keychain.into().into(), index.into()]).to_compr_pk())
460 }
461}
462
463impl Derive<XOnlyPk> for XpubDerivable {
464 #[inline]
465 fn default_keychain(&self) -> Keychain { self.keychains.first() }
466
467 #[inline]
468 fn keychains(&self) -> BTreeSet<Keychain> { self.keychains.to_set() }
469
470 fn derive(
471 &self,
472 keychain: impl Into<Keychain>,
473 index: impl Into<NormalIndex>,
474 ) -> impl Iterator<Item = XOnlyPk> {
475 iter::once(self.xpub().derive_pub([keychain.into().into(), index.into()]).to_xonly_pk())
476 }
477}
478
479pub trait DeriveSet {
480 type Legacy: DeriveLegacy;
481 type Compr: DeriveCompr;
482 type XOnly: DeriveXOnly;
483}
484
485impl DeriveSet for XpubDerivable {
486 type Legacy = XpubDerivable;
487 type Compr = XpubDerivable;
488 type XOnly = XpubDerivable;
489}