diff --git a/.gitmodules b/.gitmodules index db7cb32d5b..9076ca5fc9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,6 @@ [submodule "libs/threaded-math"] path = libs/parallel-math url = git@github.com:NilFoundation/actor-math -[submodule "libs/threaded-containers"] - path = libs/parallel-containers - url = git@github.com:NilFoundation/actor-containers [submodule "cmake/modules"] path = cmake/modules url = git@github.com:BoostCMake/cmake_modules.git diff --git a/libs/parallel-containers b/libs/parallel-containers deleted file mode 160000 index 39e2c812d3..0000000000 --- a/libs/parallel-containers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 39e2c812d375e8bcb662edd6661a8c26c50eb723 diff --git a/libs/parallel-containers/CMakeLists.txt b/libs/parallel-containers/CMakeLists.txt new file mode 100644 index 0000000000..a845938647 --- /dev/null +++ b/libs/parallel-containers/CMakeLists.txt @@ -0,0 +1,98 @@ +#---------------------------------------------------------------------------// +# MIT License +# +# Copyright (c) 2020 Mikhail Komarov +# Copyright (c) 2021 Aleksei Moskvin +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------// + +cmake_minimum_required(VERSION 2.8.12) + +cmake_policy(SET CMP0042 NEW) +cmake_policy(SET CMP0028 NEW) +cmake_policy(SET CMP0048 NEW) +cmake_policy(SET CMP0057 NEW) +cmake_policy(SET CMP0079 OLD) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake" + "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/share/modules/cmake") + +include(CMConfig) +include(CMSetupVersion) + +cm_workspace(actor) + +include(CMDeploy) +include(CMSetupVersion) + +if (NOT Boost_FOUND AND NOT CMAKE_CROSSCOMPILING) + find_package(Boost COMPONENTS REQUIRED filesystem) +endif () + +cm_project(containers WORKSPACE_NAME ${CMAKE_WORKSPACE_NAME}) + +option(BUILD_DOXYGEN_DOCS "Build with configuring Doxygen documentation compiler" TRUE) + +set(DOXYGEN_OUTPUT_DIR "${CMAKE_CURRENT_LIST_DIR}/docs" CACHE STRING "Specify doxygen output directory") + +list(APPEND ${CURRENT_PROJECT_NAME}_PUBLIC_HEADERS + ) + +list(APPEND ${CURRENT_PROJECT_NAME}_UNGROUPED_SOURCES) + +list(APPEND ${CURRENT_PROJECT_NAME}_HEADERS + ${${CURRENT_PROJECT_NAME}_PUBLIC_HEADERS} + ${${CURRENT_PROJECT_NAME}_UNGROUPED_SOURCES}) + +list(APPEND ${CURRENT_PROJECT_NAME}_SOURCES + ${${CURRENT_PROJECT_NAME}_UNGROUPED_SOURCES}) + +cm_setup_version(VERSION 0.1.0 PREFIX ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}) + +add_library(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INTERFACE) + +set_target_properties(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} PROPERTIES + EXPORT_NAME ${CURRENT_PROJECT_NAME}) + +target_link_libraries(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INTERFACE + + ${CMAKE_WORKSPACE_NAME}::core + + crypto3::algebra + crypto3::hash + + ${Boost_LIBRARIES}) + +target_include_directories(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INTERFACE + "$" + "$" + + ${Boost_INCLUDE_DIRS}) + +cm_deploy(TARGETS ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} INCLUDE include NAMESPACE ${CMAKE_WORKSPACE_NAME}::) + +if (BUILD_TESTS) + add_subdirectory(test) +endif () + +if (BUILD_EXAMPLES) + add_subdirectory(example) +endif () + diff --git a/libs/parallel-containers/README.md b/libs/parallel-containers/README.md new file mode 100644 index 0000000000..ddccd889e1 --- /dev/null +++ b/libs/parallel-containers/README.md @@ -0,0 +1,21 @@ +# Containers for =nil; Foundation's Cryptography Suite + +Containers using =nil; Foundation's cryptography suite. + +## Building + +This library uses Boost CMake build modules (https://github.com/BoostCMake/cmake_modules.git). +To actually include this library in a project it is required to: + +1. Add [CMake Modules](https://github.com/BoostCMake/cmake_modules.git) as submodule to target project repository. +2. Add all the internal dependencies using [CMake Modules](https://github.com/BoostCMake/cmake_modules.git) as submodules to target project repository. +3. Initialize parent project with [CMake Modules](https://github.com/BoostCMake/cmake_modules.git) (Look at [crypto3](https://github.com/nilfoundation/crypto3.git) for the example) + +## Dependencies + +### Internal + +* [Hash](https://github.com/nilfoundation/crypto3-hash.git). + +### External +* [Boost](https://boost.org) (>= 1.76) diff --git a/libs/parallel-containers/example/CMakeLists.txt b/libs/parallel-containers/example/CMakeLists.txt new file mode 100644 index 0000000000..346575486f --- /dev/null +++ b/libs/parallel-containers/example/CMakeLists.txt @@ -0,0 +1,48 @@ +#---------------------------------------------------------------------------// +# MIT License +# +# Copyright (c) 2020 Mikhail Komarov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------// + + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../include" + "${CMAKE_CURRENT_BINARY_DIR}/include" + + ${Boost_INCLUDE_DIRS}) + +macro(define_containers_example example) + get_filename_component(test_name ${example} NAME) + set(target_name ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_${test_name}_example) + + add_executable(${target_name} ${example}.cpp) + target_link_libraries(${target_name} PRIVATE + ${CMAKE_WORKSPACE_NAME}::algebra + ${CMAKE_WORKSPACE_NAME}::hash + ${Boost_LIBRARIES}) + set_target_properties(${target_name} PROPERTIES CXX_STANDARD 17) +endmacro() + +set(EXAMPLES_NAMES + "merkle/merkle") + +foreach(EXAMPLE_NAME ${EXAMPLES_NAMES}) + define_containers_example(${EXAMPLE_NAME}) +endforeach() diff --git a/libs/parallel-containers/example/merkle/merkle.cpp b/libs/parallel-containers/example/merkle/merkle.cpp new file mode 100644 index 0000000000..cc7e60037c --- /dev/null +++ b/libs/parallel-containers/example/merkle/merkle.cpp @@ -0,0 +1,67 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2021-2022 Aleksei Moskvin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#include +#include +#include + +#include +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::containers; + +int main() { + std::vector > data_on_leafs = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}}; + std::array element_not_in_tree = {'9'}; + merkle_tree, 3> tree = make_merkle_tree, 3>(data_on_leafs.begin(), data_on_leafs.end()); + merkle_proof, 3> proof_leaf_3(tree, 3); + merkle_proof, 3> proof_leaf_0(tree, 0); +// std::cout << "Tree structure:" << std::endl; +// std::cout << tree << std::endl; + std::vector> data_to_check = {{data_on_leafs[2]}, {data_on_leafs[0]}, element_not_in_tree}; + for (size_t i = 0; i < data_to_check.size(); ++i) { + std::cout << "Is leaf " << data_to_check[i][0] << " was in tree in position 0: "; + std::cout << std::boolalpha << proof_leaf_0.validate(data_to_check[i]) << std::endl; + std::cout << "Is leaf " << data_to_check[i][0] << " was in tree in position 3: "; + std::cout << std::boolalpha << proof_leaf_3.validate(data_to_check[i]) << std::endl; + } + std::cout << std::endl; + + std::array left = {'\x6d', '\x65', '\x73', '\x73', '\x61', '\x67', '\x65'}; + std::array right = {'\x20', '\x64', '\x69', '\x67', '\x65', '\x73', '\x74'}; + std::vector > simple_binary_tree_data = {left, right}; + merkle_tree, 2> simple_binary_tree = make_merkle_tree, 2>(simple_binary_tree_data.begin(), simple_binary_tree_data.end()); + merkle_proof, 2> simple_binary_proof_leaf_1(simple_binary_tree, 1); +// std::cout << "Tree simple binary structure:" << std::endl; +// std::cout << simple_binary_tree << std::endl; + std::cout << "Is leaf " << data_on_leafs[1][0] << " was in tree in position 1: "; + std::cout << std::boolalpha << simple_binary_proof_leaf_1.validate(data_on_leafs[1]) << std::endl; + std::cout << "Is leaf left was in tree in position 1: "; + std::cout << std::boolalpha << simple_binary_proof_leaf_1.validate(left) << std::endl; + std::cout << "Is leaf right was in tree in position 1: "; + std::cout << std::boolalpha << simple_binary_proof_leaf_1.validate(right) << std::endl; + +} \ No newline at end of file diff --git a/libs/parallel-containers/include/nil/crypto3/container/accumulation_vector.hpp b/libs/parallel-containers/include/nil/crypto3/container/accumulation_vector.hpp new file mode 100644 index 0000000000..f2d4832049 --- /dev/null +++ b/libs/parallel-containers/include/nil/crypto3/container/accumulation_vector.hpp @@ -0,0 +1,104 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_SNARK_ACCUMULATION_VECTOR_HPP +#define CRYPTO3_ZK_SNARK_ACCUMULATION_VECTOR_HPP + +#include +#include + +#include + +namespace nil { + namespace crypto3 { + namespace container { + + /** + * An accumulation vector comprises an accumulation value and a sparse vector. + * The method "accumulate_chunk" allows one to accumulate portions of the sparse + * vector into the accumulation value. + */ + template + class accumulation_vector { + using underlying_value_type = typename Type::value_type; + + public: + using group_type = Type; + + underlying_value_type first; + sparse_vector rest; + + accumulation_vector() = default; + accumulation_vector(const accumulation_vector &other) = default; + accumulation_vector(accumulation_vector &&other) = default; + accumulation_vector(const underlying_value_type &first, sparse_vector &&rest) : + first(first), rest(std::move(rest)) {}; + accumulation_vector(underlying_value_type &&first, sparse_vector &&rest) : + first(std::move(first)), rest(std::move(rest)) {}; + accumulation_vector(underlying_value_type &&first, std::vector &&v) : + first(std::move(first)), rest(std::move(v)) { + } + accumulation_vector(std::vector &&v) : + first(underlying_value_type::zero()), rest(std::move(v)) {}; + + accumulation_vector &operator=(const accumulation_vector &other) = default; + accumulation_vector &operator=(accumulation_vector &&other) = default; + + bool operator==(const accumulation_vector &other) const { + return (this->first == other.first && this->rest == other.rest); + } + + bool is_fully_accumulated() const { + return rest.empty(); + } + + std::size_t domain_size() const { + return rest.domain_size(); + } + + std::size_t size() const { + return rest.domain_size(); + } + + std::size_t size_in_bits() const { + const std::size_t first_size_in_bits = Type::value_bits; + const std::size_t rest_size_in_bits = rest.size_in_bits(); + return first_size_in_bits + rest_size_in_bits; + } + + template + accumulation_vector accumulate_chunk(InputIterator begin, InputIterator end, + std::size_t offset) const { + std::pair> acc_result = rest.insert(offset, begin, end); + underlying_value_type new_first = first + acc_result.first; + return accumulation_vector(std::move(new_first), std::move(acc_result.second)); + } + }; + } // namespace container + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_SNARK_ACCUMULATION_VECTOR_HPP diff --git a/libs/parallel-containers/include/nil/crypto3/container/merkle/node.hpp b/libs/parallel-containers/include/nil/crypto3/container/merkle/node.hpp new file mode 100644 index 0000000000..66940c9f7b --- /dev/null +++ b/libs/parallel-containers/include/nil/crypto3/container/merkle/node.hpp @@ -0,0 +1,50 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2021 Aleksei Moskvin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_MERKLE_TREE_NODE_HPP +#define CRYPTO3_MERKLE_TREE_NODE_HPP + +#include + +namespace nil { + namespace crypto3 { + namespace containers { + namespace detail { + template + struct merkle_tree_node { + typedef Hash hash_type; + + constexpr static const std::size_t digest_bits = hash_type::digest_bits; + typedef typename hash_type::digest_type digest_type; + + typedef typename Hash::digest_type value_type; + constexpr static const std::size_t value_bits = digest_bits; + }; + } // namespace detail + } // namespace containers + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_NODE_HPP diff --git a/libs/parallel-containers/include/nil/crypto3/container/merkle/proof.hpp b/libs/parallel-containers/include/nil/crypto3/container/merkle/proof.hpp new file mode 100644 index 0000000000..12c29ab29f --- /dev/null +++ b/libs/parallel-containers/include/nil/crypto3/container/merkle/proof.hpp @@ -0,0 +1,305 @@ +//---------------------------------------------------------------------------// +// MIT License +// +// Copyright (c) 2020-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// Copyright (c) 2021-2022 Aleksei Moskvin +// Copyright (c) 2021 Ilias Khairullin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_MERKLE_PROOF_HPP +#define CRYPTO3_MERKLE_PROOF_HPP + +#include +#include +#include + +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace zk { + namespace components { + template + struct merkle_proof; + } // namespace components + } // namespace zk + namespace marshalling { + namespace types { + template + struct merkle_proof_marshalling; + } + } // namespace marshalling + namespace containers { + namespace detail { + template + class merkle_proof_impl { + public: + typedef NodeType node_type; + typedef typename node_type::hash_type hash_type; + + constexpr static const std::size_t arity = Arity; + + constexpr static const std::size_t value_bits = node_type::value_bits; + typedef typename node_type::value_type value_type; + + struct path_element_type { + path_element_type(value_type x, size_t pos) : _hash(x), _position(pos) { + } + path_element_type() { + } + + bool operator==(const path_element_type &rhs) const { + return _hash == rhs._hash && _position == rhs._position; + } + bool operator!=(const path_element_type &rhs) const { + return !(rhs == *this); + } + + const value_type &hash() const { + return _hash; + } + + std::size_t position() const { + return _position; + } + + value_type _hash; + std::size_t _position; + + template + friend class nil::crypto3::marshalling::types::merkle_proof_marshalling; + }; + + typedef std::array layer_type; + typedef std::vector path_type; + + merkle_proof_impl() : _li(0) {}; + + merkle_proof_impl(std::size_t li, value_type root, path_type path) : _li(li), _root(root), + _path(path){}; + + merkle_proof_impl(const merkle_tree &tree, const std::size_t leaf_idx) { + _root = tree.root(); + _path.resize(tree.row_count() - 1); + _li = leaf_idx; + + typename std::vector::iterator v_itr = _path.begin(); + std::size_t cur_leaf = leaf_idx; + std::size_t row_len = tree.leaves(); + std::size_t row_begin_idx = 0; + while (cur_leaf != tree.size() - 1) { // while it's not _root + std::size_t cur_leaf_pos = cur_leaf % arity; + std::size_t cur_leaf_arity_pos = (cur_leaf - row_begin_idx) / arity; + std::size_t begin_this_arity = cur_leaf - cur_leaf_pos; + typename layer_type::iterator a_itr = v_itr->begin(); + for (size_t i = 0; i < cur_leaf_pos; ++i, ++begin_this_arity, ++a_itr) { + *a_itr = path_element_type(tree[begin_this_arity], i); + } + for (size_t i = cur_leaf_pos + 1; i < arity; ++i, ++begin_this_arity, ++a_itr) { + *a_itr = path_element_type(tree[begin_this_arity + 1], i); + } + v_itr++; + cur_leaf = row_len + row_begin_idx + cur_leaf_arity_pos; + row_begin_idx += row_len; + row_len /= arity; + } + } + + template + bool validate(const Hashable &a) const { + using hash_type = typename NodeType::hash_type; + value_type d = crypto3::hash(a); + for (auto &it : _path) { + accumulator_set acc; + size_t i = 0; + for (; (i < arity - 1) && i == it[i]._position; ++i) { + crypto3::hash(it[i]._hash, acc); + } + crypto3::hash(d, acc); + for (; i < arity - 1; ++i) { + crypto3::hash(it[i]._hash, acc); + } + d = accumulators::extract::hash(acc); + } + return (d == _root); + } + + static std::vector + generate_compressed_proofs(const containers::merkle_tree &tree, + std::vector leaf_idxs) { + assert(leaf_idxs.size() > 0); + std::vector sorted_idx(leaf_idxs.size()); + std::iota(sorted_idx.begin(), sorted_idx.end(), 0); + std::sort(sorted_idx.begin(), sorted_idx.end(), [&leaf_idxs](std::size_t i, std::size_t j) { + return leaf_idxs[i] < leaf_idxs[j]; }); + std::vector result_proofs(leaf_idxs.size()); + std::size_t row_len = tree.leaves(); + std::vector known(2 * row_len, false); + std::size_t prev_leaf_idx = leaf_idxs[sorted_idx[0]] + 1; + for (auto idx : sorted_idx) { + auto leaf_idx = leaf_idxs[idx]; + if (leaf_idx == prev_leaf_idx) { + result_proofs[idx] = merkle_proof_impl(leaf_idx, tree.root(), path_type()); + assert(result_proofs[idx].path().size() == 0); + continue; + } + path_type path(tree.row_count() - 1); + typename path_type::iterator path_itr = path.begin(); + std::size_t cur_leaf = leaf_idx; + std::size_t row_len = tree.leaves(); + std::size_t row_begin_idx = 0; + bool finish_path = false; + while (cur_leaf != tree.size() - 1) { + std::size_t cur_leaf_pos = cur_leaf % Arity; + std::size_t cur_leaf_arity_pos = (cur_leaf - row_begin_idx) / Arity; + std::size_t begin_this_arity = cur_leaf - cur_leaf_pos; + typename layer_type::iterator layer_itr = path_itr->begin(); + for (size_t i = 0; i < cur_leaf_pos; ++i, ++begin_this_arity) { + if (!known[begin_this_arity]) { + known[begin_this_arity] = true; + } else { + finish_path = true; + } + *layer_itr = path_element_type(tree[begin_this_arity], i); + ++layer_itr; + } + for (size_t i = cur_leaf_pos + 1; i < Arity; ++i, ++begin_this_arity) { + if (!known[begin_this_arity + 1]) { + known[begin_this_arity + 1] = true; + } else { + finish_path = true; + } + *layer_itr = path_element_type(tree[begin_this_arity + 1], i); + ++layer_itr; + } + path_itr++; + if (finish_path) { + break; + } + cur_leaf = row_len + row_begin_idx + cur_leaf_arity_pos; + row_begin_idx += row_len; + row_len /= Arity; + } + path.resize(path_itr - path.begin()); + result_proofs[idx] = merkle_proof_impl(leaf_idx, tree.root(), path); + prev_leaf_idx = leaf_idx; + } + return result_proofs; + } + + template + static bool validate_compressed_proofs(const std::vector &proofs, + const std::vector &a) { + assert(proofs.size() == a.size()); + assert(proofs.size() > 0); + std::vector sorted_idx(proofs.size()); + std::iota(sorted_idx.begin(), sorted_idx.end(), 0); + std::sort(sorted_idx.begin(), sorted_idx.end(), [&proofs](std::size_t i, std::size_t j) { + return proofs[i].leaf_index() >= proofs[j].leaf_index(); }); + std::stack> st; + auto root = proofs[sorted_idx.back()].root(); + auto full_proof_size = proofs[sorted_idx.back()].path().size(); + for (auto idx : sorted_idx) { + auto path = proofs[idx].path(); + value_type d = crypto3::hash(a[idx]); + std::vector hashes = {d}; + for (auto &it : path) { + accumulator_set acc; + std::size_t i = 0; + for (; (i < Arity - 1) && i == it[i].position(); ++i) { + crypto3::hash(it[i].hash(), acc); + } + crypto3::hash(d.begin(), d.end(), acc); + for (; i < Arity - 1; ++i) { + crypto3::hash(it[i].hash(), acc); + } + d = accumulators::extract::hash(acc); + hashes.push_back(d); + } + while (!st.empty()) { + auto top = st.top(); + if (top.second >= hashes.size()) { + break; + } + if (hashes[top.second] == top.first) { + st.pop(); + } else { + return false; + } + } + if (path.size() < full_proof_size) { + st.push(std::make_pair(d, path.size())); + } else if (d != root) { + return false; + } + } + return true; + } + + std::size_t leaf_index() const { + return _li; + } + + bool operator==(const merkle_proof_impl &rhs) const { + return _li == rhs._li && _root == rhs._root && _path == rhs._path; + } + bool operator!=(const merkle_proof_impl &rhs) const { + return !(rhs == *this); + } + + const value_type &root() const { + return _root; + } + + const path_type &path() const { + return _path; + } + + private: + std::size_t _li; + value_type _root; + path_type _path; + + template + friend class nil::crypto3::zk::components::merkle_proof; + + template + friend class nil::crypto3::marshalling::types::merkle_proof_marshalling; + }; + + + } // namespace detail + + template + using merkle_proof = + typename std::conditional::value, + detail::merkle_proof_impl, Arity>, + detail::merkle_proof_impl>::type; + + } // namespace containers + } // namespace crypto3 +} // namespace nil + +#endif diff --git a/libs/parallel-containers/include/nil/crypto3/container/merkle/tree.hpp b/libs/parallel-containers/include/nil/crypto3/container/merkle/tree.hpp new file mode 100644 index 0000000000..ac284a0728 --- /dev/null +++ b/libs/parallel-containers/include/nil/crypto3/container/merkle/tree.hpp @@ -0,0 +1,532 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2021-2022 Aleksei Moskvin +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_MERKLE_TREE_HPP +#define CRYPTO3_MERKLE_TREE_HPP + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace containers { + namespace detail { + // returns next highest power of two from a given number if it is not + // already a power of two. + inline size_t next_pow2(size_t n) { + return std::pow(2, std::ceil(std::log(n))); + } + + // find power of 2 of a number which is power of 2 + inline size_t log2_pow2(size_t n) { + return next_pow2(n); + } + + // Row_Count calculation given the number of _leaves in the tree and the branches. + inline size_t merkle_tree_row_count(size_t leafs, size_t branches) { + // Optimization + if (branches == 2) { + return std::log2(leafs) + 1; + } else { + return round(std::log(leafs) / std::log(branches)) + 1; + } + } + + // Tree length calculation given the number of _leaves in the tree and the branches. + inline size_t merkle_tree_length(size_t leafs, size_t branches) { + // Optimization + size_t len = leafs; + if (branches == 2) { + len = 2 * leafs - 1; + } else { + size_t cur = leafs; + while (cur != 0) { + cur /= branches; + len += cur; + } + } + return len; + } + + // This method returns the number of '_leaves' given a merkle tree + // length of 'len', where _leaves must be a power of 2, respecting the + // number of branches. + inline size_t merkle_tree_leaves(size_t tree_s, size_t branches) { + // Optimization + size_t len = tree_s; + if (branches == 2) { + len = (tree_s + 1) >> 1; + } else { + size_t cur = 1; + while (cur < len) { + len -= cur; + cur *= branches; + } + } + return len; + } + + // Tree length calculation given the number of _leaves in the tree, the + // rows_to_discard, and the branches. + inline size_t merkle_tree_cache_size(size_t leafs, size_t branches, size_t rows_to_discard) { + size_t shift = log2_pow2(branches); + size_t len = merkle_tree_length(leafs, branches); + size_t row_count = merkle_tree_row_count(leafs, branches); + + // '_rc - 1' means that we start discarding rows above the base + // layer, which is included in the current _rc. + size_t cache_base = row_count - 1 - rows_to_discard; + + size_t cache_size = len; + size_t cur_leafs = leafs; + + while (row_count > cache_base) { + cache_size -= cur_leafs; + cur_leafs >>= shift; // cur /= branches + row_count -= 1; + } + + return cache_size; + } + + inline bool is_merkle_tree_size_valid(size_t leafs, size_t branches) { + if (branches == 0 || leafs != next_pow2(leafs) || branches != next_pow2(branches)) { + return false; + } + + size_t cur = leafs; + size_t shift = log2_pow2(branches); + while (cur != 1) { + cur >>= shift; // cur /= branches + if (cur > leafs || cur == 0) { + return false; + } + } + + return true; + } + + // Given a tree of '_rc' with the specified number of 'branches', + // calculate the length of _hashes required for the proof. + inline size_t merkle_proof_lemma_length(size_t row_count, size_t branches) { + return 2 + ((branches - 1) * (row_count - 1)); + } + + // Merkle Tree. + // + // All _leaves and nodes are stored in a BGL graph structure. + // + // A merkle tree is a tree in which every non-leaf node is the hash of its + // child nodes. A diagram for merkle_tree_impl arity = 2: + // + // root = h1234 = h(h12 + h34) + // ./ \. + // h12 = h(h1 + h2) h34 = h(h3 + h4) + // ./ \. ./ \. + // h1 = h(tx1) h2 = h(tx2) h3 = h(tx3) h4 = h(tx4) + // ``` + // + // In graph representation: + // + // ```text + // root -> h12, h34 + // h12 -> h1, h2 + // h34 -> h3, h4 + // ``` + // + // Merkle root is always the top element. + template + struct merkle_tree_impl { + typedef NodeType node_type; + + typedef typename node_type::hash_type hash_type; + + typedef typename node_type::value_type value_type; + constexpr static const std::size_t value_bits = node_type::value_bits; + + typedef std::vector container_type; + + typedef typename container_type::allocator_type allocator_type; + typedef typename container_type::reference reference; + typedef typename container_type::const_reference const_reference; + typedef typename container_type::size_type size_type; + typedef typename container_type::difference_type difference_type; + typedef typename container_type::pointer pointer; + typedef typename container_type::const_pointer const_pointer; + typedef typename container_type::iterator iterator; + typedef typename container_type::const_iterator const_iterator; + typedef typename container_type::reverse_iterator reverse_iterator; + typedef typename container_type::const_reverse_iterator const_reverse_iterator; + + merkle_tree_impl() : _size(0), _leaves(0), _rc(0) {}; + + ~merkle_tree_impl() = default; + + merkle_tree_impl(size_t n) : + _size(detail::merkle_tree_length(n, Arity)), _leaves(n), + _rc(detail::merkle_tree_row_count(n, Arity)) { + BOOST_ASSERT_MSG(pow(Arity, round(std::log(n) / std::log(Arity))) == n, + "Wrong leaves number, it must be a power of Arity."); + } + + merkle_tree_impl(const merkle_tree_impl &x) : + _hashes(x._hashes), _size(x._size), _leaves(x._leaves), _rc(x._rc) { + } + + merkle_tree_impl(const merkle_tree_impl &x, const allocator_type &a) : _hashes(x.hashes(), a), + _size(x._size), + _leaves(x._leaves), + _rc(x._rc) {} + + merkle_tree_impl(const std::initializer_list &il) : _hashes(il) { + set_leaves(detail::merkle_tree_leaves(std::distance(il.begin(), il.end()), Arity)); + set_row_count(detail::merkle_tree_row_count(_leaves, Arity)); + set_complete_size(detail::merkle_tree_length(_leaves, Arity)); + } + + template::value, bool>::type = true> + merkle_tree_impl(Iterator first, Iterator last) : _hashes(first, last) { + set_leaves(detail::merkle_tree_leaves(std::distance(first, last), Arity)); + set_row_count(detail::merkle_tree_row_count(_leaves, Arity)); + set_complete_size(detail::merkle_tree_length(_leaves, Arity)); + } + + merkle_tree_impl(const std::initializer_list &il, const allocator_type &a) : _hashes(il, a) { + set_leaves(detail::merkle_tree_leaves(std::distance(il.begin(), il.end()), Arity)); + set_row_count(detail::merkle_tree_row_count(_leaves, Arity)); + set_complete_size(detail::merkle_tree_length(_leaves, Arity)); + } + + merkle_tree_impl(merkle_tree_impl &&x) + BOOST_NOEXCEPT(std::is_nothrow_move_constructible::value): + _hashes(x._hashes), + _size(x._size), _leaves(x._leaves), _rc(x._rc) { + } + + merkle_tree_impl(merkle_tree_impl &&x, const allocator_type &a) : + _hashes(x.hashes(), a), _size(x._size), _leaves(x._leaves), _rc(x._rc) { + } + + merkle_tree_impl &operator=(const merkle_tree_impl &x) { + _hashes = x.hashes(); + return *this; + } + + merkle_tree_impl &operator=(merkle_tree_impl &&x) { + _hashes = x._hashes; + _size = x._size; + _leaves = x._leaves; + _rc = x._rc; + return *this; + } + + bool operator==(const merkle_tree_impl &rhs) const { + return _hashes == rhs.val; + } + + bool operator!=(const merkle_tree_impl &rhs) const { + return !(rhs == *this); + } + + allocator_type get_allocator() const BOOST_NOEXCEPT { + return this->val.__alloc(); + } + + iterator begin() BOOST_NOEXCEPT { + return _hashes.begin(); + } + + const_iterator begin() const BOOST_NOEXCEPT { + return _hashes.begin(); + } + + iterator end() BOOST_NOEXCEPT { + return _hashes.end(); + } + + const_iterator end() const BOOST_NOEXCEPT { + return _hashes.end(); + } + + reverse_iterator rbegin() BOOST_NOEXCEPT { + return _hashes.rbegin(); + } + + const_reverse_iterator rbegin() const BOOST_NOEXCEPT { + return _hashes.rbegin(); + } + + reverse_iterator rend() BOOST_NOEXCEPT { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const BOOST_NOEXCEPT { + return const_reverse_iterator(begin()); + } + + const_iterator cbegin() const BOOST_NOEXCEPT { + return begin(); + } + + const_iterator cend() const BOOST_NOEXCEPT { + return end(); + } + + const_reverse_iterator crbegin() const BOOST_NOEXCEPT { + return rbegin(); + } + + const_reverse_iterator crend() const BOOST_NOEXCEPT { + return rend(); + } + + size_type size() const BOOST_NOEXCEPT { + return _hashes.size(); + } + + size_type complete_size() const BOOST_NOEXCEPT { + return _size; + } + + size_type capacity() const BOOST_NOEXCEPT { + return _hashes.capacity(); + } + + bool empty() const BOOST_NOEXCEPT { + return (_hashes.size() == 0); + } + + size_type max_size() const BOOST_NOEXCEPT { + return _hashes.max_size(); + } + + void reserve(size_type _n) { + return _hashes.reserve(_n); + } + + void shrink_to_fit() BOOST_NOEXCEPT { + return _hashes.shrink_to_fit(); + } + + reference operator[](size_type _n) BOOST_NOEXCEPT { + return _hashes[_n]; + } + + const_reference operator[](size_type _n) const BOOST_NOEXCEPT { + return _hashes[_n]; + } + + reference at(size_type _n) { + return _hashes.at(_n); + } + + const_reference at(size_type _n) const { + return _hashes.at(_n); + } + + reference front() BOOST_NOEXCEPT { + return _hashes.front(); + } + + const_reference front() const BOOST_NOEXCEPT { + return _hashes.front(); + } + + reference back() BOOST_NOEXCEPT { + return _hashes.back(); + } + + const_reference back() const BOOST_NOEXCEPT { + return _hashes.back(); + } + + value_type *hashes() BOOST_NOEXCEPT { + return _hashes; + } + + const value_type *hashes() const BOOST_NOEXCEPT { + return _hashes; + } + + void push_back(const_reference _x) { + // #error ERROR + _hashes.push_back(_x); + } + + void push_back(value_type &&_x) { + // #error ERROR + _hashes.push_back(_x); + } + + // + template + reference emplace_back(Args &&..._args) { + return _hashes.template emplace_back(_args...); + } + + template + iterator emplace(const_iterator _position, Args &&... _args) { + return _hashes.template emplace(_position, _args...); + } + + void pop_back() { + _hashes.pop_back(); + } + + void clear() BOOST_NOEXCEPT { + _hashes.clear(); + } + + void resize(size_type _sz) { + return _hashes.resize(_sz); + } + + void resize(size_type _sz, const_reference _x) { + return _hashes.resize(_sz, _x); + } + + void swap(merkle_tree_impl &other) { + _hashes.swap(other.hashes()); + std::swap(_leaves, other.leaves()); + std::swap(_rc, other.rc()); + std::swap(_size, other.size()); + } + + value_type root() const BOOST_NOEXCEPT { + BOOST_ASSERT_MSG(_size == _hashes.size(), "MerkleTree not fulfilled"); + return _hashes[_size - 1]; + } + + value_type root() BOOST_NOEXCEPT { + BOOST_ASSERT_MSG(_size == _hashes.size(), "MerkleTree not fulfilled"); + return _hashes[_size - 1]; + } + + size_t row_count() const { + return _rc; + } + + size_t leaves() const { + return _leaves; + } + + void set_leaves(size_t s) { + _leaves = s; + } + + void set_row_count(size_t s) { + _rc = s; + } + + void set_complete_size(size_t s) { + _size = s; + } + + protected: + container_type _hashes; + + size_t _size; + size_t _leaves; + // Note: The former 'upstream' merkle_light project uses 'height' + // (with regards to the tree property) incorrectly, so we've + // renamed it since it's actually a '_rc'. For example, a + // tree with 2 leaf nodes and a single root node has a height of + // 1, but a _rc of 2. + // + // Internally, this code considers only the _rc. + size_t _rc; + }; + + template + typename T::digest_type generate_hash(LeafIterator first, LeafIterator last) { + accumulator_set acc; + while (first != last) { + crypto3::hash(*first++, acc); + } + return accumulators::extract::hash(acc); + } + + template + merkle_tree_impl make_merkle_tree(LeafIterator first, LeafIterator last) { + typedef T node_type; + typedef typename node_type::hash_type hash_type; + typedef typename node_type::value_type value_type; + typedef typename std::iterator_traits::value_type leaf_value_type; + + merkle_tree_impl ret(std::distance(first, last)); + ret.resize(ret.complete_size()); + + nil::crypto3::parallel_transform(first, last, ret.begin(), [](const leaf_value_type& leaf) { + return static_cast(crypto3::hash(leaf)); + }); + + std::size_t row_idx = ret.leaves(), row_size = row_idx / Arity; + typename merkle_tree_impl::iterator it = ret.begin(); + + std::size_t next_row_start_index = std::distance(first, last); + + for (size_t row_number = 1; row_number < ret.row_count(); ++row_number, row_size /= Arity) { + nil::crypto3::parallel_for(0, row_size, [&ret, it, next_row_start_index](std::size_t index) { + ret[next_row_start_index + index] = generate_hash( + it + index * Arity, it + (index + 1) * Arity); + }); + next_row_start_index += row_size; + it += row_size * Arity; + } + return ret; + } + } // namespace detail + + template + using merkle_tree = typename std::conditional::value, + detail::merkle_tree_impl, Arity>, + detail::merkle_tree_impl>::type; + + template + merkle_tree make_merkle_tree(LeafIterator first, LeafIterator last) { + return detail::make_merkle_tree::value, + detail::merkle_tree_node, + T>::type, + Arity>(first, last); + } + + } // namespace containers + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_MERKLE_TREE_HPP diff --git a/libs/parallel-containers/include/nil/crypto3/container/sparse_vector.hpp b/libs/parallel-containers/include/nil/crypto3/container/sparse_vector.hpp new file mode 100644 index 0000000000..c074e3b263 --- /dev/null +++ b/libs/parallel-containers/include/nil/crypto3/container/sparse_vector.hpp @@ -0,0 +1,298 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2021 Mikhail Komarov +// Copyright (c) 2020-2021 Nikita Kaskov +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for a sparse vector. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_ZK_SPARSE_VECTOR_HPP +#define CRYPTO3_ZK_SPARSE_VECTOR_HPP + +#include +#include +#include + +#include +#include + +namespace nil { + namespace crypto3 { + namespace container { + + /** + * A sparse vector is a list of indices along with corresponding values. + * The indices are selected from the set {0,1,...,domain_size-1}. + */ + template + class sparse_vector { + using underlying_value_type = typename Type::value_type; + + template + using container_type = std::vector; + + typedef container_type value_container_type; + + public: + using group_type = Type; + + typedef typename value_container_type::value_type value_type; + typedef typename value_container_type::allocator_type allocator_type; + typedef typename value_container_type::reference reference; + typedef typename value_container_type::const_reference const_reference; + typedef typename value_container_type::size_type size_type; + typedef typename value_container_type::difference_type difference_type; + typedef typename value_container_type::pointer pointer; + typedef typename value_container_type::const_pointer const_pointer; + typedef typename value_container_type::iterator iterator; + typedef typename value_container_type::const_iterator const_iterator; + typedef typename value_container_type::reverse_iterator reverse_iterator; + typedef typename value_container_type::const_reverse_iterator const_reverse_iterator; + + container_type indices; + container_type values; + std::size_t domain_size_; + + sparse_vector() = default; + + sparse_vector(const sparse_vector &other) = default; + + sparse_vector(sparse_vector &&other) = default; + + sparse_vector(value_container_type &&v) : values(std::move(v)), domain_size_(values.size()) { + indices.resize(domain_size_); + std::iota(indices.begin(), indices.end(), 0); + } + + explicit sparse_vector(size_type n) : values(n) { + } + explicit sparse_vector(size_type n, const allocator_type &a) : values(n, a) { + } + + sparse_vector(size_type n, const value_type &x) : values(n, x) { + } + sparse_vector(size_type n, const value_type &x, const allocator_type &a) : values(n, x, a) { + } + template + sparse_vector(InputIterator first, InputIterator last) : values(first, last) { + } + template + sparse_vector(InputIterator first, InputIterator last, const allocator_type &a) : + values(first, last, a) { + } + + ~sparse_vector() = default; + + sparse_vector(std::initializer_list il) : values(il) { + } + + sparse_vector(std::initializer_list il, const allocator_type &a) : values(il, a) { + } + + sparse_vector &operator=(const sparse_vector &other) = default; + sparse_vector &operator=(sparse_vector &&other) = default; + + underlying_value_type operator[](const std::size_t idx) const { + auto it = std::lower_bound(indices.begin(), indices.end(), idx); + return (it != indices.end() && *it == idx) ? values[it - indices.begin()] : underlying_value_type(); + } + + bool operator==(const sparse_vector &other) const { + if (this->domain_size_ != other.domain_size_) { + return false; + } + + std::size_t this_pos = 0, other_pos = 0; + while (this_pos < this->indices.size() && other_pos < other.indices.size()) { + if (this->indices[this_pos] == other.indices[other_pos]) { + if (this->values[this_pos] != other.values[other_pos]) { + return false; + } + ++this_pos; + ++other_pos; + } else if (this->indices[this_pos] < other.indices[other_pos]) { + if (!this->values[this_pos].is_zero()) { + return false; + } + ++this_pos; + } else { + if (!other.values[other_pos].is_zero()) { + return false; + } + ++other_pos; + } + } + + /* at least one of the vectors has been exhausted, so other must be empty */ + while (this_pos < this->indices.size()) { + if (!this->values[this_pos].is_zero()) { + return false; + } + ++this_pos; + } + + while (other_pos < other.indices.size()) { + if (!other.values[other_pos].is_zero()) { + return false; + } + ++other_pos; + } + + return true; + } + + bool operator==(const value_container_type &other) const { + if (this->domain_size_ < other.size()) { + return false; + } + + std::size_t j = 0; + for (std::size_t i = 0; i < other.size(); ++i) { + if (this->indices[j] == i) { + if (this->values[j] != other[j]) { + return false; + } + ++j; + } else { + if (!other[j].is_zero()) { + return false; + } + } + } + + return true; + } + + bool is_valid() const { + if (values.size() == indices.size() && values.size() <= domain_size_) { + return false; + } + + for (std::size_t i = 0; i + 1 < indices.size(); ++i) { + if (indices[i] >= indices[i + 1]) { + return false; + } + } + + if (!indices.empty() && indices[indices.size() - 1] >= domain_size_) { + return false; + } + + return true; + } + + bool empty() const { + return indices.empty(); + } + + std::size_t domain_size() const { + return domain_size_; + } + + std::size_t size() const { + return indices.size(); + } + + std::size_t size_in_bits() const { + return indices.size() * (sizeof(std::size_t) * 8 + Type::value_bits); + } + + /* return a pair consisting of the accumulated value and the sparse vector of non-accumulated values + */ + template + std::pair> + insert(std::size_t offset, InputBaseIterator first, InputBaseIterator last) const { +#ifdef MULTICORE + const std::size_t chunks = omp_get_max_threads(); // to override, set OMP_NUM_THREADS env var + // or call omp_set_num_threads() +#else + const std::size_t chunks = 1; +#endif + + underlying_value_type accumulated_value = underlying_value_type::zero(); + sparse_vector resulting_vector; + resulting_vector.domain_size_ = domain_size_; + + const std::size_t range_len = std::distance(first, last); + bool in_block = false; + std::size_t first_pos = -1, + last_pos = -1; // g++ -flto emits unitialized warning, even though in_block + // guards for such cases. + + for (std::size_t i = 0; i < indices.size(); ++i) { + const bool matching_pos = (offset <= indices[i] && indices[i] < offset + range_len); + // printf("i = %zu, pos[i] = %zu, offset = %zu, w_size = %zu\n", i, indices[i], offset, + // w_size); + bool copy_over; + + if (in_block) { + if (matching_pos && last_pos == i - 1) { + // block can be extended, do it + last_pos = i; + copy_over = false; + } else { + // block has ended here + in_block = false; + copy_over = true; + + accumulated_value = accumulated_value + + algebra::multiexp( + values.begin() + first_pos, values.begin() + last_pos + 1, + first + (indices[first_pos] - offset), + last + (indices[last_pos] - offset) + 1, chunks); + } + } else { + if (matching_pos) { + // block can be started + first_pos = i; + last_pos = i; + in_block = true; + copy_over = false; + } else { + copy_over = true; + } + } + + if (copy_over) { + resulting_vector.indices.emplace_back(indices[i]); + resulting_vector.values.emplace_back(values[i]); + } + } + + if (in_block) { + accumulated_value = + accumulated_value + algebra::multiexp( + values.begin() + first_pos, + values.begin() + last_pos + 1, + first + (indices[first_pos] - offset), + first + (indices[last_pos] - offset) + 1, + chunks); + } + + return std::make_pair(accumulated_value, resulting_vector); + } + }; + } // namespace container + } // namespace crypto3 +} // namespace nil + +#endif // CRYPTO3_ZK_SPARSE_VECTOR_HPP diff --git a/libs/parallel-containers/test/CMakeLists.txt b/libs/parallel-containers/test/CMakeLists.txt new file mode 100644 index 0000000000..6451bb7284 --- /dev/null +++ b/libs/parallel-containers/test/CMakeLists.txt @@ -0,0 +1,86 @@ +#---------------------------------------------------------------------------// +# MIT License +# +# Copyright (c) 2020 Mikhail Komarov +# Copyright (c) 2021 Aleksei Moskvin +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +#---------------------------------------------------------------------------// + +include(CMTest) + +cm_test_link_libraries(${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME} + + crypto3::algebra + crypto3::hash + + Boost::random) + +macro(define_storage_test test) + get_filename_component(test_name ${test} NAME) + set(target_name ${CMAKE_WORKSPACE_NAME}_${CURRENT_PROJECT_NAME}_${test_name}_test) + + while(TARGET ${target_name}) + get_filename_component(TEST_DIRECTORY ${test} DIRECTORY) + get_filename_component(PARENT_DIR ${TEST_DIRECTORY} DIRECTORY) + set(target_name ${PARENT_DIR}_${target_name}) + endwhile() + + set(additional_args "") + if(ENABLE_JUNIT_TEST_OUTPUT) + set(TEST_RESULTS_DIR "${CMAKE_CURRENT_BINARY_DIR}/junit_results") + set(TEST_LOGS_DIR "${TEST_RESULTS_DIR}/logs") + file(MAKE_DIRECTORY ${TEST_LOGS_DIR}) + + set(additional_args "--log_format=JUNIT" + "--log_sink=${TEST_LOGS_DIR}/${target_name}.xml") + endif() + + cm_test(NAME ${target_name} SOURCES ${test}.cpp ARGS ${additional_args}) + + target_include_directories(${target_name} PRIVATE + "$" + "$" + + ${Boost_INCLUDE_DIRS}) + + set_target_properties(${target_name} PROPERTIES CXX_STANDARD 17) + + get_target_property(target_type Boost::unit_test_framework TYPE) + if(target_type STREQUAL "SHARED_LIB") + target_compile_definitions(${target_name} PRIVATE BOOST_TEST_DYN_LINK) + elseif(target_type STREQUAL "STATIC_LIB") + + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(${target_name} PRIVATE "-fconstexpr-steps=2147483647" "-ftemplate-backtrace-limit=0") + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${target_name} PRIVATE "-fconstexpr-ops-limit=4294967295" "-ftemplate-backtrace-limit=0") + endif() + +endmacro() + +set(TESTS_NAMES + "merkle/merkle" +) + +foreach(TEST_NAME ${TESTS_NAMES}) + define_storage_test(${TEST_NAME}) +endforeach() diff --git a/libs/parallel-containers/test/merkle/merkle.cpp b/libs/parallel-containers/test/merkle/merkle.cpp new file mode 100644 index 0000000000..f3d8dc2d5d --- /dev/null +++ b/libs/parallel-containers/test/merkle/merkle.cpp @@ -0,0 +1,400 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2018-2020 Mikhail Komarov +// Copyright (c) 2021-2022 Aleksei Moskvin +// Copyright (c) 2021 Ilias Khairullin +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE containter_merkletree_test + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace nil::crypto3; +using namespace nil::crypto3::containers; + +template +typename std::enable_if::value, std::vector>>::type + generate_random_data(std::size_t leaf_number) { + std::vector> v; + for (std::size_t i = 0; i < leaf_number; ++i) { + std::array leaf {}; + std::generate(std::begin(leaf), std::end(leaf), + [&]() { return std::rand() % (std::numeric_limits::max() + 1); }); + v.emplace_back(leaf); + } + return v; +} + +template +typename std::enable_if::value, std::vector>>::type + generate_random_data(std::size_t leaf_number) { + std::vector> v; + for (std::size_t i = 0; i < leaf_number; ++i) { + std::array leaf {}; + std::generate(std::begin(leaf), std::end(leaf), + [&]() { return algebra::random_element(); }); + v.emplace_back(leaf); + } + return v; +} + +template +void testing_validate_template_random_data(std::size_t leaf_number) { + std::array data_not_in_tree = {0u}; + auto data = generate_random_data(leaf_number); + auto tree = make_merkle_tree(data.begin(), data.end()); + + std::size_t proof_idx = std::rand() % leaf_number; + merkle_proof proof(tree, proof_idx); + bool good_validate = proof.validate(data[proof_idx]); + bool wrong_leaf_validate = proof.validate(data[(proof_idx + 1) % leaf_number]); + bool wrong_data_validate = proof.validate(data_not_in_tree); + BOOST_CHECK(good_validate); + BOOST_CHECK(!wrong_leaf_validate); + BOOST_CHECK(!wrong_data_validate); +} + +template +void testing_validate_template(std::vector data) { + std::array data_not_in_tree = {'\x6d', '\x65', '\x73', '\x73', '\x61', '\x67', '\x65'}; + merkle_tree tree = make_merkle_tree(data.begin(), data.end()); + merkle_tree tree2(tree.begin(), tree.end()); +// for (auto i = 0; i < tree.size(); ++i) { +// std::cout << tree[i] << std::endl; +// } +// tree.emplace_back(nil::crypto3::hash(data_not_in_tree[0])); + merkle_proof proof(tree, 0); + bool good_validate = proof.validate(data[0]); + bool wrong_leaf_validate = proof.validate(data[1]); + bool wrong_data_validate = proof.validate(data_not_in_tree); + BOOST_CHECK(true == good_validate); + BOOST_CHECK(false == wrong_leaf_validate); + BOOST_CHECK(false == wrong_data_validate); +} + +template +void testing_validate_template_random_data_compressed_proofs(std::size_t leaf_number) { + using merkle_proof_type = typename containers::merkle_proof; + using Element = std::array; + std::array data_not_in_tree = {0}; + auto data = generate_random_data(leaf_number); + auto tree = make_merkle_tree(data.begin(), data.end()); + + std::size_t num_idxs = std::rand() % leaf_number; + while (num_idxs == 0) { + num_idxs = std::rand() % leaf_number; + } + + std::vector proof_idxs; + std::vector data_for_validation; + for (std::size_t i = 0; i < num_idxs; ++i) { + proof_idxs.emplace_back(std::rand() % leaf_number); + } + for (auto idx : proof_idxs) { + data_for_validation.emplace_back(data[idx]); + } + + // standard case + auto start = std::chrono::high_resolution_clock::now(); + std::vector> compressed_proofs = merkle_proof_type::generate_compressed_proofs(tree, proof_idxs); + bool validate_compressed = merkle_proof_type::validate_compressed_proofs(compressed_proofs, data_for_validation); + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); + // case for arity == 4 + if (leaf_number == 16) { + std::vector> compressed_proofs_one_idx = merkle_proof_type::generate_compressed_proofs(tree, {3, 2, 1, 5, 11, 11, 0}); + bool validate_compressed_one_idx = merkle_proof_type::validate_compressed_proofs(compressed_proofs_one_idx, std::vector({data[3], data[2], data[1], data[5], data[11], data[11], data[0]})); + BOOST_CHECK(validate_compressed_one_idx); + } + // one index + std::size_t one_idx = std::rand() % leaf_number; + std::vector> compressed_proofs_one_idx = merkle_proof_type::generate_compressed_proofs(tree, {one_idx}); + bool validate_compressed_one_idx = merkle_proof_type::validate_compressed_proofs(compressed_proofs_one_idx, std::vector({data[one_idx]})); + // edge indexes + std::vector> compressed_proofs_edge_idxs = merkle_proof_type::generate_compressed_proofs(tree, {0, leaf_number - 1}); + bool validate_compressed_edge_idxs = merkle_proof_type::validate_compressed_proofs(compressed_proofs_edge_idxs, std::vector({data[0], data[leaf_number - 1]})); + // repeated indexes + std::size_t repeated_idx = std::rand() % leaf_number; + std::vector> compressed_proofs_repeated_idxs = merkle_proof_type::generate_compressed_proofs(tree, {repeated_idx, leaf_number / 2, repeated_idx}); + bool validate_compressed_repeated_idxs = merkle_proof_type::validate_compressed_proofs(compressed_proofs_repeated_idxs, std::vector({data[repeated_idx], data[leaf_number / 2], data[repeated_idx]})); + // wrong leaf + auto sorted_idxs = proof_idxs; + std::sort(sorted_idxs.begin(), sorted_idxs.end()); + std::size_t wrong_leaf_idx = 0; + for (auto idx : sorted_idxs) { + if (idx == wrong_leaf_idx) { + wrong_leaf_idx++; + } else { + break; + } + } + auto data_wrong_leaf = data_for_validation; + data_wrong_leaf[std::rand() % num_idxs] = data[wrong_leaf_idx]; + assert(data_wrong_leaf != data_for_validation); + bool wrong_leaf_validate_compressed = merkle_proof_type::validate_compressed_proofs(compressed_proofs, data_wrong_leaf); + // wrong data + auto data_wrong_data = data_for_validation; + data_wrong_data[std::rand() % num_idxs] = data_not_in_tree; + assert(data_wrong_data != data_for_validation); + bool wrong_data_validate_compressed = merkle_proof_type::validate_compressed_proofs(compressed_proofs, data_wrong_data); + + BOOST_CHECK(validate_compressed); + BOOST_CHECK(validate_compressed_one_idx); + BOOST_CHECK(validate_compressed_edge_idxs); + BOOST_CHECK(validate_compressed_repeated_idxs); + BOOST_CHECK(!wrong_leaf_validate_compressed); + BOOST_CHECK(!wrong_data_validate_compressed); +} + +template +void testing_validate_template_compressed_proofs(std::vector data) { + using merkle_proof_type = typename containers::merkle_proof; + merkle_tree tree = make_merkle_tree(data.begin(), data.end()); + + std::size_t leaf_number = data.size(); + std::size_t num_idxs = std::rand() % leaf_number; + while (num_idxs == 0) { + num_idxs = std::rand() % leaf_number; + } + std::vector proof_idxs; + std::vector data_for_validation; + for (std::size_t i = 0; i < num_idxs; ++i) { + proof_idxs.emplace_back(std::rand() % leaf_number); + } + for (auto idx : proof_idxs) { + data_for_validation.emplace_back(data[idx]); + } + + // standart case + std::vector> compressed_proofs = merkle_proof_type::generate_compressed_proofs(tree, proof_idxs); + bool validate_compressed = merkle_proof_type::validate_compressed_proofs(compressed_proofs, data_for_validation); + // one index + std::size_t one_idx = std::rand() % leaf_number; + std::vector> compressed_proofs_one_idx = merkle_proof_type::generate_compressed_proofs(tree, {one_idx}); + bool validate_compressed_one_idx = merkle_proof_type::validate_compressed_proofs(compressed_proofs_one_idx, std::vector({data[one_idx]})); + // edge indexes + std::vector> compressed_proofs_edge_idxs = merkle_proof_type::generate_compressed_proofs(tree, {0, leaf_number - 1}); + bool validate_compressed_edge_idxs = merkle_proof_type::validate_compressed_proofs(compressed_proofs_edge_idxs, std::vector({data[0], data[leaf_number - 1]})); + // repeated indexes + std::size_t repeated_idx = std::rand() % leaf_number; + std::vector> compressed_proofs_repeated_idxs = merkle_proof_type::generate_compressed_proofs(tree, {repeated_idx, leaf_number - 1, repeated_idx}); + bool validate_compressed_repeated_idxs = merkle_proof_type::validate_compressed_proofs(compressed_proofs_repeated_idxs, std::vector({data[repeated_idx], data[leaf_number - 1], data[repeated_idx]})); + // wrong leaf + auto sorted_idxs = proof_idxs; + std::sort(sorted_idxs.begin(), sorted_idxs.end()); + std::size_t wrong_leaf_idx = 0; + for (auto idx : sorted_idxs) { + if (idx == wrong_leaf_idx) { + wrong_leaf_idx++; + } else { + break; + } + } + auto data_wrong_leaf = data_for_validation; + data_wrong_leaf[std::rand() % num_idxs] = data[wrong_leaf_idx]; + assert(data_wrong_leaf != data_for_validation); + bool wrong_leaf_validate_compressed = merkle_proof_type::validate_compressed_proofs(compressed_proofs, data_wrong_leaf); + // wrong data + auto data_wrong_data = data_for_validation; + data_wrong_data[std::rand() % num_idxs] = {'9'}; + assert(data_wrong_data != data_for_validation); + bool wrong_data_validate_compressed = merkle_proof_type::validate_compressed_proofs(compressed_proofs, data_wrong_data); + + BOOST_CHECK(validate_compressed); + BOOST_CHECK(validate_compressed_one_idx); + BOOST_CHECK(validate_compressed_edge_idxs); + BOOST_CHECK(validate_compressed_repeated_idxs); + BOOST_CHECK(!wrong_leaf_validate_compressed); + BOOST_CHECK(!wrong_data_validate_compressed); +} + +template +void testing_hash_template(std::vector data, std::string result) { + merkle_tree tree = make_merkle_tree(data.begin(), data.end()); + BOOST_CHECK(result == std::to_string(tree.root())); +} + +BOOST_AUTO_TEST_SUITE(containers_merkltree_test) + +using curve_type = algebra::curves::pallas; +using field_type = typename curve_type::base_field_type; +using poseidon_type = hashes::poseidon>; +using original_poseidon_type = hashes::original_poseidon>; + +BOOST_AUTO_TEST_CASE(merkletree_construct_test_1) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}}; + merkle_tree, 2> tree_res = make_merkle_tree, 2>(v.begin(), v.end()); + merkle_tree, 2> tree(tree_res.begin(), tree_res.end()); + BOOST_CHECK_EQUAL(tree.size(), 15); + BOOST_CHECK_EQUAL(tree.leaves(), 8); + BOOST_CHECK_EQUAL(tree.row_count(), 4); +} + +BOOST_AUTO_TEST_CASE(merkletree_construct_test_2) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}}; + merkle_tree, 3> tree_res = make_merkle_tree, 3>(v.begin(), v.end()); + merkle_tree, 3> tree(tree_res.begin(), tree_res.end()); + BOOST_CHECK_EQUAL(tree.size(), 13); + BOOST_CHECK_EQUAL(tree.leaves(), 9); + BOOST_CHECK_EQUAL(tree.row_count(), 3); +} + + +BOOST_AUTO_TEST_CASE(merkletree_validate_test_1) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}}; + testing_validate_template, 2>(v); + testing_validate_template(v); + testing_validate_template, 2>(v); + + BOOST_STATIC_ASSERT_MSG(algebra::is_field_element::value, "Expecting Poseidon to consume field elements"); + std::vector> v_field = { + {0x0_cppui_modular255}, + {0x1_cppui_modular255}, + {0x2_cppui_modular255}, + {0x3_cppui_modular255}, + {0x4_cppui_modular255}, + {0x5_cppui_modular255}, + {0x6_cppui_modular255}, + {0x7_cppui_modular255} + }; + testing_validate_template(v_field); + testing_validate_template(v_field); + // When you have bytes input, use wrapper to lazy convert it to field elements: + std::vector< + nil::crypto3::hashes::block_to_field_elements_wrapper< + typename poseidon_type::word_type::field_type, + std::array + > + > wrappers; + for (const auto& inner_containers : v) { + wrappers.emplace_back(inner_containers); + } + testing_validate_template(wrappers); + testing_validate_template(wrappers); + + std::size_t leaf_number = 8; + testing_validate_template_random_data, 2, std::uint8_t, 1>(leaf_number); + testing_validate_template_random_data(leaf_number); + testing_validate_template_random_data, 2, std::uint8_t, 1>(leaf_number); + testing_validate_template_random_data(leaf_number); + testing_validate_template_random_data(leaf_number); +} + +BOOST_AUTO_TEST_CASE(merkletree_validate_test_2) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}}; + testing_validate_template, 3>(v); + testing_validate_template(v); + testing_validate_template, 3>(v); + + std::size_t leaf_number = 9; + testing_validate_template_random_data, 3, std::uint8_t, 1>(leaf_number); + testing_validate_template_random_data(leaf_number); + testing_validate_template_random_data, 3, std::uint8_t, 1>(leaf_number); +} + +BOOST_AUTO_TEST_CASE(merkletree_validate_test_3) { + using hash_type = hashes::pedersen< + hashes::find_group_hash_default_params, hashes::sha2<256>, + algebra::curves::jubjub::template g1_type>; + std::size_t leaf_number = 8; + testing_validate_template_random_data(leaf_number); +} + +BOOST_AUTO_TEST_CASE(merkletree_validate_test_4) { + using hash_type = hashes::pedersen< + hashes::find_group_hash_default_params, hashes::sha2<256>, + algebra::curves::jubjub::template g1_type>; + testing_validate_template_random_data_compressed_proofs(8); + testing_validate_template_random_data_compressed_proofs(9); + testing_validate_template_random_data_compressed_proofs(16); +} + +BOOST_AUTO_TEST_CASE(merkletree_validate_test_5) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}}; + testing_validate_template_compressed_proofs, 3>(v); + testing_validate_template_compressed_proofs(v); + testing_validate_template_compressed_proofs, 3>(v); + + std::size_t leaf_number = 16; + testing_validate_template_random_data_compressed_proofs, 4, std::uint8_t, 1>(leaf_number); + testing_validate_template_random_data_compressed_proofs(leaf_number); + testing_validate_template_random_data_compressed_proofs, 4, std::uint8_t, 1>(leaf_number); +} + +BOOST_AUTO_TEST_CASE(merkletree_hash_test_1) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}}; + testing_hash_template, 2>(v, "3b828c4f4b48c5d4cb5562a474ec9e2fd8d5546fae40e90732ef635892e42720"); + testing_hash_template(v, "11ee8b50825ce6f816a1ae06d4aa0045"); + testing_hash_template, 2>(v, "0ed2a2145cae554ca57f08420d6cb58629ca1e89dc92f819c6c1d13d"); + testing_hash_template, 2>(v, "568ff5eb286f51b8a3e8de4e53aa8daed44594a246deebbde119ea2eb27acd6b"); + testing_hash_template, 2>(v, "1a0ca31dd9e0b27afdf77021dc50023cdd814eb53ede16e8c5c322a0bcb6bd7d26a0404e5af53971e1566c1649bb9686905cdedfa9a358023065e423522d4372"); + + std::vector v_64 = { + "0123456789012345678901234567890123456789012345678901234567890123", + "0123456789012345678901234567890123456789012345678901234567890123", + "0123456789012345678901234567890123456789012345678901234567890123", + "0123456789012345678901234567890123456789012345678901234567890123" + }; + std::vector< + nil::crypto3::hashes::block_to_field_elements_wrapper< + typename poseidon_type::word_type::field_type, + std::string, + /*OverflowOnPurpose=*/ true + > + > wrappers; + for (const auto& inner_containers : v_64) { + wrappers.emplace_back(inner_containers); + } + merkle_tree tree = make_merkle_tree(wrappers.begin(), wrappers.end()); + BOOST_CHECK(tree.root() == 0x6E7641F1EAE17C0DA8227840EFEA6E1D17FB5EBA600D9DC34F314D5400E5BF3_cppui_modular255); +} + +BOOST_AUTO_TEST_CASE(merkletree_hash_test_2) { + std::vector> v = {{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}}; + testing_hash_template, 3>(v, "6831d4d32538bedaa7a51970ac10474d5884701c840781f0a434e5b6868d4b73"); + testing_hash_template(v, "0733c4cd580b1523cfbb9751f42e9420"); + testing_hash_template, 3>(v, "d9d0ff26d10aaac2882c08eb2b55e78690c949d1a73b1cfc0eb322ee"); +} + +BOOST_AUTO_TEST_SUITE_END()