clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! ClockPermute: 16-round permutation function for ClockHash-256
//!
//! Applies a series of mixing operations to the 8-word state to ensure
//! strong diffusion and nonlinearity.

use crate::constants::{P0, ROTATION_SCHEDULE};
use crate::utils::rotl64;

/// Number of permutation rounds
const ROUNDS: usize = 16;

/// Apply ClockPermute to the 8-word state (16 rounds).
///
/// Each round applies:
/// - Step A: S[i] = (S[i] + S[(i+1) mod 8]) * P0 (wrapping arithmetic)
/// - Step B: S[i] = ROTL(S[i] ⊕ S[(i+3) mod 8], R[(round+i) mod 16])
/// - Step C: Swap pairs (S[1]↔S[5], S[2]↔S[6], S[3]↔S[7])
///
/// # Arguments
///
/// * `state` - Mutable reference to 8 u64 words representing the hash state
///
/// # Panics
///
/// This function will panic if `state.len() != 8`.
#[inline]
pub fn clock_permute(state: &mut [u64; 8]) {
    for round in 0..ROUNDS {
        // Step A: Add and multiply
        for i in 0..8 {
            let next_idx = (i + 1) % 8;
            state[i] = state[i].wrapping_add(state[next_idx]).wrapping_mul(P0);
        }

        // Step B: XOR and rotate
        for i in 0..8 {
            let xor_idx = (i + 3) % 8;
            let rotation_idx = (round + i) % 16;
            let rotation = ROTATION_SCHEDULE[rotation_idx];
            state[i] = rotl64(state[i] ^ state[xor_idx], rotation);
        }

        // Step C: Cross diffusion swaps
        state.swap(1, 5);
        state.swap(2, 6);
        state.swap(3, 7);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_clock_permute_zeros() {
        let mut state = [0u64; 8];
        let original = state;
        clock_permute(&mut state);
        // All-zeros should remain all-zeros
        assert_eq!(state, original);
    }

    #[test]
    fn test_clock_permute_basic() {
        let mut state = [0u64; 8];
        state[0] = 0x1234567890ABCDEF;

        let original = state;
        clock_permute(&mut state);

        // Permutation should modify the state
        assert_ne!(state, original);
    }

    #[test]
    fn test_clock_permute_avalanche() {
        let mut state1 = [0u64; 8];
        let mut state2 = [0u64; 8];

        state1[0] = 0x0000000000000000;
        state2[0] = 0x0000000000000001; // Single bit difference

        clock_permute(&mut state1);
        clock_permute(&mut state2);

        // Should produce different results (avalanche effect)
        assert_ne!(state1, state2);
    }

    #[test]
    fn test_clock_permute_deterministic() {
        let mut state = [0u64; 8];
        state[0] = 0xDEADBEEFCAFEBABE;

        let mut state_copy = state;

        clock_permute(&mut state);
        clock_permute(&mut state_copy);

        // Same input should produce same output
        assert_eq!(state, state_copy);
    }

    #[test]
    fn test_clock_permute_full_diffusion() {
        // Test that changes in one word affect all words
        let mut state1 = [0u64; 8];
        let mut state2 = [0u64; 8];

        state1[0] = 1;
        state2[0] = 0;

        clock_permute(&mut state1);
        clock_permute(&mut state2);

        // All words should be different (full diffusion)
        for i in 0..8 {
            assert_ne!(state1[i], state2[i], "Word {} should differ", i);
        }
    }
}