use mcaptcha_pow_sha256::ConfigBuilder; use std::ffi::CStr; use std::ffi::CString; #[repr(C)] pub struct ProofOfWork { pub nonce: u64, pub result: *const libc::c_char, } impl From> for ProofOfWork { fn from(value: mcaptcha_pow_sha256::PoW) -> Self { Self { nonce: value.nonce, result: CString::new(value.result).unwrap().into_raw(), } } } #[no_mangle] pub extern "C" fn prove_work( salt: *const libc::c_char, phrase: *const libc::c_char, difficulty: u32, ) -> ProofOfWork { let (salt, phrase) = unsafe { (CStr::from_ptr(salt), CStr::from_ptr(phrase)) }; let salt = salt.to_str().unwrap(); let phrase = phrase.to_str().unwrap().to_string(); let config = ConfigBuilder::default() .salt(salt.to_string()) .build() .unwrap(); config.prove_work(&phrase, difficulty).unwrap().into() } #[no_mangle] pub extern "C" fn is_valid_proof( nonce: u64, result: *const libc::c_char, phrase: *const libc::c_char, salt: *const libc::c_char, ) -> bool { let (result, phrase, salt) = unsafe { ( CStr::from_ptr(result), CStr::from_ptr(phrase), CStr::from_ptr(salt), ) }; let result = result.to_str().unwrap().to_string(); let pow: mcaptcha_pow_sha256::PoW = mcaptcha_pow_sha256::PoWBuilder::default() .nonce(nonce) .result(result) .build() .unwrap(); let config = ConfigBuilder::default() .salt(salt.to_str().unwrap().to_string()) .build() .unwrap(); config.is_valid_proof(&pow, &phrase.to_str().unwrap().to_string()) } #[no_mangle] pub extern "C" fn is_sufficient_difficulty( nonce: u64, result: *const libc::c_char, salt: *const libc::c_char, difficulty: u32, ) -> bool { let (result, salt) = unsafe { (CStr::from_ptr(result), CStr::from_ptr(salt)) }; let result = result.to_str().unwrap().to_string(); let pow: mcaptcha_pow_sha256::PoW = mcaptcha_pow_sha256::PoWBuilder::default() .nonce(nonce) .result(result) .build() .unwrap(); let config = ConfigBuilder::default() .salt(salt.to_str().unwrap().to_string()) .build() .unwrap(); config.is_sufficient_difficulty(&pow, difficulty) } #[cfg(test)] pub mod test { use super::*; use std::ffi::CString; #[test] fn test_pow() { let salt: String = "27b7eb6be3437b7da0d9329dea9a7c76f1640cd16d39fd94b5d0dd42c763c63c".into(); let phrase: String = "b641c711c01079805a2a0888bf716b66c83419ab0c65455a49cb4d8b3e36c4ec".into(); let difficulty = 50_000; let c = mcaptcha_pow_sha256::ConfigBuilder::default() .salt(salt.clone()) .build() .unwrap(); // calculate pow let mcaptcha_work = c.prove_work(&phrase, difficulty).unwrap(); let ffi_work = super::prove_work( CString::new(salt.clone()).unwrap().into_raw(), CString::new(phrase.clone()).unwrap().into_raw(), difficulty, ); assert_eq!(ffi_work.nonce, mcaptcha_work.nonce); let res = unsafe { CStr::from_ptr(ffi_work.result) }; let res = res.to_str().unwrap(); assert_eq!(res, mcaptcha_work.result); // verify pow assert!(super::is_valid_proof( mcaptcha_work.nonce, CString::new(mcaptcha_work.result.clone()) .unwrap() .into_raw(), CString::new(phrase.clone()).unwrap().into_raw(), CString::new(salt.clone()).unwrap().into_raw(), )); assert!(!super::is_valid_proof( mcaptcha_work.nonce, CString::new(phrase.clone()).unwrap().into_raw(), CString::new(phrase.clone()).unwrap().into_raw(), CString::new(salt.clone()).unwrap().into_raw(), )); // is sufficient difficulty assert!(super::is_sufficient_difficulty( mcaptcha_work.nonce, CString::new(mcaptcha_work.result.clone()) .unwrap() .into_raw(), CString::new(salt.clone()).unwrap().into_raw(), difficulty )); // is sufficient difficulty assert!(super::is_sufficient_difficulty( mcaptcha_work.nonce, CString::new(mcaptcha_work.result.clone()) .unwrap() .into_raw(), CString::new(salt.clone()).unwrap().into_raw(), difficulty )); } }