diff --git a/src/lib.rs b/src/lib.rs index f9b68d8e3..9c364bb28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ impl OutgoingViewingKey { } /// A Sapling expanded spending key +#[derive(Clone)] struct ExpandedSpendingKey { ask: E::Fs, nsk: E::Fs, @@ -226,6 +227,7 @@ impl DiversifierKey { } /// A Sapling extended spending key +#[derive(Clone)] pub struct ExtendedSpendingKey { depth: u8, parent_fvk_tag: FVKTag, @@ -245,6 +247,29 @@ pub struct ExtendedFullViewingKey { dk: DiversifierKey, } +impl std::cmp::PartialEq for ExtendedSpendingKey { + fn eq(&self, rhs: &ExtendedSpendingKey) -> bool { + self.depth == rhs.depth + && self.parent_fvk_tag == rhs.parent_fvk_tag + && self.child_index == rhs.child_index + && self.chain_code == rhs.chain_code + && self.xsk.ask == rhs.xsk.ask + && self.xsk.nsk == rhs.xsk.nsk + && self.xsk.ovk == rhs.xsk.ovk + && self.dk == rhs.dk + } +} + +impl std::fmt::Debug for ExtendedSpendingKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!( + f, + "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})", + self.depth, self.parent_fvk_tag, self.child_index + ) + } +} + impl std::cmp::PartialEq for ExtendedFullViewingKey { fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool { self.depth == rhs.depth @@ -288,6 +313,15 @@ impl ExtendedSpendingKey { } } + /// Returns the child key corresponding to the path derived from the master key + pub fn from_path(master: &ExtendedSpendingKey, path: &[ChildIndex]) -> Self { + let mut xsk = master.clone(); + for &i in path.iter() { + xsk = xsk.derive_child(i); + } + xsk + } + pub fn derive_child(&self, i: ChildIndex) -> Self { let fvk = FullViewingKey::from_expanded_spending_key(&self.xsk, &JUBJUB); let tmp = match i { @@ -404,4 +438,25 @@ mod tests { assert!(xfvk_5h_7.is_ok()); assert_eq!(ExtendedFullViewingKey::from(&xsk_5h_7), xfvk_5h_7.unwrap()); } + + #[test] + fn path() { + let seed = [0; 32]; + let xsk_m = ExtendedSpendingKey::master(&seed); + + let xsk_5h = xsk_m.derive_child(ChildIndex::Hardened(5)); + assert_eq!( + ExtendedSpendingKey::from_path(&xsk_m, &[ChildIndex::Hardened(5)]), + xsk_5h + ); + + let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::NonHardened(7)); + assert_eq!( + ExtendedSpendingKey::from_path( + &xsk_m, + &[ChildIndex::Hardened(5), ChildIndex::NonHardened(7)] + ), + xsk_5h_7 + ); + } }