diff --git a/.gitmodules b/.gitmodules index 6122cf2..1a58f3c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "rust/librustzcash"] path = rust/librustzcash url = https://github.com/tronprotocol/librustzcash.git - branch = release_vm_zksnarks_4.0 - ignore=dirty + branch = release_vm_zksnarks_4.0_lto_false + ignore = dirty diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 13c4e3a..f2c45e2 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -24,8 +24,10 @@ link_directories(lib) set(zksnark_jni_include org_tron_common_zksnark_Librustzcash_LibrustzcashJNI.h) set(sodium_jni_include org_tron_common_zksnark_Libsodium_LibsodiumJNI.h) +set(arkworks_jni_include org_tron_common_zksnark_Libarkworks_LibarkworksJNI.h) set(zksnark_jni_src LibrustzcashJNIImpl.cpp) set(sodium_jni_src LibsodiumJNIImpl.cpp) +set(arkworks_jni_src LibarkworksJNIImpl.cpp) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src zksnark_srcs) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include) @@ -35,6 +37,7 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}") include_directories("${CMAKE_BINARY_DIR}") include_directories("${CMAKE_BINARY_DIR}/include") include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../rust/librustzcash/librustzcash/include") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../rust/libarkworks/include") add_custom_command( OUTPUT "${zksnark_jni_include}" @@ -54,10 +57,22 @@ add_custom_command( DEPENDS ${sodium_jni_include} ) +add_custom_command( + OUTPUT "${arkworks_jni_include}" + COMMAND ${Java_JAVAC_EXECUTABLE} -verbose + -h ${CMAKE_BINARY_DIR}/include + -d ${CMAKE_BINARY_DIR}/tmp + "${CMAKE_CURRENT_SOURCE_DIR}/../src/main/java/org/tron/common/zksnark/Libarkworks.java" + DEPENDS ${arkworks_jni_include} +) + set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust) set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../rust/librustzcash/target ) +set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/../rust/libarkworks/target + ) ExternalProject_Add( librustzcash @@ -71,7 +86,22 @@ ExternalProject_Add( INSTALL_COMMAND "" LOG_BUILD OFF) -add_custom_target(rust ALL DEPENDS librustzcash) +add_custom_target(rustzcash ALL DEPENDS librustzcash) + +# libarkworks +ExternalProject_Add( + libarkworks + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND cargo build + # COMMAND cargo build + --package libarkworks + --release + BINARY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../rust/libarkworks" + INSTALL_COMMAND "" + LOG_BUILD OFF) + +add_custom_target(rustarkworks ALL DEPENDS libarkworks) # libsodium @@ -93,27 +123,31 @@ ADD_LIBRARY(zksnarkjni SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/${zksnark_jni_src} ${CMAKE_CURRENT_SOURCE_DIR}/src/${sodium_jni_src} + ${CMAKE_CURRENT_SOURCE_DIR}/src/${arkworks_jni_src} ${zksnark_src_dependencies} ${zksnark_jni_include} ${sodium_jni_include} + ${arkworks_jni_include} ${zksnark_srcs} ) target_link_libraries(zksnarkjni ${CMAKE_THREAD_LIBS_INIT} "${CMAKE_CURRENT_SOURCE_DIR}/../rust/librustzcash/target/release/librustzcash.a" + "${CMAKE_CURRENT_SOURCE_DIR}/../rust/libarkworks/target/release/libarkworks.a" ${NACL_DIR}/src/libsodium/src/libsodium/.libs/libsodium.a ) -add_dependencies(zksnarkjni rust) +add_dependencies(zksnarkjni rustzcash) add_dependencies(zksnarkjni libsodium) +add_dependencies(zksnarkjni rustarkworks) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - SET_TARGET_PROPERTIES( zksnarkjni PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy" ) + SET_TARGET_PROPERTIES( zksnarkjni PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,--wrap=memcpy -pthread" ) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - INSTALL(TARGETS zksnarkjni LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../src/main/resources/native-package/linux) + INSTALL(TARGETS zksnarkjni LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../src/main/resources/META-INF/native/linux64) else() - INSTALL(TARGETS zksnarkjni LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../src/main/resources/native-package/macos) + INSTALL(TARGETS zksnarkjni LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../src/main/resources/META-INF/native/osx64) endif() diff --git a/cpp/src/LibarkworksJNIImpl.cpp b/cpp/src/LibarkworksJNIImpl.cpp new file mode 100644 index 0000000..f9fb596 --- /dev/null +++ b/cpp/src/LibarkworksJNIImpl.cpp @@ -0,0 +1,146 @@ +#include "org_tron_common_zksnark_Libarkworks_LibarkworksJNI.h" +#include "libarkworks.h" +#include + +jboolean bool2jbool(bool b) { + if (b) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksG1IsValid + * Signature: ([B[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksG1IsValid + (JNIEnv * env, jobject, jbyteArray x, jbyteArray y) { + unsigned char * x_p = (unsigned char *) env->GetByteArrayElements(x, nullptr); + unsigned char * y_p = (unsigned char *) env->GetByteArrayElements(y, nullptr); + if (x_p == NULL || y_p == NULL) { + return JNI_FALSE; + } + + bool result = libarkworks_g1_is_valid(x_p, y_p); + env->ReleaseByteArrayElements(x, (jbyte*) x_p, 0); + env->ReleaseByteArrayElements(y, (jbyte*) y_p, 0); + return bool2jbool(result); +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksG2IsValid + * Signature: ([B[B[B[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksG2IsValid + (JNIEnv * env, jobject, jbyteArray a, jbyteArray b, jbyteArray c, jbyteArray d) { + unsigned char * a_p = (unsigned char *) env->GetByteArrayElements(a, nullptr); + unsigned char * b_p = (unsigned char *) env->GetByteArrayElements(b, nullptr); + unsigned char * c_p = (unsigned char *) env->GetByteArrayElements(c, nullptr); + unsigned char * d_p = (unsigned char *) env->GetByteArrayElements(d, nullptr); + if (a_p == NULL || b_p == NULL || c_p == NULL || d_p == NULL) { + return JNI_FALSE; + } + + bool result = libarkworks_g2_is_valid(a_p, b_p, c_p, d_p); + env->ReleaseByteArrayElements(a, (jbyte*) a_p, 0); + env->ReleaseByteArrayElements(b, (jbyte*) b_p, 0); + env->ReleaseByteArrayElements(c, (jbyte*) c_p, 0); + env->ReleaseByteArrayElements(d, (jbyte*) d_p, 0); + return bool2jbool(result); +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksAddG1 + * Signature: ([B[B[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksAddG1 + (JNIEnv * env, jobject, jbyteArray a, jbyteArray b, jbyteArray result) { + unsigned char * a_p = (unsigned char *) env->GetByteArrayElements(a, nullptr); + unsigned char * b_p = (unsigned char *) env->GetByteArrayElements(b, nullptr); + unsigned char * result_p = (unsigned char *) env->GetByteArrayElements(result, nullptr); + if (a_p == NULL || b_p == NULL || result_p == NULL) { + return JNI_FALSE; + } + + bool success = libarkworks_add_g1(a_p, b_p, result_p); + env->ReleaseByteArrayElements(a, (jbyte*) a_p, 0); + env->ReleaseByteArrayElements(b, (jbyte*) b_p, 0); + env->ReleaseByteArrayElements(result, (jbyte*) result_p, 0); + return bool2jbool(success); +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksMulG1 + * Signature: ([B[B[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksMulG1 + (JNIEnv * env, jobject, jbyteArray p, jbyteArray s, jbyteArray result) { + unsigned char * p_p = (unsigned char *) env->GetByteArrayElements(p, nullptr); + unsigned char * s_p = (unsigned char *) env->GetByteArrayElements(s, nullptr); + unsigned char * result_p = (unsigned char *) env->GetByteArrayElements(result, nullptr); + if (p_p == NULL || s_p == NULL || result_p == NULL) { + return JNI_FALSE; + } + + bool success = libarkworks_mul_g1(p_p, s_p, result_p); + env->ReleaseByteArrayElements(p, (jbyte*) p_p, 0); + env->ReleaseByteArrayElements(s, (jbyte*) s_p, 0); + env->ReleaseByteArrayElements(result, (jbyte*) result_p, 0); + return bool2jbool(success); +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksPairingCheck + * Signature: ([B[BI)Z + */ +JNIEXPORT jboolean JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksPairingCheck + (JNIEnv * env, jobject, jbyteArray g1s, jbyteArray g2s, jint pairs) { + unsigned char * g1s_p = (unsigned char *) env->GetByteArrayElements(g1s, nullptr); + unsigned char * g2s_p = (unsigned char *) env->GetByteArrayElements(g2s, nullptr); + if (g1s_p == NULL || g2s_p == NULL) { + return JNI_FALSE; + } + + bool success = libarkworks_pairing_check(g1s_p, g2s_p, pairs); + env->ReleaseByteArrayElements(g1s, (jbyte*) g1s_p, 0); + env->ReleaseByteArrayElements(g2s, (jbyte*) g2s_p, 0); + return bool2jbool(success); +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksRandomG1 + * Signature: ([B)V + */ +JNIEXPORT void JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksRandomG1 + (JNIEnv * env, jobject, jbyteArray g1) { + unsigned char * g1_p = (unsigned char *) env->GetByteArrayElements(g1, nullptr); + if (g1_p == NULL) { + return; + } + + libarkworks_random_g1(g1_p); + env->ReleaseByteArrayElements(g1, (jbyte*) g1_p, 0); +} + +/* + * Class: org_tron_common_zksnark_Libarkworks_LibarkworksJNI + * Method: libarkworksRandomG2 + * Signature: ([B)V + */ +JNIEXPORT void JNICALL Java_org_tron_common_zksnark_Libarkworks_00024LibarkworksJNI_libarkworksRandomG2 + (JNIEnv * env, jobject, jbyteArray g2) { + unsigned char * g2_p = (unsigned char *) env->GetByteArrayElements(g2, nullptr); + if (g2_p == NULL) { + return; + } + + libarkworks_random_g2(g2_p); + env->ReleaseByteArrayElements(g2, (jbyte*)g2_p, 0); +} \ No newline at end of file diff --git a/cpp/src/memcpy.c b/cpp/src/memcpy.c index f560e7b..0bae5cf 100644 --- a/cpp/src/memcpy.c +++ b/cpp/src/memcpy.c @@ -1,13 +1,17 @@ -#ifdef __linux__ - #include +#ifdef __linux__ +#if defined(__x86_64__) && defined(__GNU_LIBRARY__) void *__memcpy_old(void *, const void *, size_t); asm(".symver __memcpy_old, memcpy@GLIBC_2.2.5"); -void *__wrap_memcpy(void *dest, const void *src, size_t n) -{ +void *__wrap_memcpy(void *dest, const void *src, size_t n) { return __memcpy_old(dest, src, n); } - -#endif \ No newline at end of file +#else +// https://chromium.googlesource.com/external/github.com/google/protobuf/+/HEAD/ruby/ext/google/protobuf_c/wrap_memcpy.c +void *__wrap_memcpy(void *dest, const void *src, size_t n) { + return memmove(dest, src, n); +} +#endif +#endif diff --git a/rust/libarkworks/.gitignore b/rust/libarkworks/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/rust/libarkworks/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/rust/libarkworks/Cargo.toml b/rust/libarkworks/Cargo.toml new file mode 100644 index 0000000..8bd05d8 --- /dev/null +++ b/rust/libarkworks/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "libarkworks" +version = "0.1.0" +edition = "2021" + +[lib] +name = "arkworks" +path = "src/lib.rs" +crate-type = ["staticlib", "cdylib"] + +[dependencies] +libc = "0.2" +rand = "0.8.5" +ark-bn254 = "0.4.0" +ark-ff = "0.4.2" +ark-serialize = "0.4.2" +ark-ec = "0.4.2" + +[profile.release] +lto = false +panic = 'abort' +codegen-units = 1 \ No newline at end of file diff --git a/rust/libarkworks/include/libarkworks.h b/rust/libarkworks/include/libarkworks.h new file mode 100644 index 0000000..28d164d --- /dev/null +++ b/rust/libarkworks/include/libarkworks.h @@ -0,0 +1,52 @@ +#ifndef LIBARKWORKS_INCLUDE_H_ +#define LIBARKWORKS_INCLUDE_H_ + +#include + +extern "C" { +#ifdef WIN32 + typedef uint16_t codeunit; +#else + typedef uint8_t codeunit; +#endif + + bool libarkworks_g1_is_valid( + const unsigned char *x, + const unsigned char *y + ); + + bool libarkworks_g2_is_valid( + const unsigned char *a, + const unsigned char *b, + const unsigned char *c, + const unsigned char *d + ); + + bool libarkworks_add_g1( + const unsigned char *a, + const unsigned char *b, + unsigned char *result + ); + + bool libarkworks_mul_g1( + const unsigned char *p, + const unsigned char *s, + unsigned char *result + ); + + bool libarkworks_pairing_check( + const unsigned char *g1s, + const unsigned char *g2s, + const uint32_t pairs + ); + + void libarkworks_random_g1( + unsigned char *g1 + ); + + void libarkworks_random_g2( + unsigned char *g2 + ); +} + +#endif // LIBARKWORKS_INCLUDE_H_ diff --git a/rust/libarkworks/src/bn254.rs b/rust/libarkworks/src/bn254.rs new file mode 100644 index 0000000..08ec43f --- /dev/null +++ b/rust/libarkworks/src/bn254.rs @@ -0,0 +1,66 @@ +use ark_bn254::{Bn254, G1Affine, G2Affine}; +use ark_ec::{pairing::Pairing, AffineRepr, CurveGroup}; +use ark_ff::{BigInteger256, Field, Fp12, UniformRand}; +use ark_serialize::SerializationError; + +use crate::{serialize::EthSerializeDeserialize, G1_SERIALIZED_SIZE, G2_SERIALIZED_SIZE}; + +pub fn random_g1() -> Vec { + let mut rng = rand::thread_rng(); + G1Affine::rand(&mut rng) + .eth_serialize() + .expect("failed to serialize random point") +} + +pub fn random_g2() -> Vec { + let mut rng = rand::thread_rng(); + G2Affine::rand(&mut rng) + .eth_serialize() + .expect("failed to serialize random point") +} + +pub fn g1_is_valid(serialized: Vec) -> bool { + // We probably can safely bypass is_in_correct_subgroup_assuming_on_curve check + // since there is only one subgroup and we can't fail this check + let g1 = G1Affine::eth_deserialize_unchecked(&serialized); + match g1 { + Ok(g1) => g1.is_on_curve(), + Err(_) => false, + } +} + +pub fn g2_is_valid(serialized: Vec) -> bool { + G2Affine::eth_deserialize(&serialized).is_ok() +} + +pub fn add_g1(a_serialized: &[u8], b_serialized: &[u8]) -> Result, SerializationError> { + let a = G1Affine::eth_deserialize_unchecked(a_serialized)?; + let b = G1Affine::eth_deserialize_unchecked(b_serialized)?; + (a + b).into_affine().eth_serialize() +} + +pub fn mul_g1(a_serialized: &[u8], s_serialized: &[u8]) -> Result, SerializationError> { + let p = G1Affine::eth_deserialize_unchecked(a_serialized)?; + let s = BigInteger256::eth_deserialize(s_serialized)?; + p.mul_bigint(s).into_affine().eth_serialize() +} + +pub fn pairing_check( + g1s_serialized: &[u8], + g2s_serialized: &[u8], + pairs: usize, +) -> Result { + let mut g1s = vec![]; + let mut g2s = vec![]; + for i in 0..pairs { + g1s.push(G1Affine::eth_deserialize_unchecked( + &g1s_serialized[i * G1_SERIALIZED_SIZE..(i + 1) * G1_SERIALIZED_SIZE], + )?); + g2s.push(G2Affine::eth_deserialize_unchecked( + &g2s_serialized[i * G2_SERIALIZED_SIZE..(i + 1) * G2_SERIALIZED_SIZE], + )?); + } + + let result = Bn254::multi_pairing(g1s, g2s); + Ok(result.0 == Fp12::ONE) +} diff --git a/rust/libarkworks/src/lib.rs b/rust/libarkworks/src/lib.rs new file mode 100644 index 0000000..d394774 --- /dev/null +++ b/rust/libarkworks/src/lib.rs @@ -0,0 +1,96 @@ +use std::slice; + +use libc::c_uchar; + +mod bn254; +mod serialize; + +pub const FIELD_ELEMENT_SIZE: usize = 32; +pub const G1_SERIALIZED_SIZE: usize = 64; +pub const G2_SERIALIZED_SIZE: usize = 128; + +#[no_mangle] +pub extern "system" fn libarkworks_g1_is_valid( + x: *const [c_uchar; FIELD_ELEMENT_SIZE], + y: *const [c_uchar; FIELD_ELEMENT_SIZE], +) -> bool { + let x = unsafe { &*x }; + let y = unsafe { &*y }; + bn254::g1_is_valid([&x[..], y].concat()) +} + +#[no_mangle] +pub extern "system" fn libarkworks_g2_is_valid( + a: *const [c_uchar; FIELD_ELEMENT_SIZE], + b: *const [c_uchar; FIELD_ELEMENT_SIZE], + c: *const [c_uchar; FIELD_ELEMENT_SIZE], + d: *const [c_uchar; FIELD_ELEMENT_SIZE], +) -> bool { + let a = unsafe { &*a }; + let b = unsafe { &*b }; + let c = unsafe { &*c }; + let d = unsafe { &*d }; + bn254::g2_is_valid([&a[..], b, c, d].concat()) +} + +#[no_mangle] +pub extern "system" fn libarkworks_add_g1( + a: *const [c_uchar; G1_SERIALIZED_SIZE], + b: *const [c_uchar; G1_SERIALIZED_SIZE], + result: *mut [c_uchar; G1_SERIALIZED_SIZE], +) -> bool { + let a_serialized = unsafe { &*a }; + let b_serialized = unsafe { &*b }; + let result = unsafe { &mut *result }; + + match bn254::add_g1(a_serialized, b_serialized) { + Ok(sum) => { + result.copy_from_slice(&sum); + true + } + Err(_) => false, + } +} + +#[no_mangle] +pub extern "system" fn libarkworks_mul_g1( + p: *const [c_uchar; G1_SERIALIZED_SIZE], + s: *const [c_uchar; FIELD_ELEMENT_SIZE], + result: *mut [c_uchar; G1_SERIALIZED_SIZE], +) -> bool { + let p_serialized = unsafe { &*p }; + let s_serialized = unsafe { &*s }; + let result = unsafe { &mut *result }; + + match bn254::mul_g1(p_serialized, s_serialized) { + Ok(product) => { + result.copy_from_slice(&product); + true + } + Err(_) => false, + } +} + +#[no_mangle] +pub extern "system" fn libarkworks_pairing_check( + g1s: *const c_uchar, + g2s: *const c_uchar, + pairs: u32, +) -> bool { + let g1s_serialized = unsafe { slice::from_raw_parts(g1s, G1_SERIALIZED_SIZE * pairs as usize) }; + let g2s_serialized = unsafe { slice::from_raw_parts(g2s, G2_SERIALIZED_SIZE * pairs as usize) }; + + bn254::pairing_check(g1s_serialized, g2s_serialized, pairs as usize).unwrap_or(false) +} + +#[no_mangle] +pub extern "system" fn libarkworks_random_g1(result: *mut [c_uchar; G1_SERIALIZED_SIZE]) { + let result = unsafe { &mut *result }; + result.copy_from_slice(&bn254::random_g1()); +} + +#[no_mangle] +pub extern "system" fn libarkworks_random_g2(result: *mut [c_uchar; G2_SERIALIZED_SIZE]) { + let result = unsafe { &mut *result }; + result.copy_from_slice(&bn254::random_g2()); +} diff --git a/rust/libarkworks/src/serialize.rs b/rust/libarkworks/src/serialize.rs new file mode 100644 index 0000000..3189b4a --- /dev/null +++ b/rust/libarkworks/src/serialize.rs @@ -0,0 +1,93 @@ +use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; +use ark_ff::BigInteger256; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, SerializationError}; + +use crate::FIELD_ELEMENT_SIZE; + +pub trait EthSerializeDeserialize { + fn eth_serialize(&self) -> Result, SerializationError>; + + fn eth_deserialize(serialized: &[u8]) -> Result + where + Self: Sized; + + fn eth_deserialize_unchecked(serialized: &[u8]) -> Result + where + Self: Sized; +} + +impl EthSerializeDeserialize for Affine

