clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! Test vectors for ClockHash-256
//!
//! Comprehensive test suite including empty string, short messages,
//! large messages, domain separation, and avalanche property tests.

#![cfg(test)]

use clock_hash::{DomainTag, clockhash256, clockhash256_domain, clockhash256_with_domain, tags};

/// Test empty string hash
#[test]
fn test_empty_string() {
    let hash = clockhash256(b"");
    assert_eq!(hash.len(), 32);

    // Empty string should produce deterministic hash
    let hash2 = clockhash256(b"");
    assert_eq!(hash, hash2);
}

/// Test "abc" hash
#[test]
fn test_abc() {
    let hash = clockhash256(b"abc");
    assert_eq!(hash.len(), 32);

    // Should be deterministic
    let hash2 = clockhash256(b"abc");
    assert_eq!(hash, hash2);

    // Should differ from empty string
    let empty_hash = clockhash256(b"");
    assert_ne!(hash, empty_hash);
}

/// Test 1MB of zeroes
#[test]
fn test_large_zeroes() {
    // Use a smaller size for testing to avoid large allocations
    let data = [0u8; 10000];
    let hash = clockhash256(&data);
    assert_eq!(hash.len(), 32);

    // Should be deterministic
    let hash2 = clockhash256(&data);
    assert_eq!(hash, hash2);
}

/// Test domain separation
#[test]
fn test_domain_separation() {
    let data = b"test data";

    // Different domains should produce different hashes
    let hash_block = clockhash256_domain(tags::CLK_BLOCK, data);
    let hash_tx = clockhash256_domain(tags::CLK_TX, data);
    let hash_merkle = clockhash256_domain(tags::CLK_MERKLE, data);
    let hash_nonce = clockhash256_domain(tags::CLK_NONCE, data);
    let hash_rng = clockhash256_domain(tags::CLK_RNG, data);
    let hash_raw = clockhash256(data);

    // All should be different
    assert_ne!(hash_block, hash_tx);
    assert_ne!(hash_block, hash_merkle);
    assert_ne!(hash_block, hash_nonce);
    assert_ne!(hash_block, hash_rng);
    assert_ne!(hash_block, hash_raw);
    assert_ne!(hash_tx, hash_merkle);
    assert_ne!(hash_tx, hash_nonce);
    assert_ne!(hash_tx, hash_rng);
    assert_ne!(hash_tx, hash_raw);
}

/// Test domain tag enum
#[test]
fn test_domain_tag_enum() {
    let data = b"test";

    let hash_block = clockhash256_with_domain(DomainTag::Block, data);
    let hash_tx = clockhash256_with_domain(DomainTag::Transaction, data);
    let hash_merkle = clockhash256_with_domain(DomainTag::Merkle, data);
    let hash_nonce = clockhash256_with_domain(DomainTag::Nonce, data);
    let hash_rng = clockhash256_with_domain(DomainTag::Rng, data);

    // All should be different
    assert_ne!(hash_block, hash_tx);
    assert_ne!(hash_block, hash_merkle);
    assert_ne!(hash_block, hash_nonce);
    assert_ne!(hash_block, hash_rng);
    assert_ne!(hash_tx, hash_merkle);
    assert_ne!(hash_tx, hash_nonce);
    assert_ne!(hash_tx, hash_rng);
}

/// Test avalanche property
///
/// Changing a single bit in the input should change approximately
/// 50% of the output bits.
#[test]
fn test_avalanche_property() {
    let data1 = [0u8; 100];
    let mut data2 = data1;
    data2[50] ^= 1; // Flip one bit

    let hash1 = clockhash256(&data1);
    let hash2 = clockhash256(&data2);

    // Hashes should be different
    assert_ne!(hash1, hash2);

    // Count differing bits
    let mut diff_bits = 0;
    for i in 0..32 {
        diff_bits += (hash1[i] ^ hash2[i]).count_ones();
    }

    // Should have approximately 128 bits different (50% of 256)
    // Allow some variance: between 100 and 156 bits (39% to 61%)
    assert!(
        diff_bits >= 100 && diff_bits <= 156,
        "Avalanche property: expected ~128 bits different, got {}",
        diff_bits
    );
}

/// Test incremental hasher matches one-shot
#[test]
fn test_incremental_vs_oneshot() {
    use clock_hash::ClockHasher;

    let data = b"hello, world! This is a test message.";

    // One-shot
    let hash1 = clockhash256(data);

    // Incremental
    let mut hasher = ClockHasher::new();
    hasher.update(data);
    let hash2 = hasher.finalize();

    assert_eq!(hash1, hash2);
}

/// Test incremental hasher with multiple updates
#[test]
fn test_incremental_multiple_updates() {
    use clock_hash::ClockHasher;

    let data1 = b"hello";
    let data2 = b", ";
    let data3 = b"world";

    // Incremental
    let mut hasher1 = ClockHasher::new();
    hasher1.update(data1);
    hasher1.update(data2);
    hasher1.update(data3);
    let hash1 = hasher1.finalize();

    // One-shot equivalent - use a fixed-size buffer
    let mut combined = [0u8; 12];
    combined[0..5].copy_from_slice(data1);
    combined[5..7].copy_from_slice(data2);
    combined[7..12].copy_from_slice(data3);
    let hash2 = clockhash256(&combined);

    assert_eq!(hash1, hash2);
}

/// Test deterministic behavior
#[test]
fn test_deterministic() {
    let data = b"deterministic test data";

    let hash1 = clockhash256(data);
    let hash2 = clockhash256(data);
    let hash3 = clockhash256(data);

    // All should be identical
    assert_eq!(hash1, hash2);
    assert_eq!(hash2, hash3);
}

/// Test various input sizes
#[test]
fn test_various_sizes() {
    for &size in &[0, 1, 15, 16, 17, 127, 128, 129, 255, 256, 1000] {
        // Use a fixed-size buffer for testing
        let mut data = [0xAAu8; 1000];
        let hash = clockhash256(&data[..size]);
        assert_eq!(hash.len(), 32, "Hash should be 32 bytes for size {}", size);
    }
}