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 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,
|
// Macro to call methods with arbitrary amount of arguments,
|
||||||
// First argument to the macro is context,
|
// First argument to the macro is context,
|
||||||
|
@ -53,6 +53,28 @@ macro_rules! call_with_output_arg {
|
||||||
false
|
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)]
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn hash(
|
pub extern "C" fn hash(input_buffer: *const Buffer, output_buffer: *mut Buffer) -> bool {
|
||||||
ctx: *mut RLN,
|
no_ctx_call_with_output_arg!(public_hash, output_buffer, input_buffer)
|
||||||
input_buffer: *const Buffer,
|
}
|
||||||
output_buffer: *mut Buffer,
|
|
||||||
) -> bool {
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
call_with_output_arg!(ctx, hash, output_buffer, input_buffer)
|
#[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::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::poseidon_tree::PoseidonTree;
|
||||||
use crate::protocol::*;
|
use crate::protocol::*;
|
||||||
use crate::utils::*;
|
use crate::utils::*;
|
||||||
|
@ -953,14 +953,14 @@ impl RLN<'_> {
|
||||||
// We skip deserialization of the zk-proof at the beginning
|
// We skip deserialization of the zk-proof at the beginning
|
||||||
let (proof_values_1, _) = deserialize_proof_values(&serialized[128..]);
|
let (proof_values_1, _) = deserialize_proof_values(&serialized[128..]);
|
||||||
let external_nullifier_1 =
|
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();
|
let mut serialized: Vec<u8> = Vec::new();
|
||||||
input_proof_data_2.read_to_end(&mut serialized)?;
|
input_proof_data_2.read_to_end(&mut serialized)?;
|
||||||
// We skip deserialization of the zk-proof at the beginning
|
// We skip deserialization of the zk-proof at the beginning
|
||||||
let (proof_values_2, _) = deserialize_proof_values(&serialized[128..]);
|
let (proof_values_2, _) = deserialize_proof_values(&serialized[128..]);
|
||||||
let external_nullifier_2 =
|
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
|
// 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.
|
// 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(())
|
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.
|
/// 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:
|
/// 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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -3,8 +3,8 @@ mod test {
|
||||||
use ark_std::{rand::thread_rng, UniformRand};
|
use ark_std::{rand::thread_rng, UniformRand};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rln::circuit::*;
|
use rln::circuit::*;
|
||||||
use rln::ffi::*;
|
use rln::ffi::{hash as ffi_hash, poseidon_hash as ffi_poseidon_hash, *};
|
||||||
use rln::poseidon_hash::poseidon_hash;
|
use rln::poseidon_hash::{poseidon_hash as utils_poseidon_hash, ROUND_PARAMS};
|
||||||
use rln::protocol::*;
|
use rln::protocol::*;
|
||||||
use rln::public::RLN;
|
use rln::public::RLN;
|
||||||
use rln::utils::*;
|
use rln::utils::*;
|
||||||
|
@ -280,7 +280,7 @@ mod test {
|
||||||
|
|
||||||
// generate identity
|
// generate identity
|
||||||
let identity_secret_hash = hash_to_field(b"test-merkle-proof");
|
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
|
// We prepare id_commitment and we set the leaf at provided index
|
||||||
let leaf_ser = fr_to_bytes_le(&id_commitment);
|
let leaf_ser = fr_to_bytes_le(&id_commitment);
|
||||||
|
@ -1016,22 +1016,13 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
// Tests hash to field using FFI APIs
|
// Tests hash to field using FFI APIs
|
||||||
fn test_hash_to_field_ffi() {
|
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 mut rng = rand::thread_rng();
|
||||||
let signal: [u8; 32] = rng.gen();
|
let signal: [u8; 32] = rng.gen();
|
||||||
|
|
||||||
// We prepare id_commitment and we set the leaf at provided index
|
// We prepare id_commitment and we set the leaf at provided index
|
||||||
let input_buffer = &Buffer::from(signal.as_ref());
|
let input_buffer = &Buffer::from(signal.as_ref());
|
||||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
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");
|
assert!(success, "hash call failed");
|
||||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||||
|
|
||||||
|
@ -1043,4 +1034,30 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(hash1, hash2);
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use ark_std::{rand::thread_rng, UniformRand};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rln::circuit::{TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT};
|
use rln::circuit::{Fr, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT};
|
||||||
use rln::poseidon_hash::poseidon_hash;
|
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::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 rln::utils::*;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ mod test {
|
||||||
|
|
||||||
// generate identity
|
// generate identity
|
||||||
let identity_secret_hash = hash_to_field(b"test-merkle-proof");
|
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
|
// We pass id_commitment as Read buffer to RLN's set_leaf
|
||||||
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment));
|
let mut buffer = Cursor::new(fr_to_bytes_le(&id_commitment));
|
||||||
|
@ -250,15 +251,13 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hash_to_field() {
|
fn test_hash_to_field() {
|
||||||
let rln = RLN::default();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
|
||||||
let signal: [u8; 32] = rng.gen();
|
let signal: [u8; 32] = rng.gen();
|
||||||
|
|
||||||
let mut input_buffer = Cursor::new(&signal);
|
let mut input_buffer = Cursor::new(&signal);
|
||||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
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 serialized_hash = output_buffer.into_inner();
|
||||||
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
let (hash1, _) = bytes_le_to_fr(&serialized_hash);
|
||||||
|
|
||||||
|
@ -266,4 +265,24 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(hash1, hash2);
|
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