diff --git a/icicle/CMakeLists.txt b/icicle/CMakeLists.txt index 4773ad7d3..e720caf08 100644 --- a/icicle/CMakeLists.txt +++ b/icicle/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) include(cmake/field.cmake) include(cmake/curve.cmake) +include(cmake/hash.cmake) # Set the default build type to Release if not specified if(NOT CMAKE_BUILD_TYPE) @@ -22,7 +23,7 @@ find_program(CCACHE_PROGRAM ccache) # If ccache is found, use it as the compiler launcher if(CCACHE_PROGRAM) message(STATUS "ccache found: ${CCACHE_PROGRAM}") - + # Use ccache for C and C++ compilers set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) @@ -44,6 +45,8 @@ option(ECNTT "Build ECNTT" ON) option(MSM "Build MSM" ON) option(G2 "Build G2 MSM" ON) option(EXT_FIELD "Build extension field" ON) +option(HASH "Build hashes and tree builders" ON) +option(POSEIDON "Build poseidon hash" ON) # device API library add_library(icicle_device SHARED @@ -78,7 +81,7 @@ endif() if(CURVE) set(CURVE_INDEX -1) set(FEATURES_STRING "") - check_curve(${CURVE} CURVE_INDEX FEATURES_STRING) + check_curve(${CURVE} CURVE_INDEX FEATURES_STRING) setup_curve_target(${CURVE} ${CURVE_INDEX} ${FEATURES_STRING}) elseif(FIELD) set(FIELD_INDEX -1) @@ -87,6 +90,10 @@ elseif(FIELD) setup_field_target(${FIELD} ${FIELD_INDEX} ${FEATURES_STRING}) endif() +if (HASH) + setup_hash_target() +endif() + if (CPU_BACKEND) add_subdirectory(backend/cpu) endif() @@ -94,7 +101,7 @@ endif() if (CUDA_BACKEND) string(TOLOWER "${CUDA_BACKEND}" CUDA_BACKEND_LOWER) if (CUDA_BACKEND_LOWER STREQUAL "local") - # CUDA backend is local, no need to pull + # CUDA backend is local, no need to pull message(STATUS "Adding CUDA backend from local path: icicle/backend/cuda") add_subdirectory(backend/cuda) diff --git a/icicle/backend/cpu/CMakeLists.txt b/icicle/backend/cpu/CMakeLists.txt index 5467d3da6..f4dc70430 100644 --- a/icicle/backend/cpu/CMakeLists.txt +++ b/icicle/backend/cpu/CMakeLists.txt @@ -12,6 +12,9 @@ if (FIELD) if (NTT) target_sources(icicle_field PRIVATE src/field/cpu_ntt.cpp src/polynomials/cpu_polynomial_backend.cpp) endif() + if (POSEIDON) + target_sources(icicle_field PRIVATE src/hash/cpu_poseidon.cpp) + endif() target_include_directories(icicle_field PRIVATE include) endif() # FIELD @@ -30,4 +33,10 @@ if (CURVE) target_include_directories(icicle_curve PUBLIC include) endif() +if (HASH) + target_sources(icicle_hash PRIVATE + src/hash/cpu_keccak.cpp + src/hash/cpu_merkle_tree.cpp + ) +endif() diff --git a/icicle/backend/cpu/src/hash/cpu_keccak.cpp b/icicle/backend/cpu/src/hash/cpu_keccak.cpp new file mode 100644 index 000000000..4fa6dead3 --- /dev/null +++ b/icicle/backend/cpu/src/hash/cpu_keccak.cpp @@ -0,0 +1,122 @@ + +#include "icicle/backend/hash/keccak_backend.h" + +namespace icicle { + + class KeccakBackend : public HashBackend + { + public: + KeccakBackend(uint64_t input_chunk_size, uint64_t output_size, uint64_t rate, int padding_const) + : HashBackend(output_size, input_chunk_size) + { + } + + eIcicleError hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const override + { + ICICLE_LOG_INFO << "Keccak CPU hash() called"; + // TODO implement + return eIcicleError::SUCCESS; + } + }; + + const int KECCAK_256_RATE = 136; + const int KECCAK_256_DIGEST = 4; + const int KECCAK_512_RATE = 72; + const int KECCAK_512_DIGEST = 8; + const int KECCAK_STATE_SIZE = 25; + const int KECCAK_PADDING_CONST = 1; + const int SHA3_PADDING_CONST = 6; + + class Keccak256Backend : public KeccakBackend + { + public: + Keccak256Backend(int input_chunk_size) + : KeccakBackend( + input_chunk_size, + KECCAK_256_DIGEST * sizeof(uint64_t) / sizeof(std::byte), + KECCAK_256_RATE, + KECCAK_PADDING_CONST) + { + } + }; + + class Keccak512Backend : public KeccakBackend + { + public: + Keccak512Backend(int input_chunk_size) + : KeccakBackend( + input_chunk_size, + KECCAK_512_DIGEST * sizeof(uint64_t) / sizeof(std::byte), + KECCAK_512_RATE, + KECCAK_PADDING_CONST) + { + } + }; + + class Sha3_256Backend : public KeccakBackend + { + public: + Sha3_256Backend(int input_chunk_size) + : KeccakBackend( + input_chunk_size, + KECCAK_256_DIGEST * sizeof(uint64_t) / sizeof(std::byte), + KECCAK_256_RATE, + SHA3_PADDING_CONST) + { + } + }; + + class Sha3_512Backend : public KeccakBackend + { + public: + Sha3_512Backend(int input_chunk_size) + : KeccakBackend( + input_chunk_size, + KECCAK_512_DIGEST * sizeof(uint64_t) / sizeof(std::byte), + KECCAK_512_RATE, + SHA3_PADDING_CONST) + { + } + }; + + /************************ Keccak 256 registration ************************/ + eIcicleError + create_keccak_256_hash_backend(const Device& device, uint64_t input_chunk_size, std::shared_ptr& backend) + { + backend = std::make_shared(input_chunk_size); + return eIcicleError::SUCCESS; + } + + REGISTER_KECCAK_256_FACTORY_BACKEND("CPU", create_keccak_256_hash_backend); + + /************************ Keccak 512 registration ************************/ + eIcicleError + create_keccak_512_hash_backend(const Device& device, uint64_t input_chunk_size, std::shared_ptr& backend) + { + backend = std::make_shared(input_chunk_size); + return eIcicleError::SUCCESS; + } + + REGISTER_KECCAK_512_FACTORY_BACKEND("CPU", create_keccak_512_hash_backend); + + /************************ SHA3 256 registration ************************/ + eIcicleError + create_sha3_256_hash_backend(const Device& device, uint64_t input_chunk_size, std::shared_ptr& backend) + { + backend = std::make_shared(input_chunk_size); + return eIcicleError::SUCCESS; + } + + REGISTER_SHA3_256_FACTORY_BACKEND("CPU", create_sha3_256_hash_backend); + + /************************ SHA3 512 registration ************************/ + eIcicleError + create_sha3_512_hash_backend(const Device& device, uint64_t input_chunk_size, std::shared_ptr& backend) + { + backend = std::make_shared(input_chunk_size); + return eIcicleError::SUCCESS; + } + + REGISTER_SHA3_512_FACTORY_BACKEND("CPU", create_sha3_512_hash_backend); + +} // namespace icicle \ No newline at end of file diff --git a/icicle/backend/cpu/src/hash/cpu_merkle_tree.cpp b/icicle/backend/cpu/src/hash/cpu_merkle_tree.cpp new file mode 100644 index 000000000..8d1931c10 --- /dev/null +++ b/icicle/backend/cpu/src/hash/cpu_merkle_tree.cpp @@ -0,0 +1,53 @@ +#include "icicle/backend/merkle/merkle_tree_backend.h" +#include "icicle/errors.h" +#include "icicle/utils/log.h" + +namespace icicle { + + class CPUMerkleTreeBackend : public MerkleTreeBackend + { + public: + CPUMerkleTreeBackend( + const std::vector& layer_hashes, uint64_t leaf_element_size, uint64_t output_store_min_layer = 0) + : MerkleTreeBackend(layer_hashes, leaf_element_size, output_store_min_layer) + { + } + + eIcicleError build(const std::byte* leaves, uint64_t size, const MerkleTreeConfig& config) override + { + ICICLE_LOG_INFO << "CPU CPUMerkleTreeBackend::build() called with " << size << " bytes of leaves"; + return eIcicleError::SUCCESS; // TODO: Implement tree-building logic + } + + eIcicleError get_merkle_root(std::byte* root, uint64_t root_size) const override + { + ICICLE_LOG_INFO << "CPU CPUMerkleTreeBackend::get_merkle_root() called"; + return eIcicleError::SUCCESS; // TODO: Implement root retrieval logic + } + + eIcicleError get_merkle_proof( + const std::byte* leaves, + uint64_t element_idx, + const MerkleTreeConfig& config, + MerkleProof& merkle_proof) const override + { + ICICLE_LOG_INFO << "CPU CPUMerkleTreeBackend::get_merkle_proof() called for element index " << element_idx; + return eIcicleError::SUCCESS; // TODO: Implement proof generation logic + } + }; + + eIcicleError create_merkle_tree_backend( + const Device& device, + const std::vector& layer_hashes, + uint64_t leaf_element_size, + uint64_t output_store_min_layer, + std::shared_ptr& backend) + { + ICICLE_LOG_INFO << "Creating CPU MerkleTreeBackend"; + backend = std::make_shared(layer_hashes, leaf_element_size, output_store_min_layer); + return eIcicleError::SUCCESS; + } + + REGISTER_MERKLE_TREE_FACTORY_BACKEND("CPU", create_merkle_tree_backend); + +} // namespace icicle \ No newline at end of file diff --git a/icicle/backend/cpu/src/hash/cpu_poseidon.cpp b/icicle/backend/cpu/src/hash/cpu_poseidon.cpp new file mode 100644 index 000000000..b2bafd73d --- /dev/null +++ b/icicle/backend/cpu/src/hash/cpu_poseidon.cpp @@ -0,0 +1,74 @@ +#include "icicle/backend/hash/poseidon_backend.h" +#include "icicle/utils/utils.h" + +namespace icicle { + + template + class PoseidonConstantsCPU : PoseidonConstants + { + // TODO add field here + S* m_dummy_poseidon_constant; + }; + + static eIcicleError cpu_poseidon_init_constants( + const Device& device, + unsigned arity, + unsigned alpha, + unsigned nof_partial_rounds, + unsigned nof_upper_full_rounds, + unsigned nof_end_full_rounds, + const scalar_t* rounds_constants, + const scalar_t* mds_matrix, + const scalar_t* pre_matrix, + const scalar_t* sparse_matrix, + std::shared_ptr>& constants /*out*/) + { + ICICLE_LOG_INFO << "in cpu_poseidon_init_constants()"; + // TODO implement + return eIcicleError::SUCCESS; + } + + REGISTER_POSEIDON_INIT_CONSTANTS_BACKEND("CPU", cpu_poseidon_init_constants); + + static eIcicleError cpu_poseidon_init_default_constants( + const Device& device, unsigned arity, std::shared_ptr>& constants /*out*/) + { + ICICLE_LOG_INFO << "in cpu_poseidon_init_default_constants()"; + // TODO implement + return eIcicleError::SUCCESS; + } + + REGISTER_POSEIDON_INIT_DEFAULT_CONSTANTS_BACKEND("CPU", cpu_poseidon_init_default_constants); + + template + class PoseidonBackendCPU : public HashBackend + { + public: + PoseidonBackendCPU(std::shared_ptr> constants) + : HashBackend(sizeof(S), 0 /*TODO get from constants arity of whatever*/), m_constants{constants} + { + } + + eIcicleError hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const override + { + ICICLE_LOG_INFO << "Poseidon CPU hash() " << size << " bytes, for type " << demangle(); + // TODO implement + return eIcicleError::SUCCESS; + } + + private: + std::shared_ptr> m_constants = nullptr; + }; + + static eIcicleError create_cpu_poseidon_hash_backend( + const Device& device, + std::shared_ptr> constants, + std::shared_ptr& backend /*OUT*/) + { + backend = std::make_shared>(constants); + return eIcicleError::SUCCESS; + } + + REGISTER_CREATE_POSEIDON_BACKEND("CPU", create_cpu_poseidon_hash_backend); + +} // namespace icicle \ No newline at end of file diff --git a/icicle/cmake/field.cmake b/icicle/cmake/field.cmake index 92f668539..ba3830e4b 100644 --- a/icicle/cmake/field.cmake +++ b/icicle/cmake/field.cmake @@ -50,6 +50,7 @@ function(setup_field_target FIELD FIELD_INDEX FEATURES_STRING) # Handle features handle_ntt(icicle_field "${FEATURES_LIST}") handle_ext_field(icicle_field "${FEATURES_LIST}") + handle_poseidon(icicle_field "${FEATURES_LIST}") # Add additional feature handling calls here set_target_properties(icicle_field PROPERTIES OUTPUT_NAME "icicle_field_${FIELD}") diff --git a/icicle/cmake/fields_and_curves.cmake b/icicle/cmake/fields_and_curves.cmake index e1e5a3b05..99a4b254a 100644 --- a/icicle/cmake/fields_and_curves.cmake +++ b/icicle/cmake/fields_and_curves.cmake @@ -2,17 +2,17 @@ # Define available fields with an index and their supported features # Format: index:field:features set(ICICLE_FIELDS - 1001:babybear:NTT,EXT_FIELD - 1002:stark252:NTT - 1003:m31:EXT_FIELD + 1001:babybear:NTT,EXT_FIELD,POSEIDON + 1002:stark252:NTT,POSEIDON + 1003:m31:EXT_FIELD,POSEIDON ) # Define available curves with an index and their supported features # Format: index:curve:features set(ICICLE_CURVES - 1:bn254:NTT,MSM,G2,ECNTT - 2:bls12_381:NTT,MSM,G2,ECNTT - 3:bls12_377:NTT,MSM,G2,ECNTT - 4:bw6_761:NTT,MSM,G2,ECNTT - 5:grumpkin:MSM + 1:bn254:NTT,MSM,G2,ECNTT,POSEIDON + 2:bls12_381:NTT,MSM,G2,ECNTT,POSEIDON + 3:bls12_377:NTT,MSM,G2,ECNTT,POSEIDON + 4:bw6_761:NTT,MSM,G2,ECNTT,POSEIDON + 5:grumpkin:MSM,POSEIDON ) \ No newline at end of file diff --git a/icicle/cmake/hash.cmake b/icicle/cmake/hash.cmake new file mode 100644 index 000000000..2007aa2b7 --- /dev/null +++ b/icicle/cmake/hash.cmake @@ -0,0 +1,17 @@ + + +function(setup_hash_target) + add_library(icicle_hash SHARED) + target_sources(icicle_hash PRIVATE + src/hash/keccak.cpp + src/hash/merkle_tree.cpp + ) + + target_link_libraries(icicle_hash PUBLIC icicle_device) + + install(TARGETS icicle_hash + RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/" + LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/" + ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/") +endfunction() + diff --git a/icicle/cmake/target_editor.cmake b/icicle/cmake/target_editor.cmake index 414570372..09d1932a3 100644 --- a/icicle/cmake/target_editor.cmake +++ b/icicle/cmake/target_editor.cmake @@ -28,7 +28,7 @@ function(handle_ntt TARGET FEATURE_LIST) ) set(NTT ON CACHE BOOL "Enable NTT feature" FORCE) else() - set(NTT OFF CACHE BOOL "NTT not available for this field" FORCE) + set(NTT OFF CACHE BOOL "NTT not available for this field" FORCE) endif() endfunction() @@ -63,10 +63,19 @@ endfunction() function(handle_ecntt TARGET FEATURE_LIST) if(ECNTT AND "ECNTT" IN_LIST FEATURE_LIST) target_compile_definitions(${TARGET} PUBLIC ECNTT=${ECNTT}) - target_sources(icicle_curve PRIVATE src/ecntt.cpp) + target_sources(${TARGET} PRIVATE src/ecntt.cpp) set(ECNTT ON CACHE BOOL "Enable ECNTT feature" FORCE) else() set(ECNTT OFF CACHE BOOL "ECNTT not available for this curve" FORCE) endif() endfunction() +function(handle_poseidon TARGET FEATURE_LIST) + if(POSEIDON AND "POSEIDON" IN_LIST FEATURE_LIST) + target_compile_definitions(${TARGET} PUBLIC POSEIDON=${POSEIDON}) + target_sources(${TARGET} PRIVATE src/hash/poseidon.cpp) + set(POSEIDON ON CACHE BOOL "Enable POSEIDON feature" FORCE) + else() + set(POSEIDON OFF CACHE BOOL "POSEIDON not available for this field" FORCE) + endif() +endfunction() diff --git a/icicle/include/icicle/api/babybear.h b/icicle/include/icicle/api/babybear.h index c0104443e..b0a8108cb 100644 --- a/icicle/include/icicle/api/babybear.h +++ b/icicle/include/icicle/api/babybear.h @@ -9,22 +9,37 @@ #include "icicle/ntt.h" #include "icicle/vec_ops.h" -extern "C" eIcicleError babybear_ntt_init_domain( - babybear::scalar_t* primitive_root, const NTTInitDomainConfig* config); +extern "C" eIcicleError babybear_ntt_init_domain(babybear::scalar_t* primitive_root, const NTTInitDomainConfig* config); extern "C" eIcicleError babybear_ntt( - const babybear::scalar_t* input, int size, NTTDir dir, const NTTConfig* config, babybear::scalar_t* output); + const babybear::scalar_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + babybear::scalar_t* output); extern "C" eIcicleError babybear_ntt_release_domain(); extern "C" eIcicleError babybear_vector_mul( - const babybear::scalar_t* vec_a, const babybear::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, babybear::scalar_t* result); + const babybear::scalar_t* vec_a, + const babybear::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + babybear::scalar_t* result); extern "C" eIcicleError babybear_vector_add( - const babybear::scalar_t* vec_a, const babybear::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, babybear::scalar_t* result); + const babybear::scalar_t* vec_a, + const babybear::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + babybear::scalar_t* result); extern "C" eIcicleError babybear_vector_sub( - const babybear::scalar_t* vec_a, const babybear::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, babybear::scalar_t* result); + const babybear::scalar_t* vec_a, + const babybear::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + babybear::scalar_t* result); extern "C" eIcicleError babybear_matrix_transpose( const babybear::scalar_t* input, @@ -36,15 +51,26 @@ extern "C" eIcicleError babybear_matrix_transpose( extern "C" eIcicleError babybear_bit_reverse( const babybear::scalar_t* input, uint64_t n, const VecOpsConfig* config, babybear::scalar_t* output); - extern "C" eIcicleError babybear_extension_vector_mul( - const babybear::extension_t* vec_a, const babybear::extension_t* vec_b, uint64_t n, const VecOpsConfig* config, babybear::extension_t* result); + const babybear::extension_t* vec_a, + const babybear::extension_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + babybear::extension_t* result); extern "C" eIcicleError babybear_extension_vector_add( - const babybear::extension_t* vec_a, const babybear::extension_t* vec_b, uint64_t n, const VecOpsConfig* config, babybear::extension_t* result); + const babybear::extension_t* vec_a, + const babybear::extension_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + babybear::extension_t* result); extern "C" eIcicleError babybear_extension_vector_sub( - const babybear::extension_t* vec_a, const babybear::extension_t* vec_b, uint64_t n, const VecOpsConfig* config, babybear::extension_t* result); + const babybear::extension_t* vec_a, + const babybear::extension_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + babybear::extension_t* result); extern "C" eIcicleError babybear_extension_matrix_transpose( const babybear::extension_t* input, @@ -56,18 +82,23 @@ extern "C" eIcicleError babybear_extension_matrix_transpose( extern "C" eIcicleError babybear_extension_bit_reverse( const babybear::extension_t* input, uint64_t n, const VecOpsConfig* config, babybear::extension_t* output); - extern "C" void babybear_extension_generate_scalars(babybear::extension_t* scalars, int size); -extern "C" eIcicleError babybear_extension_scalar_convert_montgomery( - const babybear::extension_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, babybear::extension_t* output); +extern "C" eIcicleError babybear_extension_scalar_convert_montgomery( + const babybear::extension_t* input, + uint64_t size, + bool is_into, + const VecOpsConfig* config, + babybear::extension_t* output); extern "C" eIcicleError babybear_extension_ntt( - const babybear::extension_t* input, int size, NTTDir dir, const NTTConfig* config, babybear::extension_t* output); - + const babybear::extension_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + babybear::extension_t* output); extern "C" void babybear_generate_scalars(babybear::scalar_t* scalars, int size); extern "C" void babybear_scalar_convert_montgomery( const babybear::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, babybear::scalar_t* output); - diff --git a/icicle/include/icicle/api/bls12_377.h b/icicle/include/icicle/api/bls12_377.h index 8287a5102..972bd59e2 100644 --- a/icicle/include/icicle/api/bls12_377.h +++ b/icicle/include/icicle/api/bls12_377.h @@ -22,20 +22,28 @@ extern "C" eIcicleError bls12_377_affine_convert_montgomery( const bls12_377::affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_377::affine_t* output); extern "C" eIcicleError bls12_377_projective_convert_montgomery( - const bls12_377::projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_377::projective_t* output); + const bls12_377::projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bls12_377::projective_t* output); extern "C" eIcicleError bls12_377_ecntt( - const bls12_377::projective_t* input, int size, NTTDir dir, const NTTConfig* config, bls12_377::projective_t* output); - + const bls12_377::projective_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bls12_377::projective_t* output); extern "C" eIcicleError bls12_377_precompute_msm_bases( - const bls12_377::affine_t* bases, - int nof_bases, - const MSMConfig* config, - bls12_377::affine_t* output_bases); + const bls12_377::affine_t* bases, int nof_bases, const MSMConfig* config, bls12_377::affine_t* output_bases); extern "C" eIcicleError bls12_377_msm( - const bls12_377::scalar_t* scalars, const bls12_377::affine_t* points, int msm_size, const MSMConfig* config, bls12_377::projective_t* out); + const bls12_377::scalar_t* scalars, + const bls12_377::affine_t* points, + int msm_size, + const MSMConfig* config, + bls12_377::projective_t* out); extern "C" bool bls12_377_g2_eq(bls12_377::g2_projective_t* point1, bls12_377::g2_projective_t* point2); @@ -46,36 +54,61 @@ extern "C" void bls12_377_g2_generate_projective_points(bls12_377::g2_projective extern "C" void bls12_377_g2_generate_affine_points(bls12_377::g2_affine_t* points, int size); extern "C" eIcicleError bls12_377_g2_affine_convert_montgomery( - const bls12_377::g2_affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_377::g2_affine_t* output); + const bls12_377::g2_affine_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bls12_377::g2_affine_t* output); extern "C" eIcicleError bls12_377_g2_projective_convert_montgomery( - const bls12_377::g2_projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_377::g2_projective_t* output); + const bls12_377::g2_projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bls12_377::g2_projective_t* output); extern "C" eIcicleError bls12_377_g2_precompute_msm_bases( - const bls12_377::g2_affine_t* bases, - int nof_bases, - const MSMConfig* config, - bls12_377::g2_affine_t* output_bases); + const bls12_377::g2_affine_t* bases, int nof_bases, const MSMConfig* config, bls12_377::g2_affine_t* output_bases); extern "C" eIcicleError bls12_377_g2_msm( - const bls12_377::scalar_t* scalars, const bls12_377::g2_affine_t* points, int msm_size, const MSMConfig* config, bls12_377::g2_projective_t* out); + const bls12_377::scalar_t* scalars, + const bls12_377::g2_affine_t* points, + int msm_size, + const MSMConfig* config, + bls12_377::g2_projective_t* out); -extern "C" eIcicleError bls12_377_ntt_init_domain( - bls12_377::scalar_t* primitive_root, const NTTInitDomainConfig* config); +extern "C" eIcicleError +bls12_377_ntt_init_domain(bls12_377::scalar_t* primitive_root, const NTTInitDomainConfig* config); extern "C" eIcicleError bls12_377_ntt( - const bls12_377::scalar_t* input, int size, NTTDir dir, const NTTConfig* config, bls12_377::scalar_t* output); + const bls12_377::scalar_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bls12_377::scalar_t* output); extern "C" eIcicleError bls12_377_ntt_release_domain(); extern "C" eIcicleError bls12_377_vector_mul( - const bls12_377::scalar_t* vec_a, const bls12_377::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bls12_377::scalar_t* result); + const bls12_377::scalar_t* vec_a, + const bls12_377::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bls12_377::scalar_t* result); extern "C" eIcicleError bls12_377_vector_add( - const bls12_377::scalar_t* vec_a, const bls12_377::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bls12_377::scalar_t* result); + const bls12_377::scalar_t* vec_a, + const bls12_377::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bls12_377::scalar_t* result); extern "C" eIcicleError bls12_377_vector_sub( - const bls12_377::scalar_t* vec_a, const bls12_377::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bls12_377::scalar_t* result); + const bls12_377::scalar_t* vec_a, + const bls12_377::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bls12_377::scalar_t* result); extern "C" eIcicleError bls12_377_matrix_transpose( const bls12_377::scalar_t* input, @@ -87,9 +120,11 @@ extern "C" eIcicleError bls12_377_matrix_transpose( extern "C" eIcicleError bls12_377_bit_reverse( const bls12_377::scalar_t* input, uint64_t n, const VecOpsConfig* config, bls12_377::scalar_t* output); - extern "C" void bls12_377_generate_scalars(bls12_377::scalar_t* scalars, int size); extern "C" void bls12_377_scalar_convert_montgomery( - const bls12_377::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, bls12_377::scalar_t* output); - + const bls12_377::scalar_t* input, + uint64_t size, + bool is_into, + const VecOpsConfig* config, + bls12_377::scalar_t* output); diff --git a/icicle/include/icicle/api/bls12_381.h b/icicle/include/icicle/api/bls12_381.h index d2b7d6999..03e3bdd36 100644 --- a/icicle/include/icicle/api/bls12_381.h +++ b/icicle/include/icicle/api/bls12_381.h @@ -22,20 +22,28 @@ extern "C" eIcicleError bls12_381_affine_convert_montgomery( const bls12_381::affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_381::affine_t* output); extern "C" eIcicleError bls12_381_projective_convert_montgomery( - const bls12_381::projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_381::projective_t* output); + const bls12_381::projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bls12_381::projective_t* output); extern "C" eIcicleError bls12_381_ecntt( - const bls12_381::projective_t* input, int size, NTTDir dir, const NTTConfig* config, bls12_381::projective_t* output); - + const bls12_381::projective_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bls12_381::projective_t* output); extern "C" eIcicleError bls12_381_precompute_msm_bases( - const bls12_381::affine_t* bases, - int nof_bases, - const MSMConfig* config, - bls12_381::affine_t* output_bases); + const bls12_381::affine_t* bases, int nof_bases, const MSMConfig* config, bls12_381::affine_t* output_bases); extern "C" eIcicleError bls12_381_msm( - const bls12_381::scalar_t* scalars, const bls12_381::affine_t* points, int msm_size, const MSMConfig* config, bls12_381::projective_t* out); + const bls12_381::scalar_t* scalars, + const bls12_381::affine_t* points, + int msm_size, + const MSMConfig* config, + bls12_381::projective_t* out); extern "C" bool bls12_381_g2_eq(bls12_381::g2_projective_t* point1, bls12_381::g2_projective_t* point2); @@ -46,36 +54,61 @@ extern "C" void bls12_381_g2_generate_projective_points(bls12_381::g2_projective extern "C" void bls12_381_g2_generate_affine_points(bls12_381::g2_affine_t* points, int size); extern "C" eIcicleError bls12_381_g2_affine_convert_montgomery( - const bls12_381::g2_affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_381::g2_affine_t* output); + const bls12_381::g2_affine_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bls12_381::g2_affine_t* output); extern "C" eIcicleError bls12_381_g2_projective_convert_montgomery( - const bls12_381::g2_projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bls12_381::g2_projective_t* output); + const bls12_381::g2_projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bls12_381::g2_projective_t* output); extern "C" eIcicleError bls12_381_g2_precompute_msm_bases( - const bls12_381::g2_affine_t* bases, - int nof_bases, - const MSMConfig* config, - bls12_381::g2_affine_t* output_bases); + const bls12_381::g2_affine_t* bases, int nof_bases, const MSMConfig* config, bls12_381::g2_affine_t* output_bases); extern "C" eIcicleError bls12_381_g2_msm( - const bls12_381::scalar_t* scalars, const bls12_381::g2_affine_t* points, int msm_size, const MSMConfig* config, bls12_381::g2_projective_t* out); + const bls12_381::scalar_t* scalars, + const bls12_381::g2_affine_t* points, + int msm_size, + const MSMConfig* config, + bls12_381::g2_projective_t* out); -extern "C" eIcicleError bls12_381_ntt_init_domain( - bls12_381::scalar_t* primitive_root, const NTTInitDomainConfig* config); +extern "C" eIcicleError +bls12_381_ntt_init_domain(bls12_381::scalar_t* primitive_root, const NTTInitDomainConfig* config); extern "C" eIcicleError bls12_381_ntt( - const bls12_381::scalar_t* input, int size, NTTDir dir, const NTTConfig* config, bls12_381::scalar_t* output); + const bls12_381::scalar_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bls12_381::scalar_t* output); extern "C" eIcicleError bls12_381_ntt_release_domain(); extern "C" eIcicleError bls12_381_vector_mul( - const bls12_381::scalar_t* vec_a, const bls12_381::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bls12_381::scalar_t* result); + const bls12_381::scalar_t* vec_a, + const bls12_381::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bls12_381::scalar_t* result); extern "C" eIcicleError bls12_381_vector_add( - const bls12_381::scalar_t* vec_a, const bls12_381::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bls12_381::scalar_t* result); + const bls12_381::scalar_t* vec_a, + const bls12_381::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bls12_381::scalar_t* result); extern "C" eIcicleError bls12_381_vector_sub( - const bls12_381::scalar_t* vec_a, const bls12_381::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bls12_381::scalar_t* result); + const bls12_381::scalar_t* vec_a, + const bls12_381::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bls12_381::scalar_t* result); extern "C" eIcicleError bls12_381_matrix_transpose( const bls12_381::scalar_t* input, @@ -87,9 +120,11 @@ extern "C" eIcicleError bls12_381_matrix_transpose( extern "C" eIcicleError bls12_381_bit_reverse( const bls12_381::scalar_t* input, uint64_t n, const VecOpsConfig* config, bls12_381::scalar_t* output); - extern "C" void bls12_381_generate_scalars(bls12_381::scalar_t* scalars, int size); extern "C" void bls12_381_scalar_convert_montgomery( - const bls12_381::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, bls12_381::scalar_t* output); - + const bls12_381::scalar_t* input, + uint64_t size, + bool is_into, + const VecOpsConfig* config, + bls12_381::scalar_t* output); diff --git a/icicle/include/icicle/api/bn254.h b/icicle/include/icicle/api/bn254.h index d054f23b4..e9d26c299 100644 --- a/icicle/include/icicle/api/bn254.h +++ b/icicle/include/icicle/api/bn254.h @@ -22,20 +22,24 @@ extern "C" eIcicleError bn254_affine_convert_montgomery( const bn254::affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bn254::affine_t* output); extern "C" eIcicleError bn254_projective_convert_montgomery( - const bn254::projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bn254::projective_t* output); + const bn254::projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bn254::projective_t* output); extern "C" eIcicleError bn254_ecntt( - const bn254::projective_t* input, int size, NTTDir dir, const NTTConfig* config, bn254::projective_t* output); - + const bn254::projective_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bn254::projective_t* output); extern "C" eIcicleError bn254_precompute_msm_bases( - const bn254::affine_t* bases, - int nof_bases, - const MSMConfig* config, - bn254::affine_t* output_bases); + const bn254::affine_t* bases, int nof_bases, const MSMConfig* config, bn254::affine_t* output_bases); extern "C" eIcicleError bn254_msm( - const bn254::scalar_t* scalars, const bn254::affine_t* points, int msm_size, const MSMConfig* config, bn254::projective_t* out); + const bn254::scalar_t* scalars, + const bn254::affine_t* points, + int msm_size, + const MSMConfig* config, + bn254::projective_t* out); extern "C" bool bn254_g2_eq(bn254::g2_projective_t* point1, bn254::g2_projective_t* point2); @@ -49,33 +53,53 @@ extern "C" eIcicleError bn254_g2_affine_convert_montgomery( const bn254::g2_affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bn254::g2_affine_t* output); extern "C" eIcicleError bn254_g2_projective_convert_montgomery( - const bn254::g2_projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bn254::g2_projective_t* output); + const bn254::g2_projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bn254::g2_projective_t* output); extern "C" eIcicleError bn254_g2_precompute_msm_bases( - const bn254::g2_affine_t* bases, - int nof_bases, - const MSMConfig* config, - bn254::g2_affine_t* output_bases); + const bn254::g2_affine_t* bases, int nof_bases, const MSMConfig* config, bn254::g2_affine_t* output_bases); extern "C" eIcicleError bn254_g2_msm( - const bn254::scalar_t* scalars, const bn254::g2_affine_t* points, int msm_size, const MSMConfig* config, bn254::g2_projective_t* out); + const bn254::scalar_t* scalars, + const bn254::g2_affine_t* points, + int msm_size, + const MSMConfig* config, + bn254::g2_projective_t* out); -extern "C" eIcicleError bn254_ntt_init_domain( - bn254::scalar_t* primitive_root, const NTTInitDomainConfig* config); +extern "C" eIcicleError bn254_ntt_init_domain(bn254::scalar_t* primitive_root, const NTTInitDomainConfig* config); extern "C" eIcicleError bn254_ntt( - const bn254::scalar_t* input, int size, NTTDir dir, const NTTConfig* config, bn254::scalar_t* output); + const bn254::scalar_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bn254::scalar_t* output); extern "C" eIcicleError bn254_ntt_release_domain(); extern "C" eIcicleError bn254_vector_mul( - const bn254::scalar_t* vec_a, const bn254::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bn254::scalar_t* result); + const bn254::scalar_t* vec_a, + const bn254::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bn254::scalar_t* result); extern "C" eIcicleError bn254_vector_add( - const bn254::scalar_t* vec_a, const bn254::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bn254::scalar_t* result); + const bn254::scalar_t* vec_a, + const bn254::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bn254::scalar_t* result); extern "C" eIcicleError bn254_vector_sub( - const bn254::scalar_t* vec_a, const bn254::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bn254::scalar_t* result); + const bn254::scalar_t* vec_a, + const bn254::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bn254::scalar_t* result); extern "C" eIcicleError bn254_matrix_transpose( const bn254::scalar_t* input, @@ -84,12 +108,10 @@ extern "C" eIcicleError bn254_matrix_transpose( const VecOpsConfig* config, bn254::scalar_t* output); -extern "C" eIcicleError bn254_bit_reverse( - const bn254::scalar_t* input, uint64_t n, const VecOpsConfig* config, bn254::scalar_t* output); - +extern "C" eIcicleError +bn254_bit_reverse(const bn254::scalar_t* input, uint64_t n, const VecOpsConfig* config, bn254::scalar_t* output); extern "C" void bn254_generate_scalars(bn254::scalar_t* scalars, int size); extern "C" void bn254_scalar_convert_montgomery( const bn254::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, bn254::scalar_t* output); - diff --git a/icicle/include/icicle/api/bw6_761.h b/icicle/include/icicle/api/bw6_761.h index 31d3b87e2..6d0226c57 100644 --- a/icicle/include/icicle/api/bw6_761.h +++ b/icicle/include/icicle/api/bw6_761.h @@ -22,20 +22,28 @@ extern "C" eIcicleError bw6_761_affine_convert_montgomery( const bw6_761::affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bw6_761::affine_t* output); extern "C" eIcicleError bw6_761_projective_convert_montgomery( - const bw6_761::projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bw6_761::projective_t* output); + const bw6_761::projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bw6_761::projective_t* output); extern "C" eIcicleError bw6_761_ecntt( - const bw6_761::projective_t* input, int size, NTTDir dir, const NTTConfig* config, bw6_761::projective_t* output); - + const bw6_761::projective_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bw6_761::projective_t* output); extern "C" eIcicleError bw6_761_precompute_msm_bases( - const bw6_761::affine_t* bases, - int nof_bases, - const MSMConfig* config, - bw6_761::affine_t* output_bases); + const bw6_761::affine_t* bases, int nof_bases, const MSMConfig* config, bw6_761::affine_t* output_bases); extern "C" eIcicleError bw6_761_msm( - const bw6_761::scalar_t* scalars, const bw6_761::affine_t* points, int msm_size, const MSMConfig* config, bw6_761::projective_t* out); + const bw6_761::scalar_t* scalars, + const bw6_761::affine_t* points, + int msm_size, + const MSMConfig* config, + bw6_761::projective_t* out); extern "C" bool bw6_761_g2_eq(bw6_761::g2_projective_t* point1, bw6_761::g2_projective_t* point2); @@ -49,33 +57,53 @@ extern "C" eIcicleError bw6_761_g2_affine_convert_montgomery( const bw6_761::g2_affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, bw6_761::g2_affine_t* output); extern "C" eIcicleError bw6_761_g2_projective_convert_montgomery( - const bw6_761::g2_projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, bw6_761::g2_projective_t* output); + const bw6_761::g2_projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + bw6_761::g2_projective_t* output); extern "C" eIcicleError bw6_761_g2_precompute_msm_bases( - const bw6_761::g2_affine_t* bases, - int nof_bases, - const MSMConfig* config, - bw6_761::g2_affine_t* output_bases); + const bw6_761::g2_affine_t* bases, int nof_bases, const MSMConfig* config, bw6_761::g2_affine_t* output_bases); extern "C" eIcicleError bw6_761_g2_msm( - const bw6_761::scalar_t* scalars, const bw6_761::g2_affine_t* points, int msm_size, const MSMConfig* config, bw6_761::g2_projective_t* out); + const bw6_761::scalar_t* scalars, + const bw6_761::g2_affine_t* points, + int msm_size, + const MSMConfig* config, + bw6_761::g2_projective_t* out); -extern "C" eIcicleError bw6_761_ntt_init_domain( - bw6_761::scalar_t* primitive_root, const NTTInitDomainConfig* config); +extern "C" eIcicleError bw6_761_ntt_init_domain(bw6_761::scalar_t* primitive_root, const NTTInitDomainConfig* config); extern "C" eIcicleError bw6_761_ntt( - const bw6_761::scalar_t* input, int size, NTTDir dir, const NTTConfig* config, bw6_761::scalar_t* output); + const bw6_761::scalar_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + bw6_761::scalar_t* output); extern "C" eIcicleError bw6_761_ntt_release_domain(); extern "C" eIcicleError bw6_761_vector_mul( - const bw6_761::scalar_t* vec_a, const bw6_761::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bw6_761::scalar_t* result); + const bw6_761::scalar_t* vec_a, + const bw6_761::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bw6_761::scalar_t* result); extern "C" eIcicleError bw6_761_vector_add( - const bw6_761::scalar_t* vec_a, const bw6_761::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bw6_761::scalar_t* result); + const bw6_761::scalar_t* vec_a, + const bw6_761::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bw6_761::scalar_t* result); extern "C" eIcicleError bw6_761_vector_sub( - const bw6_761::scalar_t* vec_a, const bw6_761::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, bw6_761::scalar_t* result); + const bw6_761::scalar_t* vec_a, + const bw6_761::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + bw6_761::scalar_t* result); extern "C" eIcicleError bw6_761_matrix_transpose( const bw6_761::scalar_t* input, @@ -84,12 +112,10 @@ extern "C" eIcicleError bw6_761_matrix_transpose( const VecOpsConfig* config, bw6_761::scalar_t* output); -extern "C" eIcicleError bw6_761_bit_reverse( - const bw6_761::scalar_t* input, uint64_t n, const VecOpsConfig* config, bw6_761::scalar_t* output); - +extern "C" eIcicleError +bw6_761_bit_reverse(const bw6_761::scalar_t* input, uint64_t n, const VecOpsConfig* config, bw6_761::scalar_t* output); extern "C" void bw6_761_generate_scalars(bw6_761::scalar_t* scalars, int size); extern "C" void bw6_761_scalar_convert_montgomery( const bw6_761::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, bw6_761::scalar_t* output); - diff --git a/icicle/include/icicle/api/grumpkin.h b/icicle/include/icicle/api/grumpkin.h index 9908e492b..235b72843 100644 --- a/icicle/include/icicle/api/grumpkin.h +++ b/icicle/include/icicle/api/grumpkin.h @@ -21,25 +21,42 @@ extern "C" eIcicleError grumpkin_affine_convert_montgomery( const grumpkin::affine_t* input, size_t n, bool is_into, const VecOpsConfig* config, grumpkin::affine_t* output); extern "C" eIcicleError grumpkin_projective_convert_montgomery( - const grumpkin::projective_t* input, size_t n, bool is_into, const VecOpsConfig* config, grumpkin::projective_t* output); + const grumpkin::projective_t* input, + size_t n, + bool is_into, + const VecOpsConfig* config, + grumpkin::projective_t* output); extern "C" eIcicleError grumpkin_precompute_msm_bases( - const grumpkin::affine_t* bases, - int nof_bases, - const MSMConfig* config, - grumpkin::affine_t* output_bases); + const grumpkin::affine_t* bases, int nof_bases, const MSMConfig* config, grumpkin::affine_t* output_bases); extern "C" eIcicleError grumpkin_msm( - const grumpkin::scalar_t* scalars, const grumpkin::affine_t* points, int msm_size, const MSMConfig* config, grumpkin::projective_t* out); + const grumpkin::scalar_t* scalars, + const grumpkin::affine_t* points, + int msm_size, + const MSMConfig* config, + grumpkin::projective_t* out); extern "C" eIcicleError grumpkin_vector_mul( - const grumpkin::scalar_t* vec_a, const grumpkin::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, grumpkin::scalar_t* result); + const grumpkin::scalar_t* vec_a, + const grumpkin::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + grumpkin::scalar_t* result); extern "C" eIcicleError grumpkin_vector_add( - const grumpkin::scalar_t* vec_a, const grumpkin::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, grumpkin::scalar_t* result); + const grumpkin::scalar_t* vec_a, + const grumpkin::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + grumpkin::scalar_t* result); extern "C" eIcicleError grumpkin_vector_sub( - const grumpkin::scalar_t* vec_a, const grumpkin::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, grumpkin::scalar_t* result); + const grumpkin::scalar_t* vec_a, + const grumpkin::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + grumpkin::scalar_t* result); extern "C" eIcicleError grumpkin_matrix_transpose( const grumpkin::scalar_t* input, @@ -51,9 +68,7 @@ extern "C" eIcicleError grumpkin_matrix_transpose( extern "C" eIcicleError grumpkin_bit_reverse( const grumpkin::scalar_t* input, uint64_t n, const VecOpsConfig* config, grumpkin::scalar_t* output); - extern "C" void grumpkin_generate_scalars(grumpkin::scalar_t* scalars, int size); extern "C" void grumpkin_scalar_convert_montgomery( const grumpkin::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, grumpkin::scalar_t* output); - diff --git a/icicle/include/icicle/api/stark252.h b/icicle/include/icicle/api/stark252.h index 3bbe9626f..cd4d190ab 100644 --- a/icicle/include/icicle/api/stark252.h +++ b/icicle/include/icicle/api/stark252.h @@ -9,22 +9,37 @@ #include "icicle/ntt.h" #include "icicle/vec_ops.h" -extern "C" eIcicleError stark252_ntt_init_domain( - stark252::scalar_t* primitive_root, const NTTInitDomainConfig* config); +extern "C" eIcicleError stark252_ntt_init_domain(stark252::scalar_t* primitive_root, const NTTInitDomainConfig* config); extern "C" eIcicleError stark252_ntt( - const stark252::scalar_t* input, int size, NTTDir dir, const NTTConfig* config, stark252::scalar_t* output); + const stark252::scalar_t* input, + int size, + NTTDir dir, + const NTTConfig* config, + stark252::scalar_t* output); extern "C" eIcicleError stark252_ntt_release_domain(); extern "C" eIcicleError stark252_vector_mul( - const stark252::scalar_t* vec_a, const stark252::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, stark252::scalar_t* result); + const stark252::scalar_t* vec_a, + const stark252::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + stark252::scalar_t* result); extern "C" eIcicleError stark252_vector_add( - const stark252::scalar_t* vec_a, const stark252::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, stark252::scalar_t* result); + const stark252::scalar_t* vec_a, + const stark252::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + stark252::scalar_t* result); extern "C" eIcicleError stark252_vector_sub( - const stark252::scalar_t* vec_a, const stark252::scalar_t* vec_b, uint64_t n, const VecOpsConfig* config, stark252::scalar_t* result); + const stark252::scalar_t* vec_a, + const stark252::scalar_t* vec_b, + uint64_t n, + const VecOpsConfig* config, + stark252::scalar_t* result); extern "C" eIcicleError stark252_matrix_transpose( const stark252::scalar_t* input, @@ -36,9 +51,7 @@ extern "C" eIcicleError stark252_matrix_transpose( extern "C" eIcicleError stark252_bit_reverse( const stark252::scalar_t* input, uint64_t n, const VecOpsConfig* config, stark252::scalar_t* output); - extern "C" void stark252_generate_scalars(stark252::scalar_t* scalars, int size); extern "C" void stark252_scalar_convert_montgomery( const stark252::scalar_t* input, uint64_t size, bool is_into, const VecOpsConfig* config, stark252::scalar_t* output); - diff --git a/icicle/include/icicle/backend/hash/hash_backend.h b/icicle/include/icicle/backend/hash/hash_backend.h new file mode 100644 index 000000000..9fa4310df --- /dev/null +++ b/icicle/include/icicle/backend/hash/hash_backend.h @@ -0,0 +1,74 @@ +#pragma once + +#include "icicle/hash/hash_config.h" +#include +#include + +namespace icicle { + + /** + * @brief Abstract base class for device-specific hash function implementations. + * + * This class serves as the backend for hashing operations and must be derived + * by any specific hash implementation (e.g., Keccak, Blake2, Poseidon). Each + * derived class should implement the hashing logic for a particular device or environment. + * + */ + class HashBackend + { + public: + /** + * @brief Constructor for the HashBackend class. + * + * @param output_size The size of the output in bytes. + * @param default_input_chunk_size The default size of a single input chunk in bytes. Useful for Merkle trees. + */ + HashBackend(uint64_t output_size, uint64_t default_input_chunk_size = 0) + : m_output_size{output_size}, m_default_input_chunk_size{default_input_chunk_size} + { + } + + /** + * @brief Virtual destructor for cleanup in derived classes. + */ + virtual ~HashBackend() = default; + + /** + * @brief Perform a hash operation on a single chunk of input data. + * + * @param input Pointer to the input data in bytes. + * @param size Number of bytes to hash. For batch case, this is a single chunk of data. Set 0 for default chunk + * size. + * @param config Hash configuration (e.g., async, device location). + * @param output Pointer to the output data in bytes. + * @return An error code of type eIcicleError indicating the result of the operation. + */ + virtual eIcicleError + hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const = 0; + + /** + * @brief Get the default input chunk size. + * @return The size of the input chunk in bytes. + */ + inline uint64_t input_default_chunk_size() const { return m_default_input_chunk_size; } + + /** + * @brief Get the output size in bytes for a single hash chunk. + * @return The output size in bytes. + */ + uint64_t output_size() const { return m_output_size; } + + protected: + const uint64_t m_output_size; ///< The number of output bytes produced by the hash. + const uint64_t m_default_input_chunk_size; ///< Expected input chunk size for hashing operations. + + inline uint64_t get_single_chunk_size(uint64_t size_or_zero) const + { + auto size = (size_or_zero == 0) ? input_default_chunk_size() : size_or_zero; + ICICLE_ASSERT(size > 0) << "Cannot infer hash size. Make sure to pass it to hasher.hash(...size...) or have " + "default size for the hasher"; + return size; + } + }; + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/backend/hash/keccak_backend.h b/icicle/include/icicle/backend/hash/keccak_backend.h new file mode 100644 index 000000000..82c1160da --- /dev/null +++ b/icicle/include/icicle/backend/hash/keccak_backend.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include "icicle/utils/utils.h" +#include "icicle/device.h" +#include "icicle/hash/keccak.h" + +namespace icicle { + + /*************************** Backend registration ***************************/ + using KeccakFactoryImpl = std::function& backend /*OUT*/)>; + + // Keccak 256 + void register_keccak_256_factory(const std::string& deviceType, KeccakFactoryImpl impl); + +#define REGISTER_KECCAK_256_FACTORY_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_keccak256) = []() -> bool { \ + register_keccak_256_factory(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + + // Keccak 512 + void register_keccak_512_factory(const std::string& deviceType, KeccakFactoryImpl impl); + +#define REGISTER_KECCAK_512_FACTORY_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_keccak512) = []() -> bool { \ + register_keccak_512_factory(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + + // SHA3 256 + void register_sha3_256_factory(const std::string& deviceType, KeccakFactoryImpl impl); + +#define REGISTER_SHA3_256_FACTORY_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_sha3_256) = []() -> bool { \ + register_sha3_256_factory(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + + // SHA3 512 + void register_sha3_512_factory(const std::string& deviceType, KeccakFactoryImpl impl); + +#define REGISTER_SHA3_512_FACTORY_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_sha3_512) = []() -> bool { \ + register_sha3_512_factory(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/backend/hash/poseidon_backend.h b/icicle/include/icicle/backend/hash/poseidon_backend.h new file mode 100644 index 000000000..485fe3858 --- /dev/null +++ b/icicle/include/icicle/backend/hash/poseidon_backend.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include "icicle/utils/utils.h" +#include "icicle/device.h" +#include "icicle/hash/poseidon.h" + +#include "icicle/fields/field_config.h" +using namespace field_config; + +namespace icicle { + + /*************************** Backend registration ***************************/ + + using InitPoseidonConstantsImpl = std::function>& constants /*out*/)>; + + // poseidon init constants + void register_poseidon_init_constants(const std::string& deviceType, InitPoseidonConstantsImpl impl); + +#define REGISTER_POSEIDON_INIT_CONSTANTS_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_poseidon_init_constants) = []() -> bool { \ + register_poseidon_init_constants(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + + using InitPoseidonDefaultConstantsImpl = std::function>& constants /*out*/)>; + + // poseidon init constants + void register_poseidon_init_default_constants(const std::string& deviceType, InitPoseidonDefaultConstantsImpl impl); + +#define REGISTER_POSEIDON_INIT_DEFAULT_CONSTANTS_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_poseidon_init_default_constants) = []() -> bool { \ + register_poseidon_init_default_constants(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + + using CreatePoseidonImpl = std::function>, std::shared_ptr& /*OUT*/)>; + + // poseidon init constants + void register_create_poseidon(const std::string& deviceType, CreatePoseidonImpl impl); + +#define REGISTER_CREATE_POSEIDON_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_create_poseidon) = []() -> bool { \ + register_create_poseidon(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/backend/merkle/merkle_tree_backend.h b/icicle/include/icicle/backend/merkle/merkle_tree_backend.h new file mode 100644 index 000000000..15876fdd1 --- /dev/null +++ b/icicle/include/icicle/backend/merkle/merkle_tree_backend.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include "icicle/hash/hash.h" +#include "icicle/merkle/merkle_tree_config.h" +#include "icicle/merkle/merkle_proof.h" + +namespace icicle { + + /** + * @brief Abstract base class for Merkle tree backend implementations. + * + * This backend handles the core logic for Merkle tree operations such as building the tree, + * retrieving the root, computing Merkle paths, and verifying elements. Derived classes + * will provide specific implementations for various devices (e.g., CPU, GPU). + */ + class MerkleTreeBackend + { + public: + /** + * @brief Constructor for the MerkleTreeBackend class. + * + * @param layer_hashes Vector of Hash objects representing the hash function for each layer. + * @param leaf_element_size Size of each leaf element in bytes. + * @param output_store_min_layer Minimum layer index to store in the output (default is 0). + */ + MerkleTreeBackend( + const std::vector& layer_hashes, uint64_t leaf_element_size, uint64_t output_store_min_layer = 0) + : m_layer_hashes(layer_hashes), m_leaf_element_size(leaf_element_size), + m_output_store_min_layer(output_store_min_layer) + { + } + + virtual ~MerkleTreeBackend() = default; + + /** + * @brief Build the Merkle tree from the provided leaves. + * @param leaves Pointer to the leaves of the tree (input data). + * @param size The size of the leaves. + * @param config Configuration for the Merkle tree operation. + * @return Error code of type eIcicleError indicating success or failure. + */ + virtual eIcicleError build(const std::byte* leaves, uint64_t size, const MerkleTreeConfig& config) = 0; + + /** + * @brief Retrieve the root of the Merkle tree. + * @param root Pointer to where the Merkle root will be written. + * @param root_size The size of the root in bytes. + * @return Error code of type eIcicleError. + */ + virtual eIcicleError get_merkle_root(std::byte* root, uint64_t root_size) const = 0; + + /** + * @brief Retrieve the Merkle path for a specific element. + * @param leaves Pointer to the leaves of the tree. + * @param element_idx Index of the element for which the Merkle path is required. + * @param config Configuration for the Merkle tree operation. + * @param merkle_proof Reference to the MerkleProof object where the path will be stored. + * @return Error code of type eIcicleError. + */ + virtual eIcicleError get_merkle_proof( + const std::byte* leaves, + uint64_t element_idx, + const MerkleTreeConfig& config, + MerkleProof& merkle_proof /*output*/) const = 0; + + /** + * @brief Get the hash functions used for each layer of the Merkle tree. + * + * @return A vector of Hash objects representing the hash function for each layer. + */ + const std::vector& get_layer_hashes() const { return m_layer_hashes; } + + /** + * @brief Get the size of each leaf element in bytes. + * + * @return Size of each leaf element in bytes. + */ + uint64_t get_leaf_element_size() const { return m_leaf_element_size; } + + /** + * @brief Get the minimum layer index to store in the output. + * + * @return Minimum layer index to store in the output. + */ + uint64_t get_output_store_min_layer() const { return m_output_store_min_layer; } + + private: + std::vector m_layer_hashes; ///< Vector of hash functions for each layer. + uint64_t m_leaf_element_size; ///< Size of each leaf element in bytes. + uint64_t m_output_store_min_layer; ///< Minimum layer index to store in the output. + }; + + /*************************** Backend Factory Registration ***************************/ + + using MerkleTreeFactoryImpl = std::function& layer_hashes, + uint64_t leaf_element_size, + uint64_t output_store_min_layer, + std::shared_ptr& backend /*OUT*/)>; + + /** + * @brief Register a MerkleTree backend factory for a specific device type. + * + * @param deviceType String identifier for the device type. + * @param impl Factory function that creates the MerkleTreeBackend. + */ + void register_merkle_tree_factory(const std::string& deviceType, MerkleTreeFactoryImpl impl); + + /** + * @brief Macro to register a MerkleTree backend factory. + * + * This macro registers a factory function for a specific backend by calling + * `register_merkle_tree_factory` at runtime. + */ +#define REGISTER_MERKLE_TREE_FACTORY_BACKEND(DEVICE_TYPE, FUNC) \ + namespace { \ + static bool UNIQUE(_reg_merkle_tree) = []() -> bool { \ + register_merkle_tree_factory(DEVICE_TYPE, FUNC); \ + return true; \ + }(); \ + } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/device.h b/icicle/include/icicle/device.h index 3128d13c9..6423716e9 100644 --- a/icicle/include/icicle/device.h +++ b/icicle/include/icicle/device.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace icicle { diff --git a/icicle/include/icicle/dispatcher.h b/icicle/include/icicle/dispatcher.h index 0e28a8346..0c0a06dad 100644 --- a/icicle/include/icicle/dispatcher.h +++ b/icicle/include/icicle/dispatcher.h @@ -1,27 +1,16 @@ #pragma once #include "icicle/utils/log.h" +#include "icicle/utils/utils.h" #include "icicle/errors.h" #include "icicle/device.h" #include "icicle/device_api.h" #include #include -#include #include using namespace icicle; -// Template function to demangle the name of the type -template -std::string demangle() -{ - int status = -4; - std::unique_ptr res{ - abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status), std::free}; - - return (status == 0) ? res.get() : typeid(T).name(); -} - // Generalized executor-dispatcher template template class tIcicleExecuteDispatcher diff --git a/icicle/include/icicle/fields/complex_extension.h b/icicle/include/icicle/fields/complex_extension.h index 9b4d35d24..6495822bd 100644 --- a/icicle/include/icicle/fields/complex_extension.h +++ b/icicle/include/icicle/fields/complex_extension.h @@ -36,9 +36,15 @@ class ComplexExtensionField FF real; FF imaginary; - static constexpr HOST_DEVICE_INLINE ComplexExtensionField zero() { return ComplexExtensionField{FF::zero(), FF::zero()}; } + static constexpr HOST_DEVICE_INLINE ComplexExtensionField zero() + { + return ComplexExtensionField{FF::zero(), FF::zero()}; + } - static constexpr HOST_DEVICE_INLINE ComplexExtensionField one() { return ComplexExtensionField{FF::one(), FF::zero()}; } + static constexpr HOST_DEVICE_INLINE ComplexExtensionField one() + { + return ComplexExtensionField{FF::one(), FF::zero()}; + } static constexpr HOST_DEVICE_INLINE ComplexExtensionField to_montgomery(const ComplexExtensionField& xs) { @@ -50,7 +56,10 @@ class ComplexExtensionField return ComplexExtensionField{xs.real * FF{CONFIG::montgomery_r_inv}, xs.imaginary * FF{CONFIG::montgomery_r_inv}}; } - static HOST_INLINE ComplexExtensionField rand_host() { return ComplexExtensionField{FF::rand_host(), FF::rand_host()}; } + static HOST_INLINE ComplexExtensionField rand_host() + { + return ComplexExtensionField{FF::rand_host(), FF::rand_host()}; + } static void rand_host_many(ComplexExtensionField* out, int size) { @@ -61,7 +70,8 @@ class ComplexExtensionField template static constexpr HOST_DEVICE_INLINE ComplexExtensionField sub_modulus(const ComplexExtensionField& xs) { - return ComplexExtensionField{FF::sub_modulus(&xs.real), FF::sub_modulus(&xs.imaginary)}; + return ComplexExtensionField{ + FF::sub_modulus(&xs.real), FF::sub_modulus(&xs.imaginary)}; } friend std::ostream& operator<<(std::ostream& os, const ComplexExtensionField& xs) @@ -101,7 +111,8 @@ class ComplexExtensionField } template - static constexpr HOST_DEVICE_INLINE ExtensionWide mul_wide(const ComplexExtensionField& xs, const ComplexExtensionField& ys) + static constexpr HOST_DEVICE_INLINE ExtensionWide + mul_wide(const ComplexExtensionField& xs, const ComplexExtensionField& ys) { FWide real_prod = FF::mul_wide(xs.real, ys.real); FWide imaginary_prod = FF::mul_wide(xs.imaginary, ys.imaginary); @@ -142,7 +153,10 @@ class ComplexExtensionField return (xs.real == ys.real) && (xs.imaginary == ys.imaginary); } - friend HOST_DEVICE_INLINE bool operator!=(const ComplexExtensionField& xs, const ComplexExtensionField& ys) { return !(xs == ys); } + friend HOST_DEVICE_INLINE bool operator!=(const ComplexExtensionField& xs, const ComplexExtensionField& ys) + { + return !(xs == ys); + } template static HOST_DEVICE_INLINE ComplexExtensionField mul_const(const ComplexExtensionField& xs) diff --git a/icicle/include/icicle/fields/quartic_extension.h b/icicle/include/icicle/fields/quartic_extension.h index 923b31f3a..2ba17c05c 100644 --- a/icicle/include/icicle/fields/quartic_extension.h +++ b/icicle/include/icicle/fields/quartic_extension.h @@ -119,7 +119,8 @@ class QuarticExtensionField } template - static constexpr HOST_DEVICE_INLINE ExtensionWide mul_wide(const QuarticExtensionField& xs, const QuarticExtensionField& ys) + static constexpr HOST_DEVICE_INLINE ExtensionWide + mul_wide(const QuarticExtensionField& xs, const QuarticExtensionField& ys) { if (CONFIG::nonresidue_is_negative) return ExtensionWide{ @@ -179,7 +180,10 @@ class QuarticExtensionField return (xs.real == ys.real) && (xs.im1 == ys.im1) && (xs.im2 == ys.im2) && (xs.im3 == ys.im3); } - friend HOST_DEVICE_INLINE bool operator!=(const QuarticExtensionField& xs, const QuarticExtensionField& ys) { return !(xs == ys); } + friend HOST_DEVICE_INLINE bool operator!=(const QuarticExtensionField& xs, const QuarticExtensionField& ys) + { + return !(xs == ys); + } template static constexpr HOST_DEVICE_INLINE QuarticExtensionField mul_unsigned(const QuarticExtensionField& xs) diff --git a/icicle/include/icicle/hash/hash.h b/icicle/include/icicle/hash/hash.h new file mode 100644 index 000000000..5656d865c --- /dev/null +++ b/icicle/include/icicle/hash/hash.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include "icicle/hash/hash_config.h" +#include "icicle/backend/hash/hash_backend.h" + +namespace icicle { + + /** + * @brief Class representing a high-level hash interface. + * + * This class provides a unified interface for performing hash operations using a + * device-specific backend hash implementation (e.g., Keccak, Blake2, Poseidon). + * The actual hashing logic is delegated to the backend, allowing users to work with + * the same interface regardless of the backend. + */ + class Hash + { + public: + /** + * @brief Constructor for the Hash class. + * + * @param backend A shared pointer to a backend hash implementation. + */ + explicit Hash(std::shared_ptr backend) : m_backend(std::move(backend)) {} + + /** + * @brief Perform a hash operation. + * + * Delegates the hash operation to the backend. + * + * @param input Pointer to the input data as bytes. + * @param size Number of bytes to hash. If 0, the default chunk size is used. + * @param config Configuration options for the hash operation. + * @param output Pointer to the output data in bytes. + * @return An error code of type eIcicleError indicating success or failure. + */ + inline eIcicleError hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const + { + return m_backend->hash(input, size, config, output); + } + + /** + * @brief Perform a hash operation using typed data. + * + * Converts the input and output types to `std::byte` pointers and forwards the call to the backend. + * + * @tparam PREIMAGE The type of the input data. + * @tparam IMAGE The type of the output data. + * @param input Pointer to the input data. + * @param size The number of elements of type `PREIMAGE` to hash. + * @param config Configuration options for the hash operation. + * @param output Pointer to the output data. + * @return An error code of type eIcicleError indicating success or failure. + */ + template + inline eIcicleError hash(const PREIMAGE* input, uint64_t size, const HashConfig& config, IMAGE* output) const + { + return hash( + reinterpret_cast(input), size * sizeof(PREIMAGE), config, + reinterpret_cast(output)); + } + + /** + * @brief Get the default input chunk size. + * @return The size of the default input chunk in bytes. + */ + inline uint64_t input_default_chunk_size() const { return m_backend->input_default_chunk_size(); } + + /** + * @brief Get the output size in bytes. + * @return The size of the output in bytes. + */ + inline uint64_t output_size() const { return m_backend->output_size(); } + + private: + std::shared_ptr m_backend; ///< Shared pointer to the backend performing the hash operation. + }; + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/hash/hash_config.h b/icicle/include/icicle/hash/hash_config.h new file mode 100644 index 000000000..d434c66c2 --- /dev/null +++ b/icicle/include/icicle/hash/hash_config.h @@ -0,0 +1,35 @@ +#pragma once + +#include "icicle/runtime.h" +#include "icicle/config_extension.h" + +namespace icicle { + + /** + * @brief Configuration structure for hash operations. + * + * This structure holds various configuration options that control how hash operations are executed. + * It allows specifying the execution stream, input/output locations (device or host), batch sizes, + * and backend-specific extensions. Additionally, it supports both synchronous and asynchronous execution modes. + */ + struct HashConfig { + icicleStreamHandle stream = nullptr; /**< Stream for asynchronous execution. Default is nullptr. */ + uint64_t batch = 1; /**< Number of input chunks to hash in batch. Default is 1. */ + bool are_inputs_on_device = + false; /**< True if inputs reside on the device (e.g., GPU), false if on the host (CPU). Default is false. */ + bool are_outputs_on_device = + false; /**< True if outputs reside on the device, false if on the host. Default is false. */ + bool is_async = false; /**< True to run the hash asynchronously, false to run synchronously. Default is false. */ + ConfigExtension* ext = nullptr; /**< Pointer to backend-specific configuration extensions. Default is nullptr. */ + }; + + /** + * @brief Creates a default HashConfig object. + * + * This function provides a default configuration for hash operations. The default configuration + * runs the hash synchronously on the host, with a single chunk of input and no backend-specific extensions. + * @return A default-initialized HashConfig. + */ + static HashConfig default_hash_config() { return HashConfig(); } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/hash/keccak.h b/icicle/include/icicle/hash/keccak.h new file mode 100644 index 000000000..150eb3d93 --- /dev/null +++ b/icicle/include/icicle/hash/keccak.h @@ -0,0 +1,63 @@ +#pragma once + +#include "icicle/hash/hash.h" + +namespace icicle { + + /** + * @brief Creates a Keccak-256 hash object. + * + * This function constructs a Hash object configured for Keccak-256, with the + * appropriate backend selected based on the current device. + * + * @param input_chunk_size size of input in bytes for the Keccak-256 hash. + * @return Hash object encapsulating the Keccak-256 backend. + */ + Hash create_keccak_256_hash(uint64_t input_chunk_size = 0); + struct Keccak256 { + inline static Hash create(uint64_t input_chunk_size = 0) { return create_keccak_256_hash(); } + }; + + /** + * @brief Creates a Keccak-512 hash object. + * + * This function constructs a Hash object configured for Keccak-512, with the + * appropriate backend selected based on the current device. + * + * @param input_chunk_size size of input in bytes for the Keccak-512 hash. + * @return Hash object encapsulating the Keccak-512 backend. + */ + Hash create_keccak_512_hash(uint64_t input_chunk_size = 0); + struct Keccak512 { + inline static Hash create(uint64_t input_chunk_size = 0) { return create_keccak_512_hash(); } + }; + + /** + * @brief Creates a SHA3-256 hash object. + * + * This function constructs a Hash object configured for SHA3-256, which is a + * variant of Keccak-256, with the appropriate backend selected based on the current device. + * + * @param input_chunk_size size of input in bytes for the SHA3-256 hash. + * @return Hash object encapsulating the SHA3-256 backend. + */ + Hash create_sha3_256_hash(uint64_t input_chunk_size = 0); + struct Sha3_256 { + inline static Hash create(uint64_t input_chunk_size = 0) { return create_sha3_256_hash(); } + }; + + /** + * @brief Creates a SHA3-512 hash object. + * + * This function constructs a Hash object configured for SHA3-512, with the + * appropriate backend selected based on the current device. + * + * @param input_chunk_size size of input in bytes for the SHA3-512 hash. + * @return Hash object encapsulating the SHA3-512 backend. + */ + Hash create_sha3_512_hash(uint64_t input_chunk_size = 0); + struct Sha3_512 { + inline static Hash create(uint64_t input_chunk_size = 0) { return create_sha3_512_hash(); } + }; + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/hash/poseidon.h b/icicle/include/icicle/hash/poseidon.h new file mode 100644 index 000000000..7330dc678 --- /dev/null +++ b/icicle/include/icicle/hash/poseidon.h @@ -0,0 +1,88 @@ +#pragma once + +#include "icicle/hash/hash.h" + +namespace icicle { + + /** + * @brief Class representing the constants for Poseidon hash. + * + * This class will store the necessary constants for Poseidon hashing operations. + * It will be managed and allocated by the backend and used when initializing the Poseidon hash. + */ + template + class PoseidonConstants + { + // The backend will populate this class with Poseidon-specific constants. + // This is intentionally left opaque and managed by the backend. + }; + + /** + * @brief Initialize Poseidon constants with full configuration parameters. + * + * This function initializes a PoseidonConstants object with the provided configuration parameters. + * The user provides arity, alpha, round constants, matrices, and other Poseidon-specific parameters. + * The backend allocates and returns the initialized constants as a shared object. + * + * @param arity The arity (branching factor) of the Poseidon hash. + * @param alpha Exponent used in the S-box function. + * @param nof_partial_rounds Number of partial rounds. + * @param nof_upper_full_rounds Number of full rounds at the beginning. + * @param nof_end_full_rounds Number of full rounds at the end. + * @param rounds_constants Array of round constants for Poseidon. + * @param mds_matrix Array representing the MDS matrix. + * @param pre_matrix Array representing the pre-processing matrix. + * @param sparse_matrix Array representing the sparse matrix. + * @param constants [OUT] Shared pointer to the initialized PoseidonConstants object. + * + * @return eIcicleError Error code indicating success or failure of the initialization. + */ + template + eIcicleError poseidon_init_constants( + unsigned arity, + unsigned alpha, + unsigned nof_partial_rounds, + unsigned nof_upper_full_rounds, + unsigned nof_end_full_rounds, + const S* rounds_constants, + const S* mds_matrix, + const S* pre_matrix, + const S* sparse_matrix, + std::shared_ptr>& constants /*out*/); + + /** + * @brief Initialize Poseidon constants with default values based on arity. + * + * This function initializes a PoseidonConstants object with default values, based only on the arity. + * The backend will populate the constants object with pre-determined default values for Poseidon parameters. + * + * @param arity The arity (branching factor) of the Poseidon hash. + * @param constants [OUT] Shared pointer to the initialized PoseidonConstants object. + * + * @return eIcicleError Error code indicating success or failure of the initialization. + */ + template + eIcicleError + poseidon_init_default_constants(unsigned arity, std::shared_ptr>& constants /*out*/); + + /** + * @brief Create a Poseidon hash object using the shared PoseidonConstants. + * + * This function creates a Poseidon hash object, using the shared constants that were initialized by + * the backend. The constants will be shared between the user and the PoseidonHasher. + * + * @param constants Shared pointer to the PoseidonConstants object, which holds hash-specific data. + * + * @return Hash Poseidon Hash object ready to perform hashing operations. + */ + template + Hash create_poseidon_hash(std::shared_ptr> constants); + struct Poseidon { + template + inline static Hash create(std::shared_ptr> constants) + { + return create_poseidon_hash(constants); + } + }; + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/merkle/merkle_proof.h b/icicle/include/icicle/merkle/merkle_proof.h new file mode 100644 index 000000000..c6113d4c3 --- /dev/null +++ b/icicle/include/icicle/merkle/merkle_proof.h @@ -0,0 +1,174 @@ +#pragma once + +#include +#include +#include +#include // For streams +#include +#include "icicle/runtime.h" + +namespace icicle { + + /** + * @brief Class representing the Merkle path in a move-only manner. + * + * This class manages a Merkle path as a collection of bytes. It is designed to be move-only, + * meaning it can be transferred but not copied, ensuring clear ownership of the path data. + * The path is stored using a `std::vector` for easy management and flexibility. + */ + class MerkleProof + { + public: + /** + * @brief Simple constructor for MerkleProof. + */ + explicit MerkleProof() = default; + + /** + * @brief Allocate and copy leaf and root data using raw byte pointers. + * @param pruned_path Whether the Merkle path is pruned. + * @param leaf_idx The index of the leaf for which the path is a proof. + * @param leaf Pointer to the leaf data as std::byte*. Can be host/device memory. + * @param leaf_size The size of the leaf data. + * @param root Pointer to the root data as std::byte*. Can be host/device memory. + * @param root_size The size of the root data. + */ + void allocate_from_bytes( + bool pruned_path, + uint64_t leaf_idx, + const std::byte* leaf, + std::size_t leaf_size, + const std::byte* root, + std::size_t root_size) + { + m_pruned = pruned_path; + m_leaf_index = leaf_idx; + + if (root != nullptr && root_size > 0) { + m_root.resize(root_size); + ICICLE_CHECK(icicle_copy(m_root.data(), root, root_size)); + } + + if (leaf != nullptr && leaf_size > 0) { + m_leaf.resize(leaf_size); + ICICLE_CHECK(icicle_copy(m_leaf.data(), leaf, leaf_size)); + } + } + + /** + * @brief Allocate and copy leaf and root data using templated types. + * @tparam LEAF The type of the leaf data. + * @tparam DIGEST The type of the root data. + * @param pruned_path Whether the Merkle path is pruned. + * @param leaf_idx The index of the leaf for which the path is a proof. + * @param leaf The leaf data. + * @param root The root data. + */ + template + void allocate(bool pruned_path, uint64_t leaf_idx, const LEAF& leaf, const DIGEST& root) + { + m_pruned = pruned_path; + m_leaf_index = leaf_idx; + + // Allocate and copy root data + m_root.resize(sizeof(DIGEST)); + ICICLE_CHECK(icicle_copy(m_root.data(), &root, sizeof(DIGEST))); + + // Allocate and copy leaf data + m_leaf.resize(sizeof(LEAF)); + ICICLE_CHECK(icicle_copy(m_leaf.data(), &leaf, sizeof(LEAF))); + } + + /** + * @brief Check if the Merkle path is pruned. + * @return True if the path is pruned, false otherwise. + */ + bool is_pruned() const { return m_pruned; } + + /** + * @brief Get a pointer to the path data. + * @return Pointer to the path data. + */ + const std::byte* get_path() const { return m_path.data(); } + + /** + * @brief Get the size of the path data. + * @return The size of the path data. + */ + uint64_t get_path_size() const { return m_path.size(); } + + /** + * @brief Push a node to the path, given as bytes, using icicle_copy. + * @param node The pointer to the node data. + * @param size The size of the node data in bytes. + */ + void push_node_to_path(const std::byte* node, uint64_t size) + { + std::size_t old_size = m_path.size(); + m_path.resize(old_size + size); + ICICLE_CHECK(icicle_copy(m_path.data() + old_size, node, size)); + } + + /** + * @brief Push a node to the path, given as a typed object. + * @tparam T The type of the node. + * @param node The pointer to the node. + */ + template + void push_node_to_path(const T& node) + { + push_node_to_path(reinterpret_cast(&node), sizeof(T)); + } + + /** + * @brief Access data at a specific offset and cast it to the desired type. + * @tparam T The type to cast the data to. + * @param offset The byte offset to access. + * @return Pointer to the data cast to type T. + */ + template + const T* access_path_at_offset(uint64_t offset) + { + if (offset >= m_path.size()) { throw std::out_of_range("Offset out of bounds"); } + return reinterpret_cast(m_path.data() + offset); + } + + /** + * @brief Get the index of the leaf this path is a proof for. + * @return Index of the proved leaf. + */ + uint64_t get_leaf_idx() const { return m_leaf_index; } + + /** + * @brief Get a pointer to the leaf data. + * @return Pointer to the leaf data, or nullptr if no leaf data is available. + */ + const std::byte* get_leaf() const { return m_leaf.empty() ? nullptr : m_leaf.data(); } + + /** + * @brief Get the size of the leaf data. + * @return The size of the leaf data, or 0 if no leaf data is available. + */ + uint64_t get_leaf_size() const { return m_leaf.size(); } + + /** + * @brief Get a pointer to the root data. + * @return Pointer to the root data, or nullptr if no root data is available. + */ + const std::byte* get_root() const { return m_root.empty() ? nullptr : m_root.data(); } + + /** + * @brief Get the size of the root data. + * @return The size of the root data, or 0 if no root data is available. + */ + uint64_t get_root_size() const { return m_root.size(); } + + private: + bool m_pruned{false}; ///< Whether the Merkle path is pruned. + uint64_t m_leaf_index{0}; ///< Index of the leaf this path is a proof for. + std::vector m_leaf; ///< Optional leaf data. + std::vector m_root; ///< Optional root data. + std::vector m_path; ///< Path data. + }; + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/merkle/merkle_tree.h b/icicle/include/icicle/merkle/merkle_tree.h new file mode 100644 index 000000000..26e1f5f01 --- /dev/null +++ b/icicle/include/icicle/merkle/merkle_tree.h @@ -0,0 +1,143 @@ +#pragma once + +#include +#include +#include +#include "icicle/merkle/merkle_tree_config.h" +#include "icicle/merkle/merkle_proof.h" +#include "icicle/backend/merkle/merkle_tree_backend.h" + +namespace icicle { + + class MerkleTree; + + /** + * @brief Create a MerkleTree with specified layer hashes and configurations. + * + * @param layer_hashes A vector of Hash objects representing the hashes of each tree layer. + * @param leaf_element_size Size of each leaf element. + * @param output_store_min_layer Minimum layer at which output is stored (default is 0). + * @return A MerkleTree object initialized with the specified backend. + */ + MerkleTree create_merkle_tree( + const std::vector& layer_hashes, uint64_t leaf_element_size, uint64_t output_store_min_layer = 0); + + /** + * @brief Class for performing Merkle tree operations. + * + * This class provides a high-level interface for building and managing Merkle trees. The underlying + * logic for tree operations, such as building, retrieving paths, and verifying, is delegated to the + * backend, which may be device-specific (e.g., CPU, GPU). + */ + class MerkleTree + { + public: + /** + * @brief Static factory method to create a MerkleTree instance. + * + * @param layer_hashes A vector of Hash objects representing the hashes of each tree layer. + * @param leaf_element_size Size of each leaf element. + * @param output_store_min_layer Minimum layer at which output is stored (default is 0). + * @return A MerkleTree object initialized with the specified backend. + */ + static MerkleTree + create(const std::vector& layer_hashes, uint64_t leaf_element_size, uint64_t output_store_min_layer = 0) + { + return create_merkle_tree(layer_hashes, leaf_element_size, output_store_min_layer); + } + + /** + * @brief Constructor for the MerkleTree class. + * @param backend Shared pointer to the backend responsible for Merkle tree operations. + */ + explicit MerkleTree(std::shared_ptr backend) : m_backend{std::move(backend)} {} + + /** + * @brief Build the Merkle tree from the provided leaves. + * @param leaves Pointer to the leaves of the tree (input data). + * @param size The size of the leaves. + * @param config Configuration for the Merkle tree operation. + * @return Error code of type eIcicleError indicating success or failure. + */ + inline eIcicleError build(const std::byte* leaves, uint64_t size, const MerkleTreeConfig& config) + { + return m_backend->build(leaves, size, config); + } + + template + inline eIcicleError + build(const T* leaves, uint64_t size /* number of leaf elements */, const MerkleTreeConfig& config) + { + return build(reinterpret_cast(leaves), size * sizeof(T), config); + } + + /** + * @brief Retrieve the root of the Merkle tree. + * @param root Pointer to where the Merkle root will be written. Must be on host memory. + * @return Error code of type eIcicleError. + */ + inline eIcicleError get_merkle_root(std::byte* root /*output*/, uint64_t root_size) const + { + return m_backend->get_merkle_root(root, root_size); + } + + template + inline eIcicleError get_merkle_root(T& root /*output*/) const + { + return get_merkle_root(reinterpret_cast(&root), sizeof(T)); + } + + /** + * @brief Retrieve the Merkle path for a specific element. + * @param leaves Pointer to the leaves of the tree. + * @param leaf_idx Index of the element for which the Merkle path is required. + * @param config Configuration for the Merkle tree operation. + * @param merkle_proof Reference to the MerkleProof object where the path will be stored. + * @return Error code of type eIcicleError. + */ + inline eIcicleError get_merkle_proof( + const std::byte* leaves, + uint64_t leaf_idx, + const MerkleTreeConfig& config, + MerkleProof& merkle_proof /*output*/) const + { + return m_backend->get_merkle_proof(leaves, leaf_idx, config, merkle_proof); + } + + template + inline eIcicleError get_merkle_proof( + const T* leaves, uint64_t leaf_idx, const MerkleTreeConfig& config, MerkleProof& merkle_proof /*output*/) const + { + return get_merkle_proof(reinterpret_cast(leaves), leaf_idx, config, merkle_proof); + } + + /** + * @brief Verify an element against the Merkle path using layer hashes. + * @param merkle_proof The MerkleProof object includes the leaf, path, and the root. + * @param valid output valid bit. True if the Proof is valid, false otherwise. + * @return Error code of type eIcicleError indicating success or failure. + */ + eIcicleError verify(const MerkleProof& merkle_proof, bool& valid /*output*/) const + { + // TODO: Implement Merkle path verification here + + // can access path by offset or all of it + // auto digest = merkle_proof.access_path_at_offset(offset); + auto path = merkle_proof.get_path(); + auto path_size = merkle_proof.get_path_size(); + auto root = merkle_proof.get_root(); + auto root_size = merkle_proof.get_root_size(); + auto leaf_idx = merkle_proof.get_leaf_idx(); + auto leaf = merkle_proof.get_leaf(); + auto leaf_size = merkle_proof.get_leaf_size(); + + valid = true; // TODO use hashers to check path from leaf to root is recomputing the expected root + return eIcicleError::SUCCESS; + } + + private: + std::shared_ptr + m_backend; ///< Shared pointer to the backend responsible for Merkle tree operations. + }; + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/merkle/merkle_tree_config.h b/icicle/include/icicle/merkle/merkle_tree_config.h new file mode 100644 index 000000000..de2005584 --- /dev/null +++ b/icicle/include/icicle/merkle/merkle_tree_config.h @@ -0,0 +1,49 @@ +#pragma once + +#include "icicle/runtime.h" +#include "icicle/config_extension.h" + +namespace icicle { + + /** + * @brief Enum representing the padding policy when the input is smaller than expected by the tree structure. + */ + enum class PaddingPolicy { + None, /**< No padding, assume input is correctly sized. */ + ZeroPadding, /**< Pad the input with zeroes to fit the expected input size. */ + LastValue /**< Pad the input by repeating the last value. */ + }; + + /** + * @brief Configuration structure for Merkle tree operations. + * + * This structure holds the configuration options for Merkle tree operations, including tree construction, + * path computation, and verification. It allows specifying whether the data (leaves, tree, and paths) + * reside on the device (e.g., GPU) or the host (e.g., CPU), and supports both synchronous and asynchronous + * execution modes, as well as backend-specific extensions. It also provides a padding policy for handling + * cases where the input size is smaller than expected by the tree structure. + */ + struct MerkleTreeConfig { + icicleStreamHandle stream = + nullptr; /**< Stream for asynchronous execution. Default is nullptr for synchronous execution. */ + bool is_leaves_on_device = + false; /**< True if leaves are on the device (GPU), false if on the host (CPU). Default is false. */ + bool is_tree_on_device = false; /**< True if the tree results are allocated on the device (GPU), false if on the + host (CPU). Default is false. */ + bool is_async = false; /**< True for asynchronous execution, false for synchronous. Default is false. */ + PaddingPolicy padding_policy = + PaddingPolicy::None; /**< Policy for handling cases where the input is smaller than expected. */ + ConfigExtension* ext = nullptr; /**< Backend-specific extensions for advanced configurations. Default is nullptr. */ + }; + + /** + * @brief Generates a default configuration for Merkle tree operations. + * + * This function provides a default configuration for Merkle tree operations with synchronous execution + * and all data (leaves, tree results, and paths) residing on the host (CPU). + * + * @return A default MerkleTreeConfig with host-based execution and no backend-specific extensions. + */ + static MerkleTreeConfig default_merkle_tree_config() { return MerkleTreeConfig(); } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/include/icicle/utils/modifiers.h b/icicle/include/icicle/utils/modifiers.h index 74520c9f9..ac62028a8 100644 --- a/icicle/include/icicle/utils/modifiers.h +++ b/icicle/include/icicle/utils/modifiers.h @@ -14,7 +14,7 @@ #define HOST_INLINE __host__ INLINE_MACRO #define DEVICE_INLINE __device__ INLINE_MACRO -#define HOST_DEVICE __host__ __device__ +#define HOST_DEVICE __host__ __device__ #define HOST_DEVICE_INLINE HOST_DEVICE INLINE_MACRO #else // not CUDA #define INLINE_MACRO diff --git a/icicle/include/icicle/utils/utils.h b/icicle/include/icicle/utils/utils.h index 7efcbc2ac..d30b8e85f 100644 --- a/icicle/include/icicle/utils/utils.h +++ b/icicle/include/icicle/utils/utils.h @@ -1,9 +1,19 @@ #pragma once -#ifndef ICICLE_UTILS_H -#define ICICLE_UTILS_H +#include +#include +#include #define CONCAT_DIRECT(a, b) a##_##b #define CONCAT_EXPAND(a, b) CONCAT_DIRECT(a, b) // expand a,b before concatenation #define UNIQUE(a) CONCAT_EXPAND(a, __LINE__) -#endif // ICICLE_UTILS_H \ No newline at end of file +// Template function to demangle the name of the type +template +std::string demangle() +{ + int status = -4; + std::unique_ptr res{ + abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status), std::free}; + + return (status == 0) ? res.get() : typeid(T).name(); +} diff --git a/icicle/src/hash/keccak.cpp b/icicle/src/hash/keccak.cpp new file mode 100644 index 000000000..dd7f415da --- /dev/null +++ b/icicle/src/hash/keccak.cpp @@ -0,0 +1,72 @@ +#include "icicle/errors.h" +#include "icicle/backend/hash/keccak_backend.h" +#include "icicle/dispatcher.h" + +namespace icicle { + + // Keccak 256 + ICICLE_DISPATCHER_INST(Keccak256Dispatcher, keccak_256_factory, KeccakFactoryImpl); + + Hash create_keccak_256_hash(uint64_t input_chunk_size) + { + std::shared_ptr backend; + ICICLE_CHECK(Keccak256Dispatcher::execute(input_chunk_size, backend)); + Hash keccak{backend}; + return keccak; + } + + // Keccak 512 + ICICLE_DISPATCHER_INST(Keccak512Dispatcher, keccak_512_factory, KeccakFactoryImpl); + + Hash create_keccak_512_hash(uint64_t input_chunk_size) + { + std::shared_ptr backend; + ICICLE_CHECK(Keccak512Dispatcher::execute(input_chunk_size, backend)); + Hash keccak{backend}; + return keccak; + } + + // Sha3 256 + ICICLE_DISPATCHER_INST(Sah3_256Dispatcher, sha3_256_factory, KeccakFactoryImpl); + + Hash create_sha3_256_hash(uint64_t input_chunk_size) + { + std::shared_ptr backend; + ICICLE_CHECK(Keccak256Dispatcher::execute(input_chunk_size, backend)); + Hash keccak{backend}; + return keccak; + } + + // Keccak 512 + ICICLE_DISPATCHER_INST(Sah3_512Dispatcher, sha3_512_factory, KeccakFactoryImpl); + + Hash create_sha3_512_hash(uint64_t input_chunk_size) + { + std::shared_ptr backend; + ICICLE_CHECK(Keccak512Dispatcher::execute(input_chunk_size, backend)); + Hash keccak{backend}; + return keccak; + } + + /*************************** C API ***************************/ + + extern "C" Hash* create_keccak_256_hash_c_api(uint64_t input_chunk_size) + { + return new Hash(create_keccak_256_hash(input_chunk_size)); + } + extern "C" Hash* create_keccak_512_hash_c_api(uint64_t input_chunk_size) + { + return new Hash(create_keccak_512_hash(input_chunk_size)); + } + extern "C" Hash* create_sha3_256_hash_c_api(uint64_t input_chunk_size) + { + return new Hash(create_sha3_256_hash(input_chunk_size)); + } + extern "C" Hash* create_sha3_512_hash_c_api(uint64_t input_chunk_size) + { + return new Hash(create_sha3_512_hash(input_chunk_size)); + } + + // TODO Yuval : need to expose one deleter from C++. This will be used to drop any object + +} // namespace icicle \ No newline at end of file diff --git a/icicle/src/hash/merkle_tree.cpp b/icicle/src/hash/merkle_tree.cpp new file mode 100644 index 000000000..b5695df3d --- /dev/null +++ b/icicle/src/hash/merkle_tree.cpp @@ -0,0 +1,27 @@ +#include "icicle/errors.h" +#include "icicle/merkle/merkle_tree.h" +#include "icicle/backend/merkle/merkle_tree_backend.h" +#include "icicle/dispatcher.h" + +namespace icicle { + + ICICLE_DISPATCHER_INST(MerkleTreeDispatcher, merkle_tree_factory, MerkleTreeFactoryImpl); + + MerkleTree + create_merkle_tree(const std::vector& layer_hashes, uint64_t leaf_element_size, uint64_t output_store_min_layer) + { + std::shared_ptr backend; + ICICLE_CHECK(MerkleTreeDispatcher::execute(layer_hashes, leaf_element_size, output_store_min_layer, backend)); + MerkleTree merkle_tree{backend}; + return merkle_tree; + } + + /*************************** C API ***************************/ + + MerkleTree* create_merkle_tree_c_api( + const std::vector& layer_hashes, uint64_t leaf_element_size, uint64_t output_store_min_layer) + { + return new MerkleTree(create_merkle_tree(layer_hashes, leaf_element_size, output_store_min_layer)); + } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/src/hash/poseidon.cpp b/icicle/src/hash/poseidon.cpp new file mode 100644 index 000000000..4d5084163 --- /dev/null +++ b/icicle/src/hash/poseidon.cpp @@ -0,0 +1,48 @@ +#include "icicle/errors.h" +#include "icicle/backend/hash/poseidon_backend.h" +#include "icicle/dispatcher.h" + +namespace icicle { + + ICICLE_DISPATCHER_INST(InitPoseidonConstantsDispatcher, poseidon_init_constants, InitPoseidonConstantsImpl); + + template <> + eIcicleError poseidon_init_constants( + unsigned arity, + unsigned alpha, + unsigned nof_partial_rounds, + unsigned nof_upper_full_rounds, + unsigned nof_end_full_rounds, + const scalar_t* rounds_constants, + const scalar_t* mds_matrix, + const scalar_t* pre_matrix, + const scalar_t* sparse_matrix, + std::shared_ptr>& constants /*out*/) + { + return InitPoseidonConstantsDispatcher::execute( + arity, alpha, nof_partial_rounds, nof_upper_full_rounds, nof_end_full_rounds, rounds_constants, mds_matrix, + pre_matrix, sparse_matrix, constants); + } + + ICICLE_DISPATCHER_INST( + InitPoseidonDefaultConstantsDispatcher, poseidon_init_default_constants, InitPoseidonDefaultConstantsImpl); + + template <> + eIcicleError + poseidon_init_default_constants(unsigned arity, std::shared_ptr>& constants /*out*/) + { + return InitPoseidonDefaultConstantsDispatcher::execute(arity, constants); + } + + ICICLE_DISPATCHER_INST(CreatePoseidonHasherDispatcher, create_poseidon, CreatePoseidonImpl); + + template <> + Hash create_poseidon_hash(std::shared_ptr> constants) + { + std::shared_ptr backend; + ICICLE_CHECK(CreatePoseidonHasherDispatcher::execute(constants, backend)); + Hash poseidon{backend}; + return poseidon; + } + +} // namespace icicle \ No newline at end of file diff --git a/icicle/tests/CMakeLists.txt b/icicle/tests/CMakeLists.txt index ddae9a7cb..13b8df4a0 100644 --- a/icicle/tests/CMakeLists.txt +++ b/icicle/tests/CMakeLists.txt @@ -55,4 +55,13 @@ if (CURVE) endif() endif() - +#hash API test +if (HASH) + add_executable(test_hash_api test_hash_api.cpp) + target_include_directories(test_hash_api PRIVATE ${CMAKE_SOURCE_DIR}/include/) + target_link_libraries(test_hash_api PRIVATE GTest::gtest_main icicle_device icicle_hash) + gtest_discover_tests(test_hash_api) + if (POSEIDON) + target_link_libraries(test_hash_api PRIVATE icicle_field) + endif() +endif() diff --git a/icicle/tests/test_hash_api.cpp b/icicle/tests/test_hash_api.cpp new file mode 100644 index 000000000..73a796b78 --- /dev/null +++ b/icicle/tests/test_hash_api.cpp @@ -0,0 +1,196 @@ +#include +#include +#include + +#include "icicle/runtime.h" +#include "icicle/utils/log.h" +#include "icicle/hash/hash.h" +#include "icicle/hash/keccak.h" +#include "icicle/merkle/merkle_tree.h" + +using namespace icicle; + +using FpMicroseconds = std::chrono::duration; +#define START_TIMER(timer) auto timer##_start = std::chrono::high_resolution_clock::now(); +#define END_TIMER(timer, msg, enable) \ + if (enable) \ + printf( \ + "%s: %.3f ms\n", msg, FpMicroseconds(std::chrono::high_resolution_clock::now() - timer##_start).count() / 1000); + +static bool VERBOSE = true; +static inline std::string s_main_target; +static inline std::string s_reference_target; + +class HashApiTest : public ::testing::Test +{ +public: + // SetUpTestSuite/TearDownTestSuite are called once for the entire test suite + static void SetUpTestSuite() + { +#ifdef BACKEND_BUILD_DIR + setenv("ICICLE_BACKEND_INSTALL_DIR", BACKEND_BUILD_DIR, 0 /*=replace*/); +#endif + icicle_load_backend_from_env_or_default(); + + const bool is_cuda_registered = is_device_registered("CUDA"); + if (!is_cuda_registered) { ICICLE_LOG_ERROR << "CUDA device not found. Testing CPU vs CPU"; } + s_main_target = is_cuda_registered ? "CUDA" : "CPU"; + s_reference_target = "CPU"; + } + static void TearDownTestSuite() + { + // make sure to fail in CI if only have one device + // ICICLE_ASSERT(is_device_registered("CUDA")) << "missing CUDA backend"; + } + + // SetUp/TearDown are called before and after each test + void SetUp() override {} + void TearDown() override {} + + template + void randomize(T* arr, uint64_t size) + { + // Create a random number generator + std::random_device rd; // Non-deterministic random number generator + std::mt19937 gen(rd()); // Mersenne Twister engine seeded with rd() + std::uniform_int_distribution dist(0, UINT32_MAX); // Range of random numbers + + // Fill the array with random values + uint32_t* u32_arr = (uint32_t*)arr; + for (int i = 0; i < (size * sizeof(T) / sizeof(uint32_t)); ++i) { + u32_arr[i] = dist(gen); + } + } +}; + +TEST_F(HashApiTest, Keccak256) +{ + const uint64_t input_size = 64; // Number of input elements + const uint64_t output_size = 256; + + // Create unique pointers for input and output arrays + auto input = std::make_unique(input_size); + auto output = std::make_unique(output_size); + + // Randomize the input array + randomize(input.get(), input_size); + + auto config = default_hash_config(); + // Create Keccak-256 hash object + auto keccak256 = Keccak256::create(); + // Run single hash operation + ICICLE_CHECK(keccak256.hash(input.get(), input_size, config, output.get())); + + // Batch hash (hashing each half separately) + auto output_batch = std::make_unique(output_size * 2); + config.batch = 2; + ICICLE_CHECK(keccak256.hash(input.get(), input_size / 2, config, output_batch.get())); + // TODO: Verify output (e.g., check CPU against CUDA) +} + +// TODO: add tests for all hashes + +/****************************** Merkle **********************************/ +class HashSumBackend : public HashBackend +{ +public: + HashSumBackend(uint64_t input_chunk_size, uint64_t output_size) : HashBackend(output_size, input_chunk_size) {} + + eIcicleError hash(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const override + { + const auto chunk_size = get_single_chunk_size(size); + const auto otput_digest_size = output_size(); + for (int i = 0; i < config.batch; ++i) { + hash_single(input, size, config, output); + input += chunk_size; + output += otput_digest_size; + } + return eIcicleError::SUCCESS; + } + + void hash_single(const std::byte* input, uint64_t size, const HashConfig& config, std::byte* output) const + { + const uint32_t* input_u32 = (const uint32_t*)input; + uint32_t* output_u32 = (uint32_t*)output; + + output_u32[0] = 0; + for (int i = 0; i < (size >> 2); ++i) { + output_u32[0] += input_u32[i]; + } + for (int i = 1; i < (output_size() >> 2); ++i) { + output_u32[i] += output_u32[0]; + } + } + + static Hash create(uint64_t input_chunk_size, uint64_t output_size) + { + auto backend = std::make_shared(input_chunk_size, output_size); + return Hash(backend); + } +}; + +TEST_F(HashApiTest, MerkleTree) +{ + // define input + constexpr int nof_leaves = 100; + uint32_t leaves[nof_leaves]; + const size_t input_size = sizeof(leaves); + for (int i = 0; i < nof_leaves; ++i) { + leaves[i] = i; + } + + // define the merkle tree + auto config = default_merkle_tree_config(); + auto layer0_hash = HashSumBackend::create(20, 8); // input 20 bytes, output 8 bytes input 400B -> 160B + auto layer1_hash = HashSumBackend::create(16, 2); // input 16 bytes, output 2 bytes 160B -> 20B + auto layer2_hash = HashSumBackend::create(20, 8); // input 20 bytes, output 8 bytes 20B -> 8B output + auto leaf_element_size = 4; + auto merkle_tree = + MerkleTree::create({layer0_hash, layer1_hash, layer2_hash}, leaf_element_size, 2 /*min level to store*/); + + // build tree + ICICLE_CHECK(merkle_tree.build(leaves, input_size, config)); + + // ret root and merkle-path to an element + uint64_t root = 0; // assuming output is 8B + uint64_t leaf_idx = 5; + MerkleProof merkle_proof{}; + ICICLE_CHECK(merkle_tree.get_merkle_root(root)); + ICICLE_CHECK(merkle_tree.get_merkle_proof(leaves, leaf_idx, config, merkle_proof)); + + bool verification_valid = false; + ICICLE_CHECK(merkle_tree.verify(merkle_proof, verification_valid)); + ASSERT_TRUE(verification_valid); +} + +#ifdef POSEIDON + +#include "icicle/fields/field_config.h" +using namespace field_config; + +#include "icicle/hash/poseidon.h" + +TEST_F(HashApiTest, poseidon12) +{ + const uint64_t input_size = 12; // Number of input elements + const uint64_t output_size = sizeof(scalar_t); + + // Create unique pointers for input and output arrays + auto input = std::make_unique(input_size); + scalar_t output = scalar_t::from(0); + + // Randomize the input array + scalar_t::rand_host_many(input.get(), input_size); + + // init poseidon scalars + std::shared_ptr> poseidon_constants; + ICICLE_CHECK(poseidon_init_default_constants(12, poseidon_constants)); + + auto config = default_hash_config(); + // Create Poseidon hash object + auto poseidon = Poseidon::create(poseidon_constants); + // Run single hash operation + ICICLE_CHECK(poseidon.hash(input.get(), input_size, config, &output)); + // TODO: Verify output (e.g., check CPU against CUDA) +} +#endif // POSEIDON \ No newline at end of file