Skip to main content

rust_yespower/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3use core::ffi::{c_char, c_int};
4use core::fmt;
5
6/// Serialized pure Tidecoin block header length.
7pub const TIDECOIN_HEADER_LEN: usize = 80;
8
9/// Tidecoin yespower output length.
10pub const TIDECOIN_HASH_LEN: usize = 32;
11
12unsafe extern "C" {
13    fn yespower_hash(input: *const c_char, output: *mut c_char) -> c_int;
14}
15
16/// Error returned when the yespower backend rejects the input or fails internally.
17#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
18pub struct Error;
19
20impl fmt::Display for Error {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.write_str("Tidecoin yespower hash failed")
23    }
24}
25
26#[cfg(feature = "std")]
27impl std::error::Error for Error {}
28
29/// Computes Tidecoin yespower over a serialized pure 80-byte block header.
30///
31/// Tidecoin uses yespower 1.0 with `N = 2048`, `r = 8`, and no personalization
32/// before AuxPoW activation.
33pub fn tidecoin_hash(input: &[u8; TIDECOIN_HEADER_LEN]) -> Result<[u8; TIDECOIN_HASH_LEN], Error> {
34    let mut output = [0u8; TIDECOIN_HASH_LEN];
35    // SAFETY: the C function reads exactly 80 bytes from `input` and writes exactly 32 bytes to
36    // `output` for the fixed Tidecoin wrapper. Both pointers are valid for those lengths and do
37    // not alias mutably.
38    let result = unsafe {
39        yespower_hash(
40            input.as_ptr().cast::<c_char>(),
41            output.as_mut_ptr().cast::<c_char>(),
42        )
43    };
44
45    if result == 0 {
46        Ok(output)
47    } else {
48        Err(Error)
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_hash() {
58        let input_hex = "0000002009f42768de3cfb4e58fc56368c1477f87f60e248d7130df3fb8acd7f6208b83a72f90dd3ad8fe06c7f70d73f256f1e07185dcc217a58b9517c699226ac0297d2ad60ba61b62a021d9b7700f0";
59        let expected_output_hex =
60            "9d90c21b5a0bb9566d2999c5d703d7327ee3ac97c020d387aa2dfd0700000000";
61
62        let input_bytes: [u8; 80] = hex::decode(input_hex)
63            .expect("Decoding failed")
64            .try_into()
65            .expect("Incorrect input length");
66        let expected_output_bytes: [u8; 32] = hex::decode(expected_output_hex)
67            .expect("Decoding failed")
68            .try_into()
69            .expect("Incorrect output length");
70
71        assert_eq!(tidecoin_hash(&input_bytes).unwrap(), expected_output_bytes);
72    }
73}