{ + fn eth_serialize(&self) -> Result, SerializationError> { + if self.infinity { + // infinity encoded as (0, 0) + return Ok(vec![0; self.serialized_size(Compress::No)]); + } + + let mut serialized = vec![]; + self.serialize_with_mode(&mut serialized, Compress::No)?; + clear_flags(&mut serialized); + Ok(convert_to_big_endian(&serialized)) + } + + fn eth_deserialize(serialized: &[u8]) -> Result { + if is_zero_vec(serialized) { + return Ok(Affine::identity()); + } + let serialized = convert_to_little_endian(serialized); + Self::deserialize_uncompressed(&serialized[..]) + } + + fn eth_deserialize_unchecked(serialized: &[u8]) -> Result { + if is_zero_vec(serialized) { + return Ok(Affine::identity()); + } + let serialized = convert_to_little_endian(serialized); + Self::deserialize_uncompressed_unchecked(&serialized[..]) + } +} + +impl EthSerializeDeserialize for BigInteger256 { + fn eth_serialize(&self) -> Result, SerializationError> { + let mut serialized = vec![]; + self.serialize_with_mode(&mut serialized, Compress::No)?; + Ok(convert_to_big_endian(&serialized)) + } + + fn eth_deserialize(serialized: &[u8]) -> Result { + let serialized = convert_to_little_endian(serialized); + Self::deserialize_uncompressed(&serialized[..]) + } + + fn eth_deserialize_unchecked(serialized: &[u8]) -> Result { + Self::eth_deserialize(serialized) + } +} + +fn clear_flags(serialized: &mut Vec) { + // we can clear flags since it is possible to + // restore points without them + let last = serialized.len() - 1; + serialized[last] &= 0b00111111; +} + +fn is_zero_vec(vec: &[u8]) -> bool { + vec.iter().all(|b| *b == 0) +} + +fn convert_to_little_endian(serialized: &[u8]) -> Vec { + convert_to_big_endian(serialized) +} + +fn convert_to_big_endian(serialized: &[u8]) -> Vec { + serialized + .chunks(FIELD_ELEMENT_SIZE) + .map(|chunk| { + let mut chunk = Vec::from(chunk); + chunk.reverse(); + chunk + }) + .fold(vec![], |mut acc, chunk| { + acc.extend(chunk); + acc + }) +} diff --git a/rust/librustzcash b/rust/librustzcash index 45f8049..5dd187a 160000 --- a/rust/librustzcash +++ b/rust/librustzcash @@ -1 +1 @@ -Subproject commit 45f80494b0760e9993e178b06fd0781caca70c69 +Subproject commit 5dd187affdc1cccc5a6fde0d08f59fe1febe0879 diff --git a/src/main/java/org/tron/common/zksnark/Libarkworks.java b/src/main/java/org/tron/common/zksnark/Libarkworks.java new file mode 100644 index 0000000..d181bea --- /dev/null +++ b/src/main/java/org/tron/common/zksnark/Libarkworks.java @@ -0,0 +1,49 @@ +package org.tron.common.zksnark; + +class Libarkworks { + private static final LibarkworksJNI INSTANCE = new LibarkworksJNI(); + + public boolean libarkworksG1IsValid(byte[] x, byte[] y) { + return INSTANCE.libarkworksG1IsValid(x, y); + } + + public boolean libarkworksG2IsValid(byte[] a, byte[] b, byte[] c, byte[] d) { + return INSTANCE.libarkworksG2IsValid(a, b, c, d); + } + + public boolean libarkworksAddG1(byte[] a, byte[] b, byte[] result) { + return INSTANCE.libarkworksAddG1(a, b, result); + } + + public boolean libarkworksMulG1(byte[] p, byte[] s, byte[] result) { + return INSTANCE.libarkworksMulG1(p, s, result); + } + + public boolean libarkworksPairingCheck(byte[] g1s, byte[] g2s, int pairs) { + return INSTANCE.libarkworksPairingCheck(g1s, g2s, pairs); + } + + public void libarkworksRandomG1(byte[] g1) { + INSTANCE.libarkworksRandomG1(g1); + } + + public void libarkworksRandomG2(byte[] g2) { + INSTANCE.libarkworksRandomG2(g2); + } + + private static class LibarkworksJNI { + private native boolean libarkworksG1IsValid(byte[] x, byte[] y); + + private native boolean libarkworksG2IsValid(byte[] a, byte[] b, byte[] c, byte[] d); + + private native boolean libarkworksAddG1(byte[] a, byte[] b, byte[] result); + + private native boolean libarkworksMulG1(byte[] p, byte[] s, byte[] result); + + private native boolean libarkworksPairingCheck(byte[] g1s, byte[] g2s, int pairs); + + private native void libarkworksRandomG1(byte[] g1); + + private native void libarkworksRandomG2(byte[] g1); + } +} diff --git a/src/main/java/org/tron/common/zksnark/LibarkworksWrapper.java b/src/main/java/org/tron/common/zksnark/LibarkworksWrapper.java new file mode 100644 index 0000000..5e15ad8 --- /dev/null +++ b/src/main/java/org/tron/common/zksnark/LibarkworksWrapper.java @@ -0,0 +1,32 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.tron.common.zksnark; + +import org.tron.common.util.Utils; + +public class LibarkworksWrapper { + private static final Libarkworks INSTANCE = new Libarkworks(); + + static { + Utils.LIBRARY.load(); + } + + private LibarkworksWrapper() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + public static Libarkworks getInstance() { + return INSTANCE; + } +} diff --git a/src/main/resources/META-INF/native/linux64/aarch64/libzksnarkjni.so b/src/main/resources/META-INF/native/linux64/aarch64/libzksnarkjni.so index 6133bbe..57e6c50 100644 Binary files a/src/main/resources/META-INF/native/linux64/aarch64/libzksnarkjni.so and b/src/main/resources/META-INF/native/linux64/aarch64/libzksnarkjni.so differ diff --git a/src/main/resources/META-INF/native/linux64/libzksnarkjni.so b/src/main/resources/META-INF/native/linux64/libzksnarkjni.so index 117ea46..4a54196 100755 Binary files a/src/main/resources/META-INF/native/linux64/libzksnarkjni.so and b/src/main/resources/META-INF/native/linux64/libzksnarkjni.so differ diff --git a/src/main/resources/META-INF/native/osx64/aarch64/libzksnarkjni.jnilib b/src/main/resources/META-INF/native/osx64/aarch64/libzksnarkjni.jnilib index 1300365..42f3a6b 100755 Binary files a/src/main/resources/META-INF/native/osx64/aarch64/libzksnarkjni.jnilib and b/src/main/resources/META-INF/native/osx64/aarch64/libzksnarkjni.jnilib differ diff --git a/src/main/resources/META-INF/native/osx64/libzksnarkjni.jnilib b/src/main/resources/META-INF/native/osx64/libzksnarkjni.jnilib index 6743bc8..c2c9f12 100755 Binary files a/src/main/resources/META-INF/native/osx64/libzksnarkjni.jnilib and b/src/main/resources/META-INF/native/osx64/libzksnarkjni.jnilib differ diff --git a/src/test/java/org/tron/common/zksnark/LibarkworksTest.java b/src/test/java/org/tron/common/zksnark/LibarkworksTest.java new file mode 100644 index 0000000..2a6595f --- /dev/null +++ b/src/test/java/org/tron/common/zksnark/LibarkworksTest.java @@ -0,0 +1,170 @@ +package org.tron.common.zksnark; + +import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; +import java.util.Arrays; + +import org.junit.Test; + +public class LibarkworksTest { + @Test + public void libarkworksG1IsValid() { + byte[] g1 = new byte[64]; + byte[][] g1XY = getG1XY(g1); + boolean isValid = LibarkworksWrapper.getInstance().libarkworksG1IsValid(g1XY[0], g1XY[1]); + assertTrue("G1 point (0, 0) should be valid", isValid); + + // this case is valid with the java implementation of BN254 + isValid = LibarkworksWrapper.getInstance().libarkworksG1IsValid(new byte[0], new byte[0]); + assertTrue("G1 point (0, 0) should be valid", isValid); + + LibarkworksWrapper.getInstance().libarkworksRandomG1(g1); + g1XY = getG1XY(g1); + isValid = LibarkworksWrapper.getInstance().libarkworksG1IsValid(g1XY[0], g1XY[1]); + assertTrue("Random G1 point should be valid", isValid); + + byte[] x = decimalStringToBytes("1284141008968177401446123273711643349693445534482996744734158642473403649434"); + byte[] y = decimalStringToBytes("6652232392179121025009765525095111191899542355288332634641296965917801744215"); + isValid = LibarkworksWrapper.getInstance().libarkworksG1IsValid(x, y); + assertTrue("G1 point should be valid", isValid); + + x = decimalStringToBytes("1284141008968177401446123273711643349693445534482996744734158642473403649435"); + y = decimalStringToBytes("6652232392179121025009765525095111191899542355288332634641296965917801744215"); + isValid = LibarkworksWrapper.getInstance().libarkworksG1IsValid(x, y); + assertFalse("G1 point should NOT be valid", isValid); + } + + @Test + public void libarkworksG2IsValid() { + byte[] g2 = new byte[128]; + byte[][] g2ABCD = getG2ABCD(g2); + boolean isValid = LibarkworksWrapper.getInstance().libarkworksG2IsValid(g2ABCD[0], g2ABCD[1], g2ABCD[2], g2ABCD[3]); + assertTrue("G2 point (0, 0, 0, 0) should be valid", isValid); + + // this case is valid with the java implementation of BN254 + isValid = LibarkworksWrapper.getInstance().libarkworksG2IsValid(new byte[0], new byte[0], new byte[0], + new byte[0]); + assertTrue("G2 point (0, 0, 0, 0) should be valid", isValid); + + LibarkworksWrapper.getInstance().libarkworksRandomG2(g2); + g2ABCD = getG2ABCD(g2); + isValid = LibarkworksWrapper.getInstance().libarkworksG2IsValid(g2ABCD[0], g2ABCD[1], g2ABCD[2], g2ABCD[3]); + assertTrue("Random G2 point should be valid", isValid); + + byte[] a = decimalStringToBytes("21121751982565218239254353368172068966841985326481744739199573873176012212444"); + byte[] b = decimalStringToBytes("10714328940565706835656507512525563808003404883416989196062222412736825474535"); + byte[] c = decimalStringToBytes("6032274688546681517580073545556919091181545541514426001760248014080068448729"); + byte[] d = decimalStringToBytes("3861041822235706362318471606688154602339352702913131403223701683401789305194"); + isValid = LibarkworksWrapper.getInstance().libarkworksG2IsValid(a, b, c, d); + assertTrue("G2 point should be valid", isValid); + + a = decimalStringToBytes("21121751982565218239254353368172068966841985326481744739199573873176012212445"); + b = decimalStringToBytes("10714328940565706835656507512525563808003404883416989196062222412736825474535"); + c = decimalStringToBytes("6032274688546681517580073545556919091181545541514426001760248014080068448729"); + d = decimalStringToBytes("3861041822235706362318471606688154602339352702913131403223701683401789305194"); + isValid = LibarkworksWrapper.getInstance().libarkworksG2IsValid(a, b, c, d); + assertFalse("G2 point should NOT be valid", isValid); + } + + @Test + public void libarkworksAddG1() { + byte[] x1 = decimalStringToBytes("1284141008968177401446123273711643349693445534482996744734158642473403649434"); + byte[] y1 = decimalStringToBytes("6652232392179121025009765525095111191899542355288332634641296965917801744215"); + byte[] p1 = concat(x1, y1); + + byte[] x2 = decimalStringToBytes("1138727310856149830881699283295531549904445644547980102009978040558245602177"); + byte[] y2 = decimalStringToBytes("17007876717863907135144187607286180690188030689394587325056604331322830212672"); + byte[] p2 = concat(x2, y2); + + byte[] x3 = decimalStringToBytes("20102382515752348416946264354484414766888026124153337256604214638968111116366"); + byte[] y3 = decimalStringToBytes("17677679321201222010634958653214725343119435180121348086429362586364864643842"); + byte[] p3 = concat(x3, y3); + + byte[] infinity = new byte[64]; + + byte[] result = new byte[64]; + boolean success = LibarkworksWrapper.getInstance().libarkworksAddG1(p1, p2, result); + assertTrue("Addition should be successful", success); + assertTrue("Actual result is not equal to expected", Arrays.equals(p3, result)); + + success = LibarkworksWrapper.getInstance().libarkworksAddG1(p1, infinity, result); + assertTrue("Addition should be successful", success); + assertTrue("Actual result is not equal to expected", Arrays.equals(p1, result)); + + success = LibarkworksWrapper.getInstance().libarkworksAddG1(infinity, infinity, result); + assertTrue("Addition should be successful", success); + assertTrue("Actual result is not equal to expected", Arrays.equals(infinity, result)); + } + + @Test + public void libarkworksMulG1() { + byte[] x = decimalStringToBytes("3160407235063619464128567545019931358567547466670244075263201465289831140767"); + byte[] y = decimalStringToBytes("4182837998304898970040019544076242834539030974440226240296180009251026336602"); + byte[] p = concat(x, y); + + byte[] s = decimalStringToBytes("6279048861608643585519945505961201560698975384537397934874007738786214836023"); + + byte[] xr = decimalStringToBytes("11506148214011837992873864028743163281598579816617145449725739769326145033132"); + byte[] yr = decimalStringToBytes("2343187880277550678705288304833947923837325393017823699197655100823437890533"); + byte[] r = concat(xr, yr); + + byte[] infinity = new byte[64]; + byte[] zero = decimalStringToBytes("0"); + + byte[] result = new byte[64]; + boolean success = LibarkworksWrapper.getInstance().libarkworksMulG1(p, s, result); + assertTrue("Multiplication should be successful", success); + assertTrue("Actual result is not equal to expected", Arrays.equals(r, result)); + + success = LibarkworksWrapper.getInstance().libarkworksMulG1(infinity, s, result); + assertTrue("Multiplication should be successful", success); + assertTrue("Actual result is not equal to expected", Arrays.equals(infinity, result)); + + success = LibarkworksWrapper.getInstance().libarkworksMulG1(p, zero, result); + assertTrue("Multiplication should be successful", success); + assertTrue("Actual result is not equal to expected", Arrays.equals(infinity, result)); + } + + @Test + public void libarkworksPairingCheck() { + int pairs = 2; + byte[] g1s = hexToBytes("1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411"); + byte[] g2s = hexToBytes("04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de8775502bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c212c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b"); + System.out.println(g1s.length); + System.out.println(g2s.length); + boolean success = LibarkworksWrapper.getInstance().libarkworksPairingCheck(g1s, g2s, pairs); + assertTrue("Pairing check should be successful", success); + } + + private static byte[] decimalStringToBytes(String decimal) { + byte[] bytes = new BigInteger(decimal).toByteArray(); + return concat(new byte[32 - bytes.length], bytes); + } + + private static byte[] hexToBytes(String hex) { + return HexBin.decode(hex); + } + + private static byte[][] getG1XY(byte[] g1) { + byte[] x = Arrays.copyOfRange(g1, 0, 32); + byte[] y = Arrays.copyOfRange(g1, 32, 64); + return new byte[][] { x, y }; + } + + private static byte[][] getG2ABCD(byte[] g2) { + byte[] a = Arrays.copyOfRange(g2, 0, 32); + byte[] b = Arrays.copyOfRange(g2, 32, 64); + byte[] c = Arrays.copyOfRange(g2, 64, 96); + byte[] d = Arrays.copyOfRange(g2, 96, 128); + return new byte[][] { a, b, c, d }; + } + + private static byte[] concat(byte[] g1s, byte[] g1) { + byte[] result = Arrays.copyOf(g1s, g1s.length + g1.length); + System.arraycopy(g1, 0, result, g1s.length, g1.length); + return result; + } +}