#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use super::super::logic::process_governance_message;
use super::super::types::{
GovernanceAction, GovernanceEffect, GovernanceState, PublicKeyBytes,
SignedGovernanceMessage,
};
use ml_dsa::signature::SignatureEncoding as MlDsaSignatureEncoding;
use ml_dsa::signature::{Keypair, Signer};
use ml_dsa::{KeyExport, MlDsa65};
fn get_root_sk() -> ml_dsa::SigningKey<MlDsa65> {
let bytes = hex::decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
.unwrap();
ml_dsa::SigningKey::<MlDsa65>::from_seed(bytes.as_slice().try_into().unwrap())
}
fn generate_key(seed: u8) -> (ml_dsa::SigningKey<MlDsa65>, PublicKeyBytes) {
let bytes = [seed; 32];
let signing_key = ml_dsa::SigningKey::<MlDsa65>::from_seed((&bytes).into());
let verifying_key = signing_key.verifying_key().to_bytes().to_vec();
(signing_key, verifying_key)
}
fn sign_action(msg: &SignedGovernanceMessage, signer: &ml_dsa::SigningKey<MlDsa65>) -> Vec<u8> {
let serialized = msg.to_canonical_bytes();
let sig: ml_dsa::Signature<MlDsa65> = signer.sign(&serialized);
MlDsaSignatureEncoding::to_bytes(&sig).into()
}
#[test]
fn test_premium_grants() {
let root_sk = get_root_sk();
let current_time = web_time::SystemTime::now()
.duration_since(web_time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut state = GovernanceState::new(current_time);
let (_, target_pubkey) = generate_key(99);
let mut msg_invalid_len = SignedGovernanceMessage {
action: GovernanceAction::GrantPremiumName {
name: "ab".to_string(),
target_pubkey: target_pubkey.clone(),
},
timestamp_sec: current_time,
signatures: vec![],
};
msg_invalid_len
.signatures
.push(sign_action(&msg_invalid_len, &root_sk));
let err = process_governance_message(&mut state, &msg_invalid_len).unwrap_err();
assert!(
matches!(err, crate::error::GovernanceError::InvalidPremiumNameLength),
"Got error: {:?}",
err
);
for i in 0..5 {
let name = (b'a' + i) as char;
let mut msg = SignedGovernanceMessage {
action: GovernanceAction::GrantPremiumName {
name: name.to_string(),
target_pubkey: target_pubkey.clone(),
},
timestamp_sec: current_time,
signatures: vec![],
};
msg.signatures.push(sign_action(&msg, &root_sk));
let effect = process_governance_message(&mut state, &msg).unwrap();
if let Some(GovernanceEffect::PremiumNameGranted {
name: granted_name, ..
}) = effect
{
assert_eq!(granted_name, name.to_string());
} else {
panic!("Expected PremiumNameGranted");
}
}
}
#[test]
fn test_rotate_root_key() {
let root_sk = get_root_sk();
let current_time = web_time::SystemTime::now()
.duration_since(web_time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut state = GovernanceState::new(current_time);
let (new_root_sk, new_root_pubkey) = generate_key(123);
let mut rotate_msg = SignedGovernanceMessage {
action: GovernanceAction::RotateRootKey {
new_key: new_root_pubkey.clone(),
},
timestamp_sec: current_time,
signatures: vec![],
};
rotate_msg
.signatures
.push(sign_action(&rotate_msg, &root_sk));
let effect = process_governance_message(&mut state, &rotate_msg).unwrap();
assert!(matches!(
effect,
Some(GovernanceEffect::RootKeyRotated { .. })
));
assert_eq!(state.get_root_key().unwrap(), new_root_pubkey);
let mut grant_msg = SignedGovernanceMessage {
action: GovernanceAction::GrantPremiumName {
name: "b".to_string(),
target_pubkey: new_root_pubkey.clone(), },
timestamp_sec: current_time + 1, signatures: vec![],
};
grant_msg.signatures.push(sign_action(&grant_msg, &root_sk));
let err = process_governance_message(&mut state, &grant_msg).unwrap_err();
assert!(matches!(
err,
crate::error::GovernanceError::InsufficientSignatures
));
grant_msg.signatures.clear();
grant_msg
.signatures
.push(sign_action(&grant_msg, &new_root_sk));
let effect = process_governance_message(&mut state, &grant_msg).unwrap();
assert!(matches!(
effect,
Some(GovernanceEffect::PremiumNameGranted { .. })
));
}
use proptest::prelude::*;
use proptest::string::string_regex;
proptest! {
#[test]
fn test_fuzz_to_canonical_bytes(
name in string_regex("[a-z0-9_-]{1,63}").unwrap(),
timestamp in any::<u64>(),
) {
let (_, target_pubkey) = generate_key(99);
let action = GovernanceAction::GrantPremiumName {
name,
target_pubkey,
};
let msg = SignedGovernanceMessage {
action: action.clone(),
timestamp_sec: timestamp,
signatures: vec![], };
let bytes = msg.to_canonical_bytes();
prop_assert!(!bytes.is_empty());
let msg_clone = msg.clone();
prop_assert_eq!(&bytes, &msg_clone.to_canonical_bytes());
let hash = GovernanceState::hash_action(&msg);
prop_assert_eq!(hash.len(), 32);
}
}
#[test]
fn test_emergency_halt_resume() {
let (root_sk, root_pubkey) = generate_key(1);
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut state = GovernanceState::new(current_time);
state.active_root_key = Some(root_pubkey);
assert!(!state.is_halted);
assert_eq!(state.total_paused_kyns, 0);
let mut halt_msg = SignedGovernanceMessage {
action: GovernanceAction::EmergencyHalt,
timestamp_sec: current_time,
signatures: vec![],
};
halt_msg.signatures.push(sign_action(&halt_msg, &root_sk));
let effect = process_governance_message(&mut state, &halt_msg).unwrap();
assert!(matches!(effect, Some(GovernanceEffect::NetworkHalted)));
assert!(state.is_halted);
let mut resume_msg = SignedGovernanceMessage {
action: GovernanceAction::EmergencyResume {
paused_kyns: 1000,
},
timestamp_sec: current_time + 1,
signatures: vec![],
};
resume_msg
.signatures
.push(sign_action(&resume_msg, &root_sk));
let effect = process_governance_message(&mut state, &resume_msg).unwrap();
assert!(matches!(effect, Some(GovernanceEffect::NetworkResumed)));
assert!(!state.is_halted);
assert_eq!(state.total_paused_kyns, 1000);
}
#[test]
fn test_revoke_premium_name() {
let (root_sk, root_pubkey) = generate_key(1);
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut state = GovernanceState::new(current_time);
state.active_root_key = Some(root_pubkey);
let mut fail_msg = SignedGovernanceMessage {
action: GovernanceAction::RevokePremiumName {
name: "ab".to_string(),
},
timestamp_sec: current_time,
signatures: vec![],
};
fail_msg.signatures.push(sign_action(&fail_msg, &root_sk));
let err = process_governance_message(&mut state, &fail_msg).unwrap_err();
assert!(matches!(
err,
crate::error::GovernanceError::InvalidPremiumNameLength
));
let mut success_msg = SignedGovernanceMessage {
action: GovernanceAction::RevokePremiumName {
name: "a".to_string(),
},
timestamp_sec: current_time + 1,
signatures: vec![],
};
success_msg
.signatures
.push(sign_action(&success_msg, &root_sk));
let effect = process_governance_message(&mut state, &success_msg).unwrap();
assert!(matches!(
effect,
Some(GovernanceEffect::PremiumNameRevoked { .. })
));
}
}