1use std::default::Default;
20use std::io::Cursor;
21use std::{error, fmt};
22use std::str::FromStr;
23#[cfg(feature = "serde")] use serde;
24
25use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
26use bitcoin_hashes::{hash160, sha512, Hash, HashEngine, Hmac, HmacEngine};
27use secp256k1::key::{PublicKey, SecretKey};
28use secp256k1::{self, Secp256k1};
29
30use network::constants::Network;
31use util::base58;
32
33pub struct ChainCode([u8; 32]);
35impl_array_newtype!(ChainCode, u8, 32);
36impl_array_newtype_show!(ChainCode);
37impl_array_newtype_encodable!(ChainCode, u8, 32);
38
39pub struct Fingerprint([u8; 4]);
41impl_array_newtype!(Fingerprint, u8, 4);
42impl_array_newtype_show!(Fingerprint);
43impl_array_newtype_encodable!(Fingerprint, u8, 4);
44
45impl Default for Fingerprint {
46 fn default() -> Fingerprint { Fingerprint([0; 4]) }
47}
48
49#[derive(Copy, Clone, PartialEq, Eq, Debug)]
51pub struct ExtendedPrivKey {
52 pub network: Network,
54 pub depth: u8,
56 pub parent_fingerprint: Fingerprint,
58 pub child_number: ChildNumber,
60 pub secret_key: SecretKey,
62 pub chain_code: ChainCode
64}
65
66#[derive(Copy, Clone, PartialEq, Eq, Debug)]
68pub struct ExtendedPubKey {
69 pub network: Network,
71 pub depth: u8,
73 pub parent_fingerprint: Fingerprint,
75 pub child_number: ChildNumber,
77 pub public_key: PublicKey,
79 pub chain_code: ChainCode
81}
82
83#[derive(Copy, Clone, PartialEq, Eq, Debug)]
85pub enum ChildNumber {
86 Normal {
88 index: u32
90 },
91 Hardened {
93 index: u32
95 },
96}
97
98impl ChildNumber {
99 pub fn from_normal_idx(index: u32) -> Self {
104 assert_eq!(index & (1 << 31), 0, "ChildNumber indices have to be within [0, 2^31 - 1], is: {}", index);
105 ChildNumber::Normal { index: index }
106 }
107
108 pub fn from_hardened_idx(index: u32) -> Self {
113 assert_eq!(index & (1 << 31), 0, "ChildNumber indices have to be within [0, 2^31 - 1], is: {}", index);
114 ChildNumber::Hardened { index: index }
115 }
116
117 pub fn is_normal(&self) -> bool {
121 !self.is_hardened()
122 }
123
124 pub fn is_hardened(&self) -> bool {
128 match *self {
129 ChildNumber::Hardened {..} => true,
130 ChildNumber::Normal {..} => false,
131 }
132 }
133
134}
135
136impl From<u32> for ChildNumber {
137 fn from(number: u32) -> Self {
138 if number & (1 << 31) != 0 {
139 ChildNumber::Hardened { index: number ^ (1 << 31) }
140 } else {
141 ChildNumber::Normal { index: number }
142 }
143 }
144}
145
146impl From<ChildNumber> for u32 {
147 fn from(cnum: ChildNumber) -> Self {
148 match cnum {
149 ChildNumber::Normal { index } => index,
150 ChildNumber::Hardened { index } => index | (1 << 31),
151 }
152 }
153}
154
155impl fmt::Display for ChildNumber {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 match *self {
158 ChildNumber::Hardened { index } => write!(f, "{}'", index),
159 ChildNumber::Normal { index } => write!(f, "{}", index),
160 }
161 }
162}
163
164#[cfg(feature = "serde")]
165impl<'de> serde::Deserialize<'de> for ChildNumber {
166 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
167 where
168 D: serde::Deserializer<'de>,
169 {
170 u32::deserialize(deserializer).map(ChildNumber::from)
171 }
172}
173
174#[cfg(feature = "serde")]
175impl serde::Serialize for ChildNumber {
176 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177 where
178 S: serde::Serializer,
179 {
180 u32::from(*self).serialize(serializer)
181 }
182}
183
184#[derive(Clone, PartialEq, Eq, Debug)]
186pub enum Error {
187 CannotDeriveFromHardenedKey,
189 Ecdsa(secp256k1::Error),
191 InvalidChildNumber(ChildNumber),
193 RngError(String)
195}
196
197impl fmt::Display for Error {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 match *self {
200 Error::CannotDeriveFromHardenedKey => f.write_str("cannot derive hardened key from public key"),
201 Error::Ecdsa(ref e) => fmt::Display::fmt(e, f),
202 Error::InvalidChildNumber(ref n) => write!(f, "child number {} is invalid", n),
203 Error::RngError(ref s) => write!(f, "rng error {}", s)
204 }
205 }
206}
207
208impl error::Error for Error {
209 fn cause(&self) -> Option<&error::Error> {
210 if let Error::Ecdsa(ref e) = *self {
211 Some(e)
212 } else {
213 None
214 }
215 }
216
217 fn description(&self) -> &str {
218 match *self {
219 Error::CannotDeriveFromHardenedKey => "cannot derive hardened key from public key",
220 Error::Ecdsa(ref e) => error::Error::description(e),
221 Error::InvalidChildNumber(_) => "child number is invalid",
222 Error::RngError(_) => "rng error"
223 }
224 }
225}
226
227impl From<secp256k1::Error> for Error {
228 fn from(e: secp256k1::Error) -> Error { Error::Ecdsa(e) }
229}
230
231impl ExtendedPrivKey {
232 pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
234 let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(b"Bitcoin seed");
235 hmac_engine.input(seed);
236 let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
237
238 Ok(ExtendedPrivKey {
239 network: network,
240 depth: 0,
241 parent_fingerprint: Default::default(),
242 child_number: ChildNumber::from_normal_idx(0),
243 secret_key: SecretKey::from_slice(&hmac_result[..32]).map_err(Error::Ecdsa)?,
244 chain_code: ChainCode::from(&hmac_result[32..]),
245 })
246 }
247
248 pub fn derive_priv<C: secp256k1::Signing>(
250 &self,
251 secp: &Secp256k1<C>,
252 cnums: &[ChildNumber],
253 ) -> Result<ExtendedPrivKey, Error> {
254 let mut sk: ExtendedPrivKey = *self;
255 for cnum in cnums {
256 sk = sk.ckd_priv(secp, *cnum)?;
257 }
258 Ok(sk)
259 }
260
261 pub fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
263 let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
264 let mut be_n = [0; 4];
265 match i {
266 ChildNumber::Normal {..} => {
267 hmac_engine.input(&PublicKey::from_secret_key(secp, &self.secret_key).serialize()[..]);
269 }
270 ChildNumber::Hardened {..} => {
271 hmac_engine.input(&[0u8]);
273 hmac_engine.input(&self.secret_key[..]);
274 }
275 }
276 BigEndian::write_u32(&mut be_n, u32::from(i));
277
278 hmac_engine.input(&be_n);
279 let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
280 let mut sk = SecretKey::from_slice(&hmac_result[..32]).map_err(Error::Ecdsa)?;
281 sk.add_assign(&self.secret_key[..]).map_err(Error::Ecdsa)?;
282
283 Ok(ExtendedPrivKey {
284 network: self.network,
285 depth: self.depth + 1,
286 parent_fingerprint: self.fingerprint(secp),
287 child_number: i,
288 secret_key: sk,
289 chain_code: ChainCode::from(&hmac_result[32..])
290 })
291 }
292
293 pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> hash160::Hash {
295 ExtendedPubKey::from_private(secp, self).identifier()
296 }
297
298 pub fn fingerprint<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> Fingerprint {
300 Fingerprint::from(&self.identifier(secp)[0..4])
301 }
302}
303
304impl ExtendedPubKey {
305 pub fn from_private<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &ExtendedPrivKey) -> ExtendedPubKey {
307 ExtendedPubKey {
308 network: sk.network,
309 depth: sk.depth,
310 parent_fingerprint: sk.parent_fingerprint,
311 child_number: sk.child_number,
312 public_key: PublicKey::from_secret_key(secp, &sk.secret_key),
313 chain_code: sk.chain_code
314 }
315 }
316
317 pub fn derive_pub<C: secp256k1::Verification>(
319 &self,
320 secp: &Secp256k1<C>,
321 cnums: &[ChildNumber],
322 ) -> Result<ExtendedPubKey, Error> {
323 let mut pk: ExtendedPubKey = *self;
324 for cnum in cnums {
325 pk = pk.ckd_pub(secp, *cnum)?
326 }
327 Ok(pk)
328 }
329
330 pub fn ckd_pub_tweak(&self, i: ChildNumber) -> Result<(SecretKey, ChainCode), Error> {
332 match i {
333 ChildNumber::Hardened {..} => {
334 Err(Error::CannotDeriveFromHardenedKey)
335 }
336 ChildNumber::Normal { index: n } => {
337 let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
338 hmac_engine.input(&self.public_key.serialize()[..]);
339 let mut be_n = [0; 4];
340 BigEndian::write_u32(&mut be_n, n);
341 hmac_engine.input(&be_n);
342
343 let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
344
345 let secret_key = SecretKey::from_slice(&hmac_result[..32])?;
346 let chain_code = ChainCode::from(&hmac_result[32..]);
347 Ok((secret_key, chain_code))
348 }
349 }
350 }
351
352 pub fn ckd_pub<C: secp256k1::Verification>(
354 &self,
355 secp: &Secp256k1<C>,
356 i: ChildNumber,
357 ) -> Result<ExtendedPubKey, Error> {
358 let (sk, chain_code) = self.ckd_pub_tweak(i)?;
359 let mut pk = self.public_key.clone();
360 pk.add_exp_assign(secp, &sk[..]).map_err(Error::Ecdsa)?;
361
362 Ok(ExtendedPubKey {
363 network: self.network,
364 depth: self.depth + 1,
365 parent_fingerprint: self.fingerprint(),
366 child_number: i,
367 public_key: pk,
368 chain_code: chain_code
369 })
370 }
371
372 pub fn identifier(&self) -> hash160::Hash {
374 hash160::Hash::hash(&self.public_key.serialize())
375 }
376
377 pub fn fingerprint(&self) -> Fingerprint {
379 Fingerprint::from(&self.identifier()[0..4])
380 }
381}
382
383impl fmt::Display for ExtendedPrivKey {
384 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
385 let mut ret = [0; 78];
386 ret[0..4].copy_from_slice(&match self.network {
387 Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
388 Network::Testnet | Network::Regtest => [0x04, 0x35, 0x83, 0x94],
389 }[..]);
390 ret[4] = self.depth as u8;
391 ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
392
393 BigEndian::write_u32(&mut ret[9..13], u32::from(self.child_number));
394
395 ret[13..45].copy_from_slice(&self.chain_code[..]);
396 ret[45] = 0;
397 ret[46..78].copy_from_slice(&self.secret_key[..]);
398 fmt.write_str(&base58::check_encode_slice(&ret[..]))
399 }
400}
401
402impl FromStr for ExtendedPrivKey {
403 type Err = base58::Error;
404
405 fn from_str(inp: &str) -> Result<ExtendedPrivKey, base58::Error> {
406 let data = base58::from_check(inp)?;
407
408 if data.len() != 78 {
409 return Err(base58::Error::InvalidLength(data.len()));
410 }
411
412 let cn_int: u32 = Cursor::new(&data[9..13]).read_u32::<BigEndian>().unwrap();
413 let child_number: ChildNumber = ChildNumber::from(cn_int);
414
415 Ok(ExtendedPrivKey {
416 network: if &data[0..4] == [0x04u8, 0x88, 0xAD, 0xE4] {
417 Network::Bitcoin
418 } else if &data[0..4] == [0x04u8, 0x35, 0x83, 0x94] {
419 Network::Testnet
420 } else {
421 return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()));
422 },
423 depth: data[4],
424 parent_fingerprint: Fingerprint::from(&data[5..9]),
425 child_number: child_number,
426 chain_code: ChainCode::from(&data[13..45]),
427 secret_key: SecretKey::from_slice(
428 &data[46..78]).map_err(|e|
429 base58::Error::Other(e.to_string()))?
430 })
431 }
432}
433
434impl fmt::Display for ExtendedPubKey {
435 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
436 let mut ret = [0; 78];
437 ret[0..4].copy_from_slice(&match self.network {
438 Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E],
439 Network::Testnet | Network::Regtest => [0x04u8, 0x35, 0x87, 0xCF],
440 }[..]);
441 ret[4] = self.depth as u8;
442 ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
443
444 BigEndian::write_u32(&mut ret[9..13], u32::from(self.child_number));
445
446 ret[13..45].copy_from_slice(&self.chain_code[..]);
447 ret[45..78].copy_from_slice(&self.public_key.serialize()[..]);
448 fmt.write_str(&base58::check_encode_slice(&ret[..]))
449 }
450}
451
452impl FromStr for ExtendedPubKey {
453 type Err = base58::Error;
454
455 fn from_str(inp: &str) -> Result<ExtendedPubKey, base58::Error> {
456 let data = base58::from_check(inp)?;
457
458 if data.len() != 78 {
459 return Err(base58::Error::InvalidLength(data.len()));
460 }
461
462 let cn_int: u32 = Cursor::new(&data[9..13]).read_u32::<BigEndian>().unwrap();
463 let child_number: ChildNumber = ChildNumber::from(cn_int);
464
465 Ok(ExtendedPubKey {
466 network: if &data[0..4] == [0x04u8, 0x88, 0xB2, 0x1E] {
467 Network::Bitcoin
468 } else if &data[0..4] == [0x04u8, 0x35, 0x87, 0xCF] {
469 Network::Testnet
470 } else {
471 return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()));
472 },
473 depth: data[4],
474 parent_fingerprint: Fingerprint::from(&data[5..9]),
475 child_number: child_number,
476 chain_code: ChainCode::from(&data[13..45]),
477 public_key: PublicKey::from_slice(
478 &data[45..78]).map_err(|e|
479 base58::Error::Other(e.to_string()))?
480 })
481 }
482}
483
484#[cfg(test)]
485mod tests {
486 use std::str::FromStr;
487 use std::string::ToString;
488
489 use secp256k1::{self, Secp256k1};
490 use hex::decode as hex_decode;
491
492 use network::constants::Network::{self, Bitcoin};
493
494 use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
495 use super::ChildNumber::{Hardened, Normal};
496 use super::Error;
497
498 fn test_path<C: secp256k1::Signing + secp256k1::Verification>(secp: &Secp256k1<C>,
499 network: Network,
500 seed: &[u8],
501 path: &[ChildNumber],
502 expected_sk: &str,
503 expected_pk: &str) {
504
505 let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap();
506 let mut pk = ExtendedPubKey::from_private(secp, &sk);
507
508 assert_eq!(
510 &sk.derive_priv(secp, path).unwrap().to_string()[..],
511 expected_sk
512 );
513
514 if path.iter().any(|cnum| cnum.is_hardened()) {
517 assert_eq!(
518 pk.derive_pub(secp, path),
519 Err(Error::CannotDeriveFromHardenedKey)
520 );
521 } else {
522 assert_eq!(
523 &pk.derive_pub(secp, path).unwrap().to_string()[..],
524 expected_pk
525 );
526 }
527
528 for &num in path.iter() {
530 sk = sk.ckd_priv(secp, num).unwrap();
531 match num {
532 Normal {..} => {
533 let pk2 = pk.ckd_pub(secp, num).unwrap();
534 pk = ExtendedPubKey::from_private(secp, &sk);
535 assert_eq!(pk, pk2);
536 }
537 Hardened {..} => {
538 assert_eq!(
539 pk.ckd_pub(secp, num),
540 Err(Error::CannotDeriveFromHardenedKey)
541 );
542 pk = ExtendedPubKey::from_private(secp, &sk);
543 }
544 }
545 }
546
547 assert_eq!(&sk.to_string()[..], expected_sk);
549 assert_eq!(&pk.to_string()[..], expected_pk);
550 let decoded_sk = ExtendedPrivKey::from_str(expected_sk);
552 let decoded_pk = ExtendedPubKey::from_str(expected_pk);
553 assert_eq!(Ok(sk), decoded_sk);
554 assert_eq!(Ok(pk), decoded_pk);
555 }
556
557 #[test]
558 fn test_vector_1() {
559 let secp = Secp256k1::new();
560 let seed = hex_decode("000102030405060708090a0b0c0d0e0f").unwrap();
561
562 test_path(&secp, Bitcoin, &seed, &[],
564 "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
565 "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
566
567 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_hardened_idx(0)],
569 "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
570 "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
571
572 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1)],
574 "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
575 "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
576
577 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2)],
579 "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
580 "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
581
582 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2), ChildNumber::from_normal_idx(2)],
584 "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
585 "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
586
587 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2), ChildNumber::from_normal_idx(2), ChildNumber::from_normal_idx(1000000000)],
589 "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
590 "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
591 }
592
593 #[test]
594 fn test_vector_2() {
595 let secp = Secp256k1::new();
596 let seed = hex_decode("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542").unwrap();
597
598 test_path(&secp, Bitcoin, &seed, &[],
600 "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
601 "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
602
603 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_normal_idx(0)],
605 "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
606 "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
607
608 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647)],
610 "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
611 "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
612
613 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647), ChildNumber::from_normal_idx(1)],
615 "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
616 "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
617
618 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2147483646)],
620 "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
621 "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
622
623 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2147483646), ChildNumber::from_normal_idx(2)],
625 "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
626 "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
627 }
628
629 #[test]
630 fn test_vector_3() {
631 let secp = Secp256k1::new();
632 let seed = hex_decode("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be").unwrap();
633
634 test_path(&secp, Bitcoin, &seed, &[],
636 "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
637 "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13");
638
639 test_path(&secp, Bitcoin, &seed, &[ChildNumber::from_hardened_idx(0)],
641 "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
642 "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y");
643
644 }
645
646 #[test]
647 #[cfg(all(feature = "serde", feature = "strason"))]
648 pub fn encode_decode_childnumber() {
649 serde_round_trip!(ChildNumber::from_normal_idx(0));
650 serde_round_trip!(ChildNumber::from_normal_idx(1));
651 serde_round_trip!(ChildNumber::from_normal_idx((1 << 31) - 1));
652 serde_round_trip!(ChildNumber::from_hardened_idx(0));
653 serde_round_trip!(ChildNumber::from_hardened_idx(1));
654 serde_round_trip!(ChildNumber::from_hardened_idx((1 << 31) - 1));
655 }
656}
657