1use alloc::string::{String, ToString};
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::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)]
47pub struct Deriver<'a> {
48 wallet: &'a Wallet,
50}
51
52impl<'a> Deriver<'a> {
53 #[inline]
55 #[must_use]
56 pub const fn new(wallet: &'a Wallet) -> Self {
57 Self { wallet }
58 }
59
60 #[inline]
72 pub fn derive(&self, index: u32) -> Result<DerivedAddress, Error> {
73 self.derive_with(DerivationStyle::Standard, index)
74 }
75
76 #[allow(deprecated)]
93 pub fn derive_with(&self, style: DerivationStyle, index: u32) -> Result<DerivedAddress, Error> {
94 let derived = match style {
95 DerivationStyle::Standard => {
96 DerivedKey::derive_standard_path(self.wallet.seed(), index)?
97 }
98 DerivationStyle::Trust => DerivedKey::derive_trust_path(self.wallet.seed(), index)?,
99 DerivationStyle::LedgerLive => {
100 DerivedKey::derive_ledger_live_path(self.wallet.seed(), index)?
101 }
102 DerivationStyle::Legacy => DerivedKey::derive_legacy_path(self.wallet.seed(), index)?,
103 };
104
105 let signing_key = derived.to_signing_key();
106 let verifying_key: VerifyingKey = signing_key.verifying_key();
107 let public_key_bytes = verifying_key.as_bytes();
108
109 Ok(DerivedAddress {
110 path: style.path(index),
111 private_key_hex: Zeroizing::new(hex::encode(derived.private_key.as_slice())),
112 public_key_hex: hex::encode(public_key_bytes),
113 address: bs58::encode(public_key_bytes).into_string(),
114 })
115 }
116
117 #[inline]
128 pub fn derive_many(&self, start: u32, count: u32) -> Result<Vec<DerivedAddress>, Error> {
129 self.derive_many_with(DerivationStyle::Standard, start, count)
130 }
131
132 pub fn derive_many_with(
144 &self,
145 style: DerivationStyle,
146 start: u32,
147 count: u32,
148 ) -> Result<Vec<DerivedAddress>, Error> {
149 (start..start + count)
150 .map(|index| self.derive_with(style, index))
151 .collect()
152 }
153
154 pub fn derive_path(&self, path: &str) -> Result<DerivedAddress, Error> {
170 let derived = DerivedKey::derive_path(self.wallet.seed(), path)?;
171
172 let signing_key = derived.to_signing_key();
173 let verifying_key: VerifyingKey = signing_key.verifying_key();
174 let public_key_bytes = verifying_key.as_bytes();
175
176 Ok(DerivedAddress {
177 path: path.to_string(),
178 private_key_hex: Zeroizing::new(hex::encode(derived.private_key.as_slice())),
179 public_key_hex: hex::encode(public_key_bytes),
180 address: bs58::encode(public_key_bytes).into_string(),
181 })
182 }
183}
184
185#[cfg(test)]
186#[allow(deprecated)]
187mod tests {
188 use super::*;
189
190 fn test_wallet() -> Wallet {
191 Wallet::from_mnemonic(
192 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
193 None,
194 )
195 .unwrap()
196 }
197
198 #[test]
199 fn test_derive_address() {
200 let wallet = test_wallet();
201 let deriver = Deriver::new(&wallet);
202 let addr = deriver.derive(0).unwrap();
203
204 assert!(addr.address.len() >= 32 && addr.address.len() <= 44);
206 assert_eq!(addr.path, "m/44'/501'/0'/0'");
207 }
208
209 #[test]
210 fn test_derive_many() {
211 let wallet = test_wallet();
212 let deriver = Deriver::new(&wallet);
213 let addresses = deriver.derive_many(0, 3).unwrap();
214
215 assert_eq!(addresses.len(), 3);
216 assert_eq!(addresses[0].path, "m/44'/501'/0'/0'");
217 assert_eq!(addresses[1].path, "m/44'/501'/1'/0'");
218 assert_eq!(addresses[2].path, "m/44'/501'/2'/0'");
219
220 assert_ne!(addresses[0].address, addresses[1].address);
222 assert_ne!(addresses[1].address, addresses[2].address);
223 }
224
225 #[test]
226 fn test_deterministic_derivation() {
227 let wallet = test_wallet();
228 let deriver = Deriver::new(&wallet);
229
230 let addr1 = deriver.derive(0).unwrap();
231 let addr2 = deriver.derive(0).unwrap();
232
233 assert_eq!(addr1.address, addr2.address);
234 assert_eq!(*addr1.private_key_hex, *addr2.private_key_hex);
235 }
236
237 #[test]
238 fn test_derive_with_trust() {
239 let wallet = test_wallet();
240 let deriver = Deriver::new(&wallet);
241 let addr = deriver.derive_with(DerivationStyle::Trust, 0).unwrap();
242
243 assert_eq!(addr.path, "m/44'/501'/0'");
244 assert!(addr.address.len() >= 32 && addr.address.len() <= 44);
245 }
246
247 #[test]
248 fn test_derive_with_ledger_live() {
249 let wallet = test_wallet();
250 let deriver = Deriver::new(&wallet);
251 let addr = deriver.derive_with(DerivationStyle::LedgerLive, 0).unwrap();
252
253 assert_eq!(addr.path, "m/44'/501'/0'/0'/0'");
254 assert!(addr.address.len() >= 32 && addr.address.len() <= 44);
255 }
256
257 #[test]
258 fn test_different_styles_produce_different_addresses() {
259 let wallet = test_wallet();
260 let deriver = Deriver::new(&wallet);
261
262 let standard = deriver.derive_with(DerivationStyle::Standard, 0).unwrap();
263 let trust = deriver.derive_with(DerivationStyle::Trust, 0).unwrap();
264 let ledger_live = deriver.derive_with(DerivationStyle::LedgerLive, 0).unwrap();
265 let legacy = deriver.derive_with(DerivationStyle::Legacy, 0).unwrap();
266
267 assert_ne!(standard.address, trust.address);
269 assert_ne!(standard.address, ledger_live.address);
270 assert_ne!(standard.address, legacy.address);
271 assert_ne!(trust.address, ledger_live.address);
272 }
273}