1use alloc::string::String;
4use alloc::vec::Vec;
5use ed25519_dalek::VerifyingKey;
6use zeroize::Zeroizing;
7
8use crate::Error;
9use crate::derivation_style::DerivationStyle;
10use crate::slip10::DerivedKey;
11use kobe_core::Wallet;
12
13#[derive(Debug, Clone)]
15pub struct DerivedAddress {
16 pub path: String,
18 pub private_key_hex: Zeroizing<String>,
20 pub public_key_hex: String,
22 pub address: String,
24}
25
26#[derive(Debug)]
43pub struct Deriver<'a> {
44 wallet: &'a Wallet,
46}
47
48impl<'a> Deriver<'a> {
49 #[inline]
51 #[must_use]
52 pub const fn new(wallet: &'a Wallet) -> Self {
53 Self { wallet }
54 }
55
56 #[inline]
64 pub fn derive(&self, index: u32) -> Result<DerivedAddress, Error> {
65 self.derive_with_style(DerivationStyle::Standard, index)
66 }
67
68 #[allow(deprecated)]
74 pub fn derive_with_style(
75 &self,
76 style: DerivationStyle,
77 index: u32,
78 ) -> Result<DerivedAddress, Error> {
79 let derived = match style {
80 DerivationStyle::Standard => {
81 DerivedKey::derive_standard_path(self.wallet.seed(), index)?
82 }
83 DerivationStyle::Trust => DerivedKey::derive_trust_path(self.wallet.seed(), index)?,
84 DerivationStyle::LedgerLive => {
85 DerivedKey::derive_ledger_live_path(self.wallet.seed(), index)?
86 }
87 DerivationStyle::Legacy => DerivedKey::derive_legacy_path(self.wallet.seed(), index)?,
88 };
89
90 let signing_key = derived.to_signing_key();
91 let verifying_key: VerifyingKey = signing_key.verifying_key();
92 let public_key_bytes = verifying_key.as_bytes();
93
94 Ok(DerivedAddress {
95 path: style.path(index),
96 private_key_hex: Zeroizing::new(hex::encode(derived.private_key.as_slice())),
97 public_key_hex: hex::encode(public_key_bytes),
98 address: bs58::encode(public_key_bytes).into_string(),
99 })
100 }
101
102 pub fn derive_many(&self, start_index: u32, count: u32) -> Result<Vec<DerivedAddress>, Error> {
108 self.derive_many_with_style(DerivationStyle::Standard, start_index, count)
109 }
110
111 pub fn derive_many_with_style(
117 &self,
118 style: DerivationStyle,
119 start_index: u32,
120 count: u32,
121 ) -> Result<Vec<DerivedAddress>, Error> {
122 (start_index..start_index + count)
123 .map(|account| self.derive_with_style(style, account))
124 .collect()
125 }
126}
127
128#[cfg(test)]
129#[allow(deprecated)]
130mod tests {
131 use super::*;
132
133 fn test_wallet() -> Wallet {
134 Wallet::from_mnemonic(
135 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
136 None,
137 )
138 .unwrap()
139 }
140
141 #[test]
142 fn test_derive_address() {
143 let wallet = test_wallet();
144 let deriver = Deriver::new(&wallet);
145 let addr = deriver.derive(0).unwrap();
146
147 assert!(addr.address.len() >= 32 && addr.address.len() <= 44);
149 assert_eq!(addr.path, "m/44'/501'/0'/0'");
150 }
151
152 #[test]
153 fn test_derive_many() {
154 let wallet = test_wallet();
155 let deriver = Deriver::new(&wallet);
156 let addresses = deriver.derive_many(0, 3).unwrap();
157
158 assert_eq!(addresses.len(), 3);
159 assert_eq!(addresses[0].path, "m/44'/501'/0'/0'");
160 assert_eq!(addresses[1].path, "m/44'/501'/1'/0'");
161 assert_eq!(addresses[2].path, "m/44'/501'/2'/0'");
162
163 assert_ne!(addresses[0].address, addresses[1].address);
165 assert_ne!(addresses[1].address, addresses[2].address);
166 }
167
168 #[test]
169 fn test_deterministic_derivation() {
170 let wallet = test_wallet();
171 let deriver = Deriver::new(&wallet);
172
173 let addr1 = deriver.derive(0).unwrap();
174 let addr2 = deriver.derive(0).unwrap();
175
176 assert_eq!(addr1.address, addr2.address);
177 assert_eq!(*addr1.private_key_hex, *addr2.private_key_hex);
178 }
179
180 #[test]
181 fn test_derive_with_trust_style() {
182 let wallet = test_wallet();
183 let deriver = Deriver::new(&wallet);
184 let addr = deriver
185 .derive_with_style(DerivationStyle::Trust, 0)
186 .unwrap();
187
188 assert_eq!(addr.path, "m/44'/501'/0'");
189 assert!(addr.address.len() >= 32 && addr.address.len() <= 44);
190 }
191
192 #[test]
193 fn test_derive_with_ledger_live_style() {
194 let wallet = test_wallet();
195 let deriver = Deriver::new(&wallet);
196 let addr = deriver
197 .derive_with_style(DerivationStyle::LedgerLive, 0)
198 .unwrap();
199
200 assert_eq!(addr.path, "m/44'/501'/0'/0'/0'");
201 assert!(addr.address.len() >= 32 && addr.address.len() <= 44);
202 }
203
204 #[test]
205 fn test_different_styles_produce_different_addresses() {
206 let wallet = test_wallet();
207 let deriver = Deriver::new(&wallet);
208
209 let standard = deriver
210 .derive_with_style(DerivationStyle::Standard, 0)
211 .unwrap();
212 let trust = deriver
213 .derive_with_style(DerivationStyle::Trust, 0)
214 .unwrap();
215 let ledger_live = deriver
216 .derive_with_style(DerivationStyle::LedgerLive, 0)
217 .unwrap();
218 let legacy = deriver
219 .derive_with_style(DerivationStyle::Legacy, 0)
220 .unwrap();
221
222 assert_ne!(standard.address, trust.address);
224 assert_ne!(standard.address, ledger_live.address);
225 assert_ne!(standard.address, legacy.address);
226 assert_ne!(trust.address, ledger_live.address);
227 }
228}