use std::str::FromStr;
use distributed_topic_tracker::{
DefaultSecretRotation, EncryptedRecord, GossipRecordContent, Record, RecordTopic,
RotationHandle, encryption_keypair, salt, signing_keypair, unix_minute,
};
use mainline::SigningKey;
#[test]
fn test_record_serialization_roundtrip() {
let signing_key = SigningKey::generate(&mut rand::rng());
let topic = [1u8; 32];
let unix_minute = 12345u64;
let node_id = [2u8; 32];
let active_peers = [[3u8; 32]; 5];
let last_message_hashes = [[4u8; 32]; 5];
let record_content = GossipRecordContent {
active_peers,
last_message_hashes,
};
let record = Record::sign(
topic,
unix_minute,
node_id,
record_content.clone(),
&signing_key,
)
.expect("Failed to sign record");
let bytes = record.to_bytes();
let deserialized = Record::from_bytes(bytes).expect("Failed to deserialize record");
let deserialized_content: GossipRecordContent =
deserialized.content().expect("Failed to get content");
assert_eq!(record.topic(), deserialized.topic());
assert_eq!(record.unix_minute(), deserialized.unix_minute());
assert_eq!(record.node_id(), deserialized.node_id());
assert_eq!(
record_content.active_peers,
deserialized_content.active_peers
);
assert_eq!(
record_content.last_message_hashes,
deserialized_content.last_message_hashes
);
assert_eq!(record.signature(), deserialized.signature());
}
#[test]
fn test_record_verification() {
let signing_key = SigningKey::generate(&mut rand::rng());
let topic = [1u8; 32];
let unix_minute = 12345u64;
let node_id = signing_key.verifying_key().to_bytes();
let active_peers = [[3u8; 32]; 5];
let last_message_hashes = [[4u8; 32]; 5];
let record_content = GossipRecordContent {
active_peers,
last_message_hashes,
};
let record = Record::sign(topic, unix_minute, node_id, record_content, &signing_key).unwrap();
assert!(record.verify(&topic, unix_minute).is_ok());
let wrong_topic = [99u8; 32];
assert!(record.verify(&wrong_topic, unix_minute).is_err());
assert!(record.verify(&topic, unix_minute + 1).is_err());
}
#[test]
fn test_encrypted_record_roundtrip() {
let signing_key = SigningKey::generate(&mut rand::rng());
let encryption_key = SigningKey::generate(&mut rand::rng());
let topic = [1u8; 32];
let unix_minute = 12345u64;
let node_id = signing_key.verifying_key().to_bytes();
let active_peers = [[3u8; 32]; 5];
let last_message_hashes = [[4u8; 32]; 5];
let record_content = GossipRecordContent {
active_peers,
last_message_hashes,
};
let record = Record::sign(
topic,
unix_minute,
node_id,
record_content.clone(),
&signing_key,
)
.expect("Failed to sign record");
let encrypted = record.encrypt(&encryption_key);
let decrypted = encrypted.decrypt(&encryption_key).unwrap();
let deserialized_content: GossipRecordContent =
decrypted.content().expect("Failed to get content");
assert_eq!(record.topic(), decrypted.topic());
assert_eq!(record.unix_minute(), decrypted.unix_minute());
assert_eq!(record.node_id(), decrypted.node_id());
assert_eq!(
record_content.active_peers,
deserialized_content.active_peers
);
assert_eq!(
record_content.last_message_hashes,
deserialized_content.last_message_hashes
);
assert_eq!(record.signature(), decrypted.signature());
}
#[test]
fn test_encrypted_record_serialization() {
let signing_key = SigningKey::generate(&mut rand::rng());
let encryption_key = SigningKey::generate(&mut rand::rng());
let topic = [1u8; 32];
let unix_minute = 12345u64;
let node_id = signing_key.verifying_key().to_bytes();
let active_peers = [[3u8; 32]; 5];
let last_message_hashes = [[4u8; 32]; 5];
let record_content = GossipRecordContent {
active_peers,
last_message_hashes,
};
let record = Record::sign(topic, unix_minute, node_id, record_content, &signing_key)
.expect("Failed to sign record");
let encrypted = record.encrypt(&encryption_key);
let bytes = encrypted.to_bytes();
let deserialized =
EncryptedRecord::from_bytes(bytes).expect("Failed to deserialize encrypted record");
let decrypted = deserialized
.decrypt(&encryption_key)
.expect("Failed to decrypt record");
assert_eq!(record.topic(), decrypted.topic());
assert_eq!(record.unix_minute(), decrypted.unix_minute());
}
#[test]
fn test_default_secret_rotation() {
let rotation = RotationHandle::new(DefaultSecretRotation);
let topic_hash = [1u8; 32];
let unix_minute = 12345u64;
let initial_secret_hash = [2u8; 32];
let secret1 = rotation.derive(topic_hash, unix_minute, initial_secret_hash);
let secret2 = rotation.derive(topic_hash, unix_minute, initial_secret_hash);
assert_eq!(secret1, secret2);
let secret3 = rotation.derive(topic_hash, unix_minute + 1, initial_secret_hash);
assert_ne!(secret1, secret3);
let different_topic = [99u8; 32];
let secret4 = rotation.derive(different_topic, unix_minute, initial_secret_hash);
assert_ne!(secret1, secret4);
}
#[test]
fn test_unix_minute_function() {
let current = unix_minute(0);
let prev = unix_minute(-1);
let next = unix_minute(1);
assert_eq!(current, prev + 1);
assert_eq!(next, current + 1);
let current2 = unix_minute(0);
assert_eq!(current, current2);
}
#[test]
fn test_topic_signing_keypair_deterministic() {
let topic_id = RecordTopic::from_str("test-topic").unwrap();
let record_topic = topic_id;
let unix_minute = 12345u64;
let key1 = signing_keypair(record_topic, unix_minute);
let key2 = signing_keypair(record_topic, unix_minute);
assert_eq!(key1.to_bytes(), key2.to_bytes());
let key3 = signing_keypair(record_topic, unix_minute + 1);
assert_ne!(key1.to_bytes(), key3.to_bytes());
}
#[test]
fn test_topic_encryption_keypair_deterministic() {
let topic_id = RecordTopic::from_str("test-topic").unwrap();
let record_topic = topic_id;
let unix_minute = 12345u64;
let initial_secret_hash = [1u8; 32];
let rotation = RotationHandle::new(DefaultSecretRotation);
let key1 = encryption_keypair(record_topic, &rotation, initial_secret_hash, unix_minute);
let key2 = encryption_keypair(record_topic, &rotation, initial_secret_hash, unix_minute);
assert_eq!(key1.to_bytes(), key2.to_bytes());
let key3 = crate::encryption_keypair(
record_topic,
&rotation,
initial_secret_hash,
unix_minute + 1,
);
assert_ne!(key1.to_bytes(), key3.to_bytes());
}
#[test]
fn test_topic_salt_deterministic() {
let topic_id = RecordTopic::from_str("test-topic").unwrap();
let record_topic = topic_id;
let unix_minute = 12345u64;
let salt1 = salt(record_topic, unix_minute);
let salt2 = salt(record_topic, unix_minute);
assert_eq!(salt1, salt2);
let salt3 = salt(record_topic, unix_minute + 1);
assert_ne!(salt1, salt3);
}