Skip to main content

hasherkawpow_sys/
lib.rs

1//! # hasherkawpow-sys
2//!
3//! Low-level Rust bindings for KawPow hashing and verification.
4//!
5//! This crate provides two functions:
6//!
7//! - [`hash_kawpow`] — compute a KawPow hash from a header hash, nonce, and block height.
8//! - [`verify_kawpow`] — verify a previously computed KawPow hash.
9//!
10//! These functions wrap FFI calls to native implementations via `unsafe` code,
11//! but expose a safe Rust API with fixed-size arrays.
12//!
13//! ## Upstream Source
14//!
15//! The underlying C source code is from
16//! [MintPond's hasher-kawpow library](https://github.com/MintPond/hasher-kawpow),
17//! which itself adapts most of its native code from the
18//! [Ravencoin project](https://github.com/RavenProject/Ravencoin).
19//!
20//! ## Example
21//!
22//! ```
23//! use hasherkawpow_sys::{hash_kawpow, verify_kawpow};
24//!
25//! let header_hash = [0u8; 32];
26//! let nonce: u64 = 42;
27//! let block_height = 100;
28//!
29//! // Compute a hash
30//! let (mix, hash) = hash_kawpow(&header_hash, &nonce, block_height);
31//!
32//! // Verify the hash
33//! assert!(verify_kawpow(&header_hash, &nonce, block_height, &mix, &hash));
34//! ```
35
36unsafe extern "C" {
37    fn hash_one(
38        header_hash_bytes: *const u8,
39        nonce64_ptr: *const u64,
40        block_height: i32,
41        mix_out_bytes: *const u8,
42        hash_out_bytes: *const u8,
43    );
44
45    fn verify(
46        header_hash_bytes: *const u8,
47        nonce64_ptr: *const u64,
48        block_height: i32,
49        mix_out_bytes: *const u8,
50        hash_out_bytes: *const u8,
51    ) -> bool;
52}
53
54/// Computes a KawPow hash.
55///
56/// # Arguments
57///
58/// * `header_hash` - The 32-byte header hash of the block.
59/// * `nonce` - The nonce used for the hash.
60/// * `block_height` - The block height at which the hash is being computed.
61///
62/// # Returns
63///
64/// A tuple `(mix_out, hash_out)`:
65/// - `mix_out`: A 32-byte array representing the mix digest.
66/// - `hash_out`: A 32-byte array representing the final KawPow hash.
67///
68/// # Safety
69///
70/// Internally this calls into an `unsafe` FFI function (`hash_one`).
71///
72/// # Examples
73///
74/// ```
75/// use hasherkawpow_sys::hash_kawpow;
76///
77/// let header_hash = [0u8; 32];
78/// let nonce: u64 = 42;
79/// let block_height = 100;
80///
81/// let (mix, hash) = hash_kawpow(&header_hash, &nonce, block_height);
82/// assert_eq!(mix.len(), 32);
83/// assert_eq!(hash.len(), 32);
84/// ```
85pub fn hash_kawpow(header_hash: &[u8; 32], nonce: &u64, block_height: i32) -> ([u8; 32], [u8; 32]) {
86    let mut mix_out = [0u8; 32];
87    let mut hash_out = [0u8; 32];
88    unsafe {
89        hash_one(
90            header_hash.as_ptr(),
91            nonce,
92            block_height,
93            mix_out.as_mut_ptr(),
94            hash_out.as_mut_ptr(),
95        );
96    }
97    (mix_out, hash_out)
98}
99
100/// Verifies a KawPow hash result.
101///
102/// # Arguments
103///
104/// * `header_hash` - The 32-byte header hash of the block.
105/// * `nonce` - The nonce used for the hash.
106/// * `block_height` - The block height at which the hash is being verified.
107/// * `mix_out` - The 32-byte mix digest that was computed.
108/// * `hash_out` - The 32-byte final KawPow hash that was computed.
109///
110/// # Returns
111///
112/// `true` if the provided `mix_out` and `hash_out` are valid for the given
113/// inputs, otherwise `false`.
114///
115/// # Safety
116///
117/// Internally this calls into an `unsafe` FFI function (`verify`).
118///
119/// # Examples
120///
121/// ```
122/// use hasherkawpow_sys::{hash_kawpow, verify_kawpow};
123///
124/// let header_hash = [0u8; 32];
125/// let nonce: u64 = 42;
126/// let block_height = 100;
127///
128/// // Compute a hash
129/// let (mix, hash) = hash_kawpow(&header_hash, &nonce, block_height);
130///
131/// // Verify the hash
132/// let is_valid = verify_kawpow(&header_hash, &nonce, block_height, &mix, &hash);
133/// assert!(is_valid);
134/// ```
135pub fn verify_kawpow(
136    header_hash: &[u8; 32],
137    nonce: &u64,
138    block_height: i32,
139    mix_out: &[u8; 32],
140    hash_out: &[u8; 32],
141) -> bool {
142    let valid;
143    unsafe {
144        valid = verify(
145            header_hash.as_ptr(),
146            nonce,
147            block_height,
148            mix_out.as_ptr(),
149            hash_out.as_ptr(),
150        );
151    }
152
153    valid
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use std::time::Instant;
160
161    // Helper to convert a hex string to Vec<u8>
162    fn hex_to_bytes(hexstr: &str) -> Vec<u8> {
163        let mut bytes = Vec::with_capacity(hexstr.len() / 2);
164        let chars: Vec<_> = hexstr.chars().collect();
165        for i in (0..hexstr.len()).step_by(2) {
166            let byte = u8::from_str_radix(&format!("{}{}", chars[i], chars[i + 1]), 16)
167                .expect("valid hex");
168            bytes.push(byte);
169        }
170        bytes
171    }
172
173    // Helper to convert big-endian hex to little-endian u64
174    fn hex_to_le_u64(hexstr: &str) -> u64 {
175        let mut bytes = hex_to_bytes(hexstr);
176        bytes.reverse();
177        let mut arr = [0u8; 8];
178        arr.copy_from_slice(&bytes);
179        u64::from_le_bytes(arr)
180    }
181
182    // Helper to convert Vec<u8> to hex string
183    fn bytes_to_hex(bytes: &[u8]) -> String {
184        bytes
185            .iter()
186            .map(|b| format!("{:02x}", b))
187            .collect::<String>()
188    }
189
190    #[test]
191    fn test_hash_kawpow_matches_expected() {
192        let header_hash =
193            hex_to_bytes("63543d3913fe56e6720c5e61e8d208d05582875822628f483279a3e8d9c9a8b3");
194        let nonce = hex_to_le_u64("88a23b0033eb959b");
195        let block_height = 262523i32;
196        let expected_mix_hash = "89732e5ff8711c32558a308fc4b8ee77416038a70995670e3eb84cbdead2e337";
197        let expected_hash = "0000000718ba5143286c46f44eee668fdf59b8eba810df21e4e2f4ec9538fc20";
198
199        let header_hash_arr: [u8; 32] = header_hash.try_into().unwrap();
200        let (mix, hash) = hash_kawpow(&header_hash_arr, &nonce, block_height);
201
202        let mix_hex = bytes_to_hex(&mix);
203        let hash_hex = bytes_to_hex(&hash);
204
205        println!("Mix Hash: {}", mix_hex);
206        println!("Expected: {}\n", expected_mix_hash);
207        println!("Hash:     {}", hash_hex);
208        println!("Expected: {}\n", expected_hash);
209
210        assert_eq!(mix_hex, expected_mix_hash, "Got invalid mix hash");
211        assert_eq!(hash_hex, expected_hash, "Got invalid hash");
212    }
213
214    #[test]
215    fn test_verify_kawpow_matches_expected() {
216        let header_hash =
217            hex_to_bytes("63543d3913fe56e6720c5e61e8d208d05582875822628f483279a3e8d9c9a8b3");
218        let nonce = hex_to_le_u64("88a23b0033eb959b");
219        let block_height = 262523i32;
220        let expected_hash = "0000000718ba5143286c46f44eee668fdf59b8eba810df21e4e2f4ec9538fc20";
221
222        let header_hash_arr: [u8; 32] = header_hash.try_into().unwrap();
223        let (mix, hash) = hash_kawpow(&header_hash_arr, &nonce, block_height);
224
225        let valid = verify_kawpow(&header_hash_arr, &nonce, block_height, &mix, &hash);
226        assert!(valid, "Verification failed");
227        assert_eq!(
228            bytes_to_hex(&hash),
229            expected_hash,
230            "Verified hash output does not match original hash"
231        );
232    }
233
234    #[test]
235    fn test_verify_kawpow_benchmark() {
236        let header_hash =
237            hex_to_bytes("63543d3913fe56e6720c5e61e8d208d05582875822628f483279a3e8d9c9a8b3");
238        let nonce = hex_to_le_u64("88a23b0033eb959b");
239        let block_height = 262523i32;
240
241        let header_hash_arr: [u8; 32] = header_hash.try_into().unwrap();
242        let (mix, _hash) = hash_kawpow(&header_hash_arr, &nonce, block_height);
243        let hash_out_arr: [u8; 32] = [0u8; 32];
244
245        let iterations = 1000;
246        let start = Instant::now();
247        for _ in 0..iterations {
248            let valid = verify_kawpow(&header_hash_arr, &nonce, block_height, &mix, &hash_out_arr);
249            assert!(valid, "Verification failed");
250        }
251        let elapsed = start.elapsed().as_millis();
252        let verify_ps = (iterations as f64) / (elapsed as f64) * 1000.0;
253        println!("verify/sec = {}", verify_ps);
254    }
255}