kobe_primitives/derive.rs
1//! Unified derivation trait and account type.
2//!
3//! All chain-specific derivers implement [`Derive`], providing a consistent
4//! API surface across chains. [`DeriveExt`] is automatically implemented for
5//! all `Derive` types, providing batch derivation via [`derive_many`](DeriveExt::derive_many).
6
7use alloc::string::String;
8use alloc::vec::Vec;
9
10use zeroize::Zeroizing;
11
12use crate::DeriveError;
13
14/// A derived account from any chain.
15///
16/// Contains the derivation path, key material, and on-chain address.
17/// The private key is zeroized on drop.
18#[derive(Debug, Clone)]
19#[non_exhaustive]
20pub struct DerivedAccount {
21 /// BIP-32/SLIP-10 derivation path used (e.g. `m/44'/60'/0'/0/0`).
22 pub path: String,
23 /// Private key in hex (zeroized on drop).
24 pub private_key: Zeroizing<String>,
25 /// Public key in hex.
26 pub public_key: String,
27 /// On-chain address in the chain's native format.
28 pub address: String,
29}
30
31impl DerivedAccount {
32 /// Create a new derived account.
33 #[must_use]
34 pub const fn new(
35 path: String,
36 private_key: Zeroizing<String>,
37 public_key: String,
38 address: String,
39 ) -> Self {
40 Self {
41 path,
42 private_key,
43 public_key,
44 address,
45 }
46 }
47}
48
49/// Unified derivation trait implemented by all chain derivers.
50///
51/// Provides a consistent API for deriving accounts regardless of the
52/// underlying chain. Each chain crate (`kobe-evm`, `kobe-btc`, etc.)
53/// implements this trait on its `Deriver` type.
54///
55/// Batch derivation is provided by the blanket [`DeriveExt`] trait.
56///
57/// # Example
58///
59/// ```no_run
60/// use kobe_primitives::{Derive, DeriveExt, DerivedAccount};
61///
62/// fn derive_first_account<D: Derive>(d: &D) -> DerivedAccount {
63/// d.derive(0).unwrap()
64/// }
65/// ```
66pub trait Derive {
67 /// The error type returned by derivation operations.
68 type Error: core::fmt::Debug + core::fmt::Display + From<DeriveError>;
69
70 /// Derive an account at the given index using the chain's default path.
71 ///
72 /// # Errors
73 ///
74 /// Returns an error if key derivation or address encoding fails.
75 fn derive(&self, index: u32) -> Result<DerivedAccount, Self::Error>;
76
77 /// Derive an account at a custom path string.
78 ///
79 /// # Errors
80 ///
81 /// Returns an error if the path is invalid or derivation fails.
82 fn derive_path(&self, path: &str) -> Result<DerivedAccount, Self::Error>;
83}
84
85/// Extension trait providing batch derivation for all [`Derive`] implementors.
86///
87/// This trait is automatically implemented for any type implementing `Derive`.
88pub trait DeriveExt: Derive {
89 /// Derive `count` accounts starting at index `start`.
90 ///
91 /// # Errors
92 ///
93 /// Returns [`DeriveError::IndexOverflow`] if `start + count` overflows `u32`.
94 fn derive_many(&self, start: u32, count: u32) -> Result<Vec<DerivedAccount>, Self::Error> {
95 let end = start.checked_add(count).ok_or(DeriveError::IndexOverflow)?;
96 (start..end).map(|i| self.derive(i)).collect()
97 }
98}
99
100impl<T: Derive> DeriveExt for T {}