Skip to content

Commit

Permalink
ipa-multipoint : Error handling and init no-copy in JNI (#158)
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Zamojski <[email protected]>
  • Loading branch information
thomas-quadratic authored Mar 18, 2024
1 parent e4c77f4 commit 007c808
Show file tree
Hide file tree
Showing 7 changed files with 2,918 additions and 2,680 deletions.
305 changes: 228 additions & 77 deletions ipa-multipoint/ipa_multipoint_jni/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
/*
* Copyright Besu Contributors
*
/* Copyright Besu Contributors
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
Expand All @@ -12,111 +10,264 @@
*
* SPDX-License-Identifier: Apache-2.0
*/

use std::convert::TryInto;
mod parsers;
use parsers::{parse_scalars, parse_indices, parse_commitment, parse_commitments};

use jni::objects::JClass;
use jni::sys::jbyteArray;
use jni::JNIEnv;
use once_cell::sync::Lazy;

use std::convert::TryInto;


// TODO: Use a pointer here instead. This is only being used so that the interface does not get changed.
// TODO: and bindings do not need to be modified.
pub static CONFIG: Lazy<ffi_interface::Context> = Lazy::new(ffi_interface::Context::default);


/// Commit receives a list of 32 byte scalars and returns a 32 byte scalar
/// Scalar is actually the map_to_field(commitment) because we want to reuse the commitment in parent node.
/// This is ported from rust-verkle.
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commit(
env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray,
env: JNIEnv, _class: JClass<'_>, values: jbyteArray,
) -> jbyteArray {
let input = env
.convert_byte_array(input)
.expect("Cannot convert jbyteArray to rust array");

let commitment = ffi_interface::commit_to_scalars(&CONFIG, &input).unwrap();

env.byte_array_from_slice(&commitment)
.expect("Couldn't convert to byte array")
let input = match parse_scalars(&env, values) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let commitment = match ffi_interface::commit_to_scalars(&CONFIG, &input) {
Ok(v) => v,
Err(_) => {
env.throw_new("java/lang/IllegalArgumentException",
"Could not commit to scalars.")
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let result = match env.byte_array_from_slice(&commitment) {
Ok(v) => v,
Err(_) => {
env.throw_new("java/lang/IllegalArgumentException",
"Couldn't return commitment.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}

/// Commit_root receives a list of 32 byte scalars and returns a 32 byte commitment.to_bytes()
/// This is ported from rust-verkle.
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commitRoot(
env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray,
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_commitAsCompressed(
env: JNIEnv, _class: JClass<'_>, values: jbyteArray
) -> jbyteArray {
let input = env
.convert_byte_array(input)
.expect("Cannot convert jbyteArray to rust array");

let commitment = ffi_interface::commit_to_scalars(&CONFIG, &input).unwrap();
let hash = ffi_interface::serialize_commitment(commitment);
let input = match parse_scalars(&env, values) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let commitment = match ffi_interface::commit_to_scalars(&CONFIG, &input) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", format!("{e:?}"))
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let compressed = ffi_interface::serialize_commitment(commitment);
let result = match env.byte_array_from_slice(&compressed) {
Ok(v) => v,
Err(_) => {
env.throw_new("java/lang/IllegalArgumentException",
"Couldn't return commitment.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}

env.byte_array_from_slice(&hash)
.expect("Couldn't convert to byte array")
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_updateSparse(
env: JNIEnv, _class: JClass<'_>, commitment: jbyteArray, indices: jbyteArray, old_values: jbyteArray, new_values: jbyteArray
) -> jbyteArray {
let commitment = match parse_commitment(&env, commitment) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for updateSparse commitment input.");
return std::ptr::null_mut();
}
};
let pos = match parse_indices(&env, indices) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let old = match parse_scalars(&env, old_values) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let old: Vec<ffi_interface::ScalarBytes> = old.chunks_exact(32).map(|x| {
let mut array = [0u8; 32];
array.copy_from_slice(x);
array
}).collect();
let new = match parse_scalars(&env, new_values) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let new: Vec<ffi_interface::ScalarBytes> = new.chunks_exact(32).map(|x| {
let mut array = [0u8; 32];
array.copy_from_slice(x);
array
}).collect();
let commitment = match ffi_interface::update_commitment_sparse(&CONFIG, commitment, pos, old, new) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", format!("{e:?}"))
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let result = match env.byte_array_from_slice(&commitment) {
Ok(v) => v,
Err(_) => {
env.throw_new("java/lang/IllegalArgumentException", "Couldn't return commitment.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}

#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_groupToField(
env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray,
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_compress(
env: JNIEnv, _class: JClass<'_>, commitment: jbyteArray
) -> jbyteArray {
let commitment = env
.convert_byte_array(input)
.expect("Cannot convert jbyteArray to rust array");

let commitment_bytes = commitment.try_into().unwrap();
let commitment = match parse_commitment(&env, commitment) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let compressed = ffi_interface::serialize_commitment(commitment);
let result = match env.byte_array_from_slice(&compressed) {
Ok(s) => s,
Err(_) => {
env.throw_new(
"java/lang/IllegalArgumentException",
"Invalid commitment output. Couldn't convert to byte array.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}

let hash = ffi_interface::hash_commitment(commitment_bytes);
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_compressMany(
env: JNIEnv, _class: JClass<'_>, commitments: jbyteArray
) -> jbyteArray {

env.byte_array_from_slice(&hash)
.expect("Couldn't convert to byte array")
let commitments = match parse_commitments(&env, commitments) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let compressed: Vec<u8> = commitments.chunks_exact(64).flat_map(|x| ffi_interface::serialize_commitment(x.try_into().unwrap())).collect();
let result = match env.byte_array_from_slice(&compressed) {
Ok(s) => s,
Err(_) => {
env.throw_new(
"java/lang/IllegalArgumentException",
"Invalid commitment output. Couldn't convert to byte array.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}

/// Update commitment sparse
/// Expects byteArray of fixed 64bytes for the commitment
/// and dynamic tuple (old_scalar(32 bytes), new_scalar(32 bytes), index(1 byte)) in this sequence
/// Bytearray is processed with ffi_interface::deserialize_update_commitment_sparse and sent to ffi_interface::update_commitment_sparse.
/// We get updated commitemnt and return it as 64 bytes.
/// If Commitment is empty we should pass https://github.com/crate-crypto/rust-verkle/blob/bb5af2f2fe9788d49d2896b9614a3125f8227818/ffi_interface/src/lib.rs#L57
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_updateCommitmentSparse(
env: JNIEnv,
_class: JClass<'_>,
input: jbyteArray,
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_hash(
env: JNIEnv, _class: JClass<'_>, commitment: jbyteArray
) -> jbyteArray {
let input = env
.convert_byte_array(input)
.expect("Cannot convert jbyteArray to rust array");

let (old_commitment_bytes, indexes, old_scalars, new_scalars) =
match ffi_interface::deserialize_update_commitment_sparse(input) {
Ok(decomposed_input) => decomposed_input,
Err(err) => {
env.throw_new(
"java/text/ParseException",
format!("Could not deserialize the input, error : {:?}", err),
)
.expect("Failed to throw exception");
return std::ptr::null_mut(); // Return null pointer to indicate an error
}
};
let updated_commitment = ffi_interface::update_commitment_sparse(
&CONFIG,
old_commitment_bytes,
indexes,
old_scalars,
new_scalars,
)
.unwrap();
let commitment = match parse_commitment(&env, commitment) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let hash = ffi_interface::hash_commitment(commitment);
let result = match env.byte_array_from_slice(&hash) {
Ok(s) => s,
Err(_) => {
env.throw_new(
"java/lang/IllegalArgumentException",
"Invalid commitment output. Couldn't convert to byte array.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}

env.byte_array_from_slice(&updated_commitment)
.expect("Couldn't convert to byte array")
#[no_mangle]
pub extern "system" fn Java_org_hyperledger_besu_nativelib_ipamultipoint_LibIpaMultipoint_hashMany(
env: JNIEnv, _class: JClass<'_>, commitments: jbyteArray
) -> jbyteArray {
let input = match parse_commitments(&env, commitments) {
Ok(v) => v,
Err(e) => {
env.throw_new("java/lang/IllegalArgumentException", e)
.expect("Failed to throw exception for commit inputs.");
return std::ptr::null_mut();
}
};
let input: Vec<ffi_interface::CommitmentBytes> = input.chunks_exact(64).map(|x| {
let mut array = [0u8; 64];
array.copy_from_slice(x);
array
}).collect();
let hashes = ffi_interface::hash_commitments(&input);
let hashes: Vec<u8> = hashes.iter().flat_map(|x| x.iter().copied()).collect();
let result = match env.byte_array_from_slice(&hashes) {
Ok(s) => s,
Err(_) => {
env.throw_new(
"java/lang/IllegalArgumentException",
"Invalid scalars output. Couldn't convert to byte array.")
.expect("Couldn't convert to byte array");
return std::ptr::null_mut();
}
};
result
}
44 changes: 44 additions & 0 deletions ipa-multipoint/ipa_multipoint_jni/src/parsers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use jni::JNIEnv;
use jni::objects::ReleaseMode;
use jni::sys::jbyteArray;

use std::convert::TryFrom;

use ffi_interface::CommitmentBytes;


pub fn parse_scalars<'a>(env: &'a JNIEnv<'a>, values: jbyteArray) -> Result<&'a [u8], String> {
let input_len = env.get_array_length(values).map_err(|_| "Cannot get array lenght".to_string())? as usize;
if input_len % 32 != 0 {
return Err("Wrong input size: should be a mulitple of 32 bytes".to_string())
};
let input_elements = env.get_primitive_array_critical(values, ReleaseMode::NoCopyBack).map_err(|_| "Cannot get array elements".to_string())?;
let input_slice = unsafe { std::slice::from_raw_parts(input_elements.as_ptr() as *const u8, input_len) };
Ok(input_slice)
}

pub fn parse_indices(env: &JNIEnv, values: jbyteArray) -> Result<Vec<usize>, String> {
let input_len = env.get_array_length(values).map_err(|_| "Cannot get array lenght".to_string())? as usize;
let input_elements = env.get_primitive_array_critical(values, ReleaseMode::NoCopyBack).map_err(|_| "Cannot get array elements".to_string())?;
let input_slice = unsafe { std::slice::from_raw_parts(input_elements.as_ptr() as *const u8, input_len) };
let result: Vec<usize> = input_slice.iter().map(|&x| x as usize).collect();
Ok(result)
}

pub fn parse_commitment(env: &JNIEnv, commitment: jbyteArray) -> Result<CommitmentBytes, String> {
let input_len = env.get_array_length(commitment).map_err(|_| "Cannot get commitment lenght".to_string())? as usize;
let input_elements = env.get_primitive_array_critical(commitment, ReleaseMode::NoCopyBack).map_err(|_| "Cannot get array elements".to_string())?;
let input_slice = unsafe { std::slice::from_raw_parts(input_elements.as_ptr() as *const u8, input_len) };
let result: CommitmentBytes = CommitmentBytes::try_from(input_slice).map_err(|_| "Wrong commitment size: should be 64 bytes".to_string())?;
Ok(result)
}

pub fn parse_commitments<'a>(env: &'a JNIEnv<'a>, commitment: jbyteArray) -> Result<&'a [u8], String> {
let input_len = env.get_array_length(commitment).map_err(|_| "Cannot get commitment lenght".to_string())? as usize;
if input_len % 64 != 0 {
return Err("Wrong input size: should be a mulitple of 64 bytes".to_string())
};
let input_elements = env.get_primitive_array_critical(commitment, ReleaseMode::NoCopyBack).map_err(|_| "Cannot get array elements".to_string())?;
let input_slice = unsafe { std::slice::from_raw_parts(input_elements.as_ptr() as *const u8, input_len) };
Ok(input_slice)
}
Loading

0 comments on commit 007c808

Please sign in to comment.