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