use crate::domain::{
services::key_derivation,
value_objects::{EdDSAKey, NullifierKey, ViewingKey},
};
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct KeySet {
spending_key: [u8; 32],
pub viewing_key: ViewingKey,
pub nullifier_key: NullifierKey,
pub eddsa_key: EdDSAKey,
}
impl KeySet {
pub fn new(
spending_key: [u8; 32],
viewing_key: ViewingKey,
nullifier_key: NullifierKey,
eddsa_key: EdDSAKey,
) -> Self {
Self {
spending_key,
viewing_key,
nullifier_key,
eddsa_key,
}
}
pub fn from_spending_key(spending_key: [u8; 32]) -> Self {
Self {
spending_key,
viewing_key: key_derivation::derive_viewing_key_from_spending(&spending_key),
nullifier_key: key_derivation::derive_nullifier_key_from_spending(&spending_key),
eddsa_key: key_derivation::derive_eddsa_key_from_spending(&spending_key),
}
}
pub fn spending_key(&self) -> &[u8; 32] {
&self.spending_key
}
pub fn export_viewing_key(&self) -> ViewingKey {
self.viewing_key.clone()
}
pub fn matches_viewing_key(&self, vk: &ViewingKey) -> bool {
self.viewing_key == *vk
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyset_new() {
let spending = [1u8; 32];
let viewing = ViewingKey::from_bytes([2u8; 32]);
let nullifier = NullifierKey::from_bytes([3u8; 32]);
let eddsa = EdDSAKey::from_bytes([4u8; 32]);
let keyset = KeySet::new(spending, viewing.clone(), nullifier.clone(), eddsa.clone());
assert_eq!(keyset.spending_key(), &spending);
assert_eq!(keyset.viewing_key, viewing);
assert_eq!(keyset.nullifier_key, nullifier);
assert_eq!(keyset.eddsa_key, eddsa);
}
#[test]
fn test_keyset_new_zero_keys() {
let spending = [0u8; 32];
let viewing = ViewingKey::from_bytes([0u8; 32]);
let nullifier = NullifierKey::from_bytes([0u8; 32]);
let eddsa = EdDSAKey::from_bytes([0u8; 32]);
let keyset = KeySet::new(spending, viewing.clone(), nullifier.clone(), eddsa.clone());
assert_eq!(keyset.spending_key(), &[0u8; 32]);
assert_eq!(keyset.viewing_key, viewing);
}
#[test]
fn test_keyset_new_max_keys() {
let spending = [255u8; 32];
let viewing = ViewingKey::from_bytes([255u8; 32]);
let nullifier = NullifierKey::from_bytes([255u8; 32]);
let eddsa = EdDSAKey::from_bytes([255u8; 32]);
let keyset = KeySet::new(spending, viewing.clone(), nullifier.clone(), eddsa.clone());
assert_eq!(keyset.spending_key(), &[255u8; 32]);
assert_eq!(keyset.viewing_key, viewing);
}
#[test]
fn test_from_spending_key() {
let spending_key = [42u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
assert_eq!(keyset.spending_key(), &spending_key);
assert_ne!(keyset.viewing_key.as_bytes(), &[0u8; 32]);
assert_ne!(keyset.nullifier_key.as_bytes(), &[0u8; 32]);
assert_ne!(keyset.eddsa_key.as_bytes(), &[0u8; 32]);
}
#[test]
fn test_from_spending_key_deterministic() {
let spending_key = [100u8; 32];
let keyset1 = KeySet::from_spending_key(spending_key);
let keyset2 = KeySet::from_spending_key(spending_key);
assert_eq!(keyset1.viewing_key, keyset2.viewing_key);
assert_eq!(keyset1.nullifier_key, keyset2.nullifier_key);
assert_eq!(keyset1.eddsa_key, keyset2.eddsa_key);
}
#[test]
fn test_from_spending_key_different_inputs() {
let spending_key1 = [1u8; 32];
let spending_key2 = [2u8; 32];
let keyset1 = KeySet::from_spending_key(spending_key1);
let keyset2 = KeySet::from_spending_key(spending_key2);
assert_ne!(keyset1.viewing_key, keyset2.viewing_key);
assert_ne!(keyset1.nullifier_key, keyset2.nullifier_key);
assert_ne!(keyset1.eddsa_key, keyset2.eddsa_key);
}
#[test]
fn test_from_spending_key_zero() {
let spending_key = [0u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
assert_eq!(keyset.spending_key(), &[0u8; 32]);
assert_ne!(keyset.viewing_key.as_bytes(), &[0u8; 32]);
}
#[test]
fn test_from_spending_key_sequential() {
let mut spending_key = [0u8; 32];
for (i, byte) in spending_key.iter_mut().enumerate() {
*byte = i as u8;
}
let keyset = KeySet::from_spending_key(spending_key);
assert_eq!(keyset.spending_key(), &spending_key);
assert_ne!(keyset.viewing_key.as_bytes(), &[0u8; 32]);
}
#[test]
fn test_export_viewing_key() {
let spending_key = [42u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
let exported = keyset.export_viewing_key();
assert_eq!(exported, keyset.viewing_key);
}
#[test]
fn test_export_viewing_key_immutable() {
let spending_key = [10u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
let exported1 = keyset.export_viewing_key();
let exported2 = keyset.export_viewing_key();
assert_eq!(exported1, exported2);
assert_eq!(exported1, keyset.viewing_key);
}
#[test]
fn test_export_viewing_key_different_keysets() {
let keyset1 = KeySet::from_spending_key([1u8; 32]);
let keyset2 = KeySet::from_spending_key([2u8; 32]);
let vk1 = keyset1.export_viewing_key();
let vk2 = keyset2.export_viewing_key();
assert_ne!(vk1, vk2);
}
#[test]
fn test_matches_viewing_key_true() {
let spending_key = [42u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
let vk = keyset.viewing_key.clone();
assert!(keyset.matches_viewing_key(&vk));
}
#[test]
fn test_matches_viewing_key_false() {
let keyset1 = KeySet::from_spending_key([1u8; 32]);
let keyset2 = KeySet::from_spending_key([2u8; 32]);
assert!(!keyset1.matches_viewing_key(&keyset2.viewing_key));
}
#[test]
fn test_matches_viewing_key_exported() {
let spending_key = [99u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
let exported_vk = keyset.export_viewing_key();
assert!(keyset.matches_viewing_key(&exported_vk));
}
#[test]
fn test_matches_viewing_key_different() {
let keyset = KeySet::from_spending_key([10u8; 32]);
let other_vk = ViewingKey::from_bytes([99u8; 32]);
assert!(!keyset.matches_viewing_key(&other_vk));
}
#[test]
fn test_keyset_clone() {
let spending_key = [42u8; 32];
let keyset1 = KeySet::from_spending_key(spending_key);
let keyset2 = keyset1.clone();
assert_eq!(keyset1, keyset2);
assert_eq!(keyset1.spending_key(), keyset2.spending_key());
assert_eq!(keyset1.viewing_key, keyset2.viewing_key);
assert_eq!(keyset1.nullifier_key, keyset2.nullifier_key);
assert_eq!(keyset1.eddsa_key, keyset2.eddsa_key);
}
#[test]
fn test_keyset_partial_eq() {
let spending_key = [42u8; 32];
let keyset1 = KeySet::from_spending_key(spending_key);
let keyset2 = KeySet::from_spending_key(spending_key);
assert_eq!(keyset1, keyset2);
}
#[test]
fn test_keyset_not_equal() {
let keyset1 = KeySet::from_spending_key([1u8; 32]);
let keyset2 = KeySet::from_spending_key([2u8; 32]);
assert_ne!(keyset1, keyset2);
}
#[test]
fn test_full_derivation_chain() {
let spending_key = [123u8; 32];
let keyset = KeySet::from_spending_key(spending_key);
assert_eq!(keyset.spending_key(), &spending_key);
assert_ne!(keyset.viewing_key.as_bytes(), &spending_key);
assert_ne!(keyset.nullifier_key.as_bytes(), &spending_key);
assert_ne!(keyset.eddsa_key.as_bytes(), &spending_key);
assert_ne!(
keyset.viewing_key.as_bytes(),
keyset.nullifier_key.as_bytes()
);
assert_ne!(keyset.viewing_key.as_bytes(), keyset.eddsa_key.as_bytes());
assert_ne!(keyset.nullifier_key.as_bytes(), keyset.eddsa_key.as_bytes());
}
#[test]
fn test_viewing_key_workflow() {
let spending_key = [200u8; 32];
let wallet_keyset = KeySet::from_spending_key(spending_key);
let auditor_vk = wallet_keyset.export_viewing_key();
assert!(wallet_keyset.matches_viewing_key(&auditor_vk));
let other_keyset = KeySet::from_spending_key([201u8; 32]);
let other_vk = other_keyset.export_viewing_key();
assert!(!wallet_keyset.matches_viewing_key(&other_vk));
}
#[test]
fn test_keyset_reproducibility() {
let spending_key = [77u8; 32];
let keyset1 = KeySet::from_spending_key(spending_key);
let keyset2 = KeySet::from_spending_key(spending_key);
assert_eq!(keyset1, keyset2);
assert_eq!(keyset1.export_viewing_key(), keyset2.export_viewing_key());
}
#[test]
fn test_multiple_keysets_independent() {
let keys = [
KeySet::from_spending_key([1u8; 32]),
KeySet::from_spending_key([2u8; 32]),
KeySet::from_spending_key([3u8; 32]),
];
for i in 0..keys.len() {
for j in (i + 1)..keys.len() {
assert_ne!(keys[i], keys[j]);
assert_ne!(keys[i].viewing_key, keys[j].viewing_key);
assert_ne!(keys[i].nullifier_key, keys[j].nullifier_key);
assert_ne!(keys[i].eddsa_key, keys[j].eddsa_key);
}
}
}
}