mirror of https://github.com/vacp2p/zerokit.git
feat(rln): expose poseidon to ffi (#112)
This commit is contained in:
parent
e21e9954ac
commit
a6145ab201
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::slice;
|
||||
|
||||
use crate::public::RLN;
|
||||
use crate::public::{hash as public_hash, poseidon_hash as public_poseidon_hash, RLN};
|
||||
|
||||
// Macro to call methods with arbitrary amount of arguments,
|
||||
// First argument to the macro is context,
|
||||
|
@ -53,6 +53,28 @@ macro_rules! call_with_output_arg {
|
|||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Macro to call methods with arbitrary amount of arguments,
|
||||
// which are not implemented in a ctx RLN object
|
||||
// First argument is the method to call
|
||||
// Second argument is the output buffer argument
|
||||
// The remaining arguments are all other inputs to the method
|
||||
macro_rules! no_ctx_call_with_output_arg {
|
||||
($method:ident, $output_arg:expr, $( $arg:expr ),* ) => {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
if $method($($arg.process()),*, &mut output_data).is_ok() {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
} else {
|
||||
std::mem::forget(output_data);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,10 +364,12 @@ pub extern "C" fn recover_id_secret(
|
|||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn hash(
|
||||
ctx: *mut RLN,
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
) -> bool {
|
||||
call_with_output_arg!(ctx, hash, output_buffer, input_buffer)
|
||||
pub extern "C" fn hash(input_buffer: *const Buffer, output_buffer: *mut Buffer) -> bool {
|
||||
no_ctx_call_with_output_arg!(public_hash, output_buffer, input_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn poseidon_hash(input_buffer: *const Buffer, output_buffer: *mut Buffer) -> bool {
|
||||
no_ctx_call_with_output_arg!(public_poseidon_hash, output_buffer, input_buffer)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::circuit::{vk_from_raw, zkey_from_raw, Curve, Fr};
|
||||
use crate::poseidon_hash::poseidon_hash;
|
||||
use crate::poseidon_hash::poseidon_hash as utils_poseidon_hash;
|
||||
use crate::poseidon_tree::PoseidonTree;
|
||||
use crate::protocol::*;
|
||||
use crate::utils::*;
|
||||
|
@ -953,14 +953,14 @@ impl RLN<'_> {
|
|||
// We skip deserialization of the zk-proof at the beginning
|
||||
let (proof_values_1, _) = deserialize_proof_values(&serialized[128..]);
|
||||
let external_nullifier_1 =
|
||||
poseidon_hash(&[proof_values_1.epoch, proof_values_1.rln_identifier]);
|
||||
utils_poseidon_hash(&[proof_values_1.epoch, proof_values_1.rln_identifier]);
|
||||
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_proof_data_2.read_to_end(&mut serialized)?;
|
||||
// We skip deserialization of the zk-proof at the beginning
|
||||
let (proof_values_2, _) = deserialize_proof_values(&serialized[128..]);
|
||||
let external_nullifier_2 =
|
||||
poseidon_hash(&[proof_values_2.epoch, proof_values_2.rln_identifier]);
|
||||
utils_poseidon_hash(&[proof_values_2.epoch, proof_values_2.rln_identifier]);
|
||||
|
||||
// We continue only if the proof values are for the same epoch
|
||||
// The idea is that proof values that go as input to this function are verified first (with zk-proof verify), hence ensuring validity of epoch and other fields.
|
||||
|
@ -984,38 +984,6 @@ impl RLN<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Hashes an input signal to an element in the working prime field.
|
||||
///
|
||||
/// The result is computed as the Keccak256 of the input signal modulo the prime field characteristic.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the byte vector containing the input signal.
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the resulting field element (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le))
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// let signal: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&signal);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// rln.hash(&mut input_buffer, &mut output_buffer)
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // We deserialize the keygen output
|
||||
/// let field_element = deserialize_field_element(output_buffer.into_inner());
|
||||
/// ```
|
||||
pub fn hash<R: Read, W: Write>(&self, mut input_data: R, mut output_data: W) -> io::Result<()> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let hash = hash_to_field(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) populated from the identity secret, the Merkle tree index, the epoch and signal.
|
||||
///
|
||||
/// Input values are:
|
||||
|
@ -1055,6 +1023,72 @@ impl Default for RLN<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Hashes an input signal to an element in the working prime field.
|
||||
///
|
||||
/// The result is computed as the Keccak256 of the input signal modulo the prime field characteristic.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the byte vector containing the input signal.
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the resulting field element (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le))
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// let signal: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&signal);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// hash(&mut input_buffer, &mut output_buffer)
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // We deserialize the keygen output
|
||||
/// let field_element = deserialize_field_element(output_buffer.into_inner());
|
||||
/// ```
|
||||
pub fn hash<R: Read, W: Write>(mut input_data: R, mut output_data: W) -> io::Result<()> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let hash = hash_to_field(&serialized);
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hashes a set of elements to a single element in the working prime field, using Poseidon.
|
||||
///
|
||||
/// The result is computed as the Poseidon Hash of the input signal.
|
||||
///
|
||||
/// Input values are:
|
||||
/// - `input_data`: a reader for the byte vector containing the input signal.
|
||||
///
|
||||
/// Output values are:
|
||||
/// - `output_data`: a writer receiving the serialization of the resulting field element (serialization done with [`rln::utils::fr_to_bytes_le`](crate::utils::fr_to_bytes_le))
|
||||
///
|
||||
/// Example
|
||||
/// ```
|
||||
/// let data = vec![hash_to_field(b"foo")];
|
||||
/// let signal = vec_fr_to_bytes_le(&data);
|
||||
///
|
||||
/// let mut input_buffer = Cursor::new(&signal);
|
||||
/// let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
/// poseidon_hash(&mut input_buffer, &mut output_buffer)
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // We deserialize the hash output
|
||||
/// let hash_result = deserialize_field_element(output_buffer.into_inner());
|
||||
/// ```
|
||||
pub fn poseidon_hash<R: Read, W: Write>(mut input_data: R, mut output_data: W) -> io::Result<()> {
|
||||
let mut serialized: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut serialized)?;
|
||||
|
||||
let (inputs, _) = bytes_le_to_vec_fr(&serialized);
|
||||
let hash = utils_poseidon_hash(inputs.as_ref());
|
||||
output_data.write_all(&fr_to_bytes_le(&hash))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -3,8 +3,8 @@ mod test {
|
|||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use rln::circuit::*;
|
||||
use rln::ffi::*;
|
||||
use rln::poseidon_hash::poseidon_hash;
|
||||
use rln::ffi::{hash as ffi_hash, poseidon_hash as ffi_poseidon_hash, *};
|
||||
use rln::poseidon_hash::{poseidon_hash as utils_poseidon_hash, ROUND_PARAMS};
|
||||
use rln::protocol::*;
|
||||
use rln::public::RLN;
|
||||
use rln::utils::*;
|
||||
|
@ -280,7 +280,7 @@ mod test {
|
|||
|
||||
// generate identity
|
||||
let identity_secret_hash = hash_to_field(b"test-merkle-proof");
|
||||
let id_commitment = poseidon_hash(&vec![identity_secret_hash]);
|
||||
let id_commitment = utils_poseidon_hash(&vec![identity_secret_hash]);
|
||||
|
||||
// We prepare id_commitment and we set the leaf at provided index
|
||||
let leaf_ser = fr_to_bytes_le(&id_commitment);
|
||||
|
@ -1016,22 +1016,13 @@ mod test {
|
|||
#[test]
|
||||
// Tests hash to field using FFI APIs
|
||||
fn test_hash_to_field_ffi() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
// We create a RLN instance
|
||||
let mut rln_pointer = MaybeUninit::<*mut RLN>::uninit();
|
||||
let input_buffer = &Buffer::from(TEST_RESOURCES_FOLDER.as_bytes());
|
||||
let success = new(tree_height, input_buffer, rln_pointer.as_mut_ptr());
|
||||
assert!(success, "RLN object creation failed");
|
||||
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
// We prepare id_commitment and we set the leaf at provided index
|
||||
let input_buffer = &Buffer::from(signal.as_ref());
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = hash(rln_pointer, input_buffer, output_buffer.as_mut_ptr());
|
||||
let success = ffi_hash(input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "hash call failed");
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
|
||||
|
@ -1043,4 +1034,30 @@ mod test {
|
|||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// Test Poseidon hash FFI
|
||||
fn test_poseidon_hash_ffi() {
|
||||
// generate random number between 1..ROUND_PARAMS.len()
|
||||
let mut rng = thread_rng();
|
||||
let number_of_inputs = rng.gen_range(1..ROUND_PARAMS.len());
|
||||
let mut inputs = Vec::with_capacity(number_of_inputs);
|
||||
for _ in 0..number_of_inputs {
|
||||
inputs.push(Fr::rand(&mut rng));
|
||||
}
|
||||
let inputs_ser = vec_fr_to_bytes_le(&inputs);
|
||||
let input_buffer = &Buffer::from(inputs_ser.as_ref());
|
||||
|
||||
let expected_hash = utils_poseidon_hash(inputs.as_ref());
|
||||
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = ffi_poseidon_hash(input_buffer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "poseidon hash call failed");
|
||||
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
let result_data = <&[u8]>::from(&output_buffer).to_vec();
|
||||
let (received_hash, _) = bytes_le_to_fr(&result_data);
|
||||
|
||||
assert_eq!(received_hash, expected_hash);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use rand::Rng;
|
||||
use rln::circuit::{TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT};
|
||||
use rln::poseidon_hash::poseidon_hash;
|
||||
use rln::circuit::{Fr, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT};
|
||||
use rln::poseidon_hash::{poseidon_hash as utils_poseidon_hash, ROUND_PARAMS};
|
||||
use rln::protocol::{compute_tree_root, deserialize_identity_tuple, hash_to_field};
|
||||
use rln::public::RLN;
|
||||
use rln::public::{hash as public_hash, poseidon_hash as public_poseidon_hash, RLN};
|
||||
use rln::utils::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
|
@ -19,7 +20,7 @@ mod test {
|
|||
|
||||
// generate identity
|
||||
let identity_secret_hash = hash_to_field(b"test-merkle-proof");
|
||||
let id_commitment = poseidon_hash(&vec![identity_secret_hash]);
|
||||
let id_commitment = utils_poseidon_hash(&vec![identity_secret_hash]);
|
||||
|
||||
// We pass id_commitment as Read buffer to RLN's set_leaf
|
||||
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment));
|
||||
|
@ -250,15 +251,13 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_hash_to_field() {
|
||||
let rln = RLN::default();
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = thread_rng();
|
||||
let signal: [u8; 32] = rng.gen();
|
||||
|
||||
let mut input_buffer = Cursor::new(&signal);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
rln.hash(&mut input_buffer, &mut output_buffer).unwrap();
|
||||
public_hash(&mut input_buffer, &mut output_buffer).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
|
@ -266,4 +265,24 @@ mod test {
|
|||
|
||||
assert_eq!(hash1, hash2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_poseidon_hash() {
|
||||
let mut rng = thread_rng();
|
||||
let number_of_inputs = rng.gen_range(1..ROUND_PARAMS.len());
|
||||
let mut inputs = Vec::with_capacity(number_of_inputs);
|
||||
for _ in 0..number_of_inputs {
|
||||
inputs.push(Fr::rand(&mut rng));
|
||||
}
|
||||
let expected_hash = utils_poseidon_hash(&inputs);
|
||||
|
||||
let mut input_buffer = Cursor::new(vec_fr_to_bytes_le(&inputs));
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
|
||||
public_poseidon_hash(&mut input_buffer, &mut output_buffer).unwrap();
|
||||
let serialized_hash = output_buffer.into_inner();
|
||||
let (hash, _) = bytes_le_to_fr(&serialized_hash);
|
||||
|
||||
assert_eq!(hash, expected_hash);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue