From 3b6ebc1535ec69d46f546938d70cd26e0b01d1ad Mon Sep 17 00:00:00 2001 From: Iluvmagick Date: Tue, 6 Feb 2024 22:16:28 +0400 Subject: [PATCH] Fixed hash decomposition component. --- .../hashes/sha2/plonk/decomposition.hpp | 192 +++++++++++------- .../components/hashes/sha2/plonk/sha256.hpp | 24 --- .../hashes/sha2/plonk/sha256_process.hpp | 51 ----- .../blueprint/utils/satisfiability_check.hpp | 2 +- test/hashes/plonk/decomposition.cpp | 79 +++---- test/hashes/plonk/sha256_process.cpp | 4 +- 6 files changed, 167 insertions(+), 185 deletions(-) diff --git a/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp b/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp index 4d285a5b6..729110815 100644 --- a/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp +++ b/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp @@ -3,6 +3,7 @@ // Copyright (c) 2021 Nikita Kaskov // Copyright (c) 2022 Alisa Cherniaeva // Copyright (c) 2022 Ekaterina Chukavina +// Copyright (c) 2023 Dmitrii Tabalin // // MIT License // @@ -80,7 +81,7 @@ namespace nil { constexpr static std::size_t get_rows_amount(std::size_t witness_amount, std::size_t lookup_column_amount) { - return 3; + return 4; } constexpr static std::size_t get_empty_rows_amount() { return 1; @@ -88,7 +89,7 @@ namespace nil { const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); const std::size_t empty_rows_amount = get_empty_rows_amount(); - constexpr static const std::size_t gates_amount = 1; + constexpr static const std::size_t gates_amount = 2; struct input_type { std::array data; @@ -102,14 +103,14 @@ namespace nil { std::array output; result_type(const decomposition &component, std::uint32_t start_row_index) { - output = {var(component.W(0), start_row_index + 1, false), - var(component.W(1), start_row_index + 1, false), - var(component.W(2), start_row_index + 1, false), - var(component.W(3), start_row_index + 1, false), - var(component.W(4), start_row_index + 1, false), + output = {var(component.W(6), start_row_index + 1, false), var(component.W(5), start_row_index + 1, false), - var(component.W(6), start_row_index + 1, false), - var(component.W(7), start_row_index + 1, false)}; + var(component.W(4), start_row_index + 1, false), + var(component.W(3), start_row_index + 1, false), + var(component.W(6), start_row_index + 3, false), + var(component.W(5), start_row_index + 3, false), + var(component.W(4), start_row_index + 3, false), + var(component.W(3), start_row_index + 3, false)}; } result_type(const decomposition &component, std::uint32_t start_row_index, bool skip) { @@ -172,6 +173,13 @@ namespace nil { } return output; } + + std::map component_lookup_tables() const { + std::map lookup_tables; + lookup_tables["sha256_sparse_base4/first_column"] = 0; // REQUIRED_TABLE + + return lookup_tables; + } }; template @@ -188,34 +196,55 @@ namespace nil { const typename plonk_native_decomposition::input_type instance_input, const std::uint32_t start_row_index) { + using integral_type = typename BlueprintFieldType::integral_type; - std::size_t row = start_row_index; - std::array data = { - typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.data[0]).data), - typename BlueprintFieldType::integral_type(var_value(assignment, instance_input.data[1]).data)}; - std::array range_chunks; + std::array data = { + integral_type(var_value(assignment, instance_input.data[0]).data), + integral_type(var_value(assignment, instance_input.data[1]).data)}; + std::array, 4>, 2> range_chunks; + std::array, 2> output_chunks; std::size_t shift = 0; - for (std::size_t i = 0; i < 8; i++) { - range_chunks[i] = (data[0] >> shift) & ((65536) - 1); - assignment.witness(component.W(i), row) = range_chunks[i]; - range_chunks[i + 8] = (data[1] >> shift) & ((65536) - 1); - assignment.witness(component.W(i), row + 2) = range_chunks[i + 8]; - shift += 16; + for (std::size_t data_idx = 0; data_idx < 2; data_idx++) { + for (std::size_t chunk_idx = 0; chunk_idx < 4; chunk_idx++) { + output_chunks[data_idx][chunk_idx] = (data[data_idx] >> (chunk_idx * 32)) & 0xFFFFFFFF; + // subchunks are 14, 14, and 4 bits long respectively + range_chunks[data_idx][chunk_idx][0] = + (output_chunks[data_idx][chunk_idx] & 0b11111111111111000000000000000000) >> 18; + range_chunks[data_idx][chunk_idx][1] = + (output_chunks[data_idx][chunk_idx] & 0b00000000000000111111111111110000) >> 4; + range_chunks[data_idx][chunk_idx][2] = + (output_chunks[data_idx][chunk_idx] & 0b00000000000000000000000000001111); + BOOST_ASSERT( + output_chunks[data_idx][chunk_idx] == + range_chunks[data_idx][chunk_idx][0] * (1 << 18) + + range_chunks[data_idx][chunk_idx][1] * (1 << 4) + + range_chunks[data_idx][chunk_idx][2]); + } + } + for (std::size_t data_idx = 0; data_idx < 2; data_idx++) { + const std::size_t first_row = start_row_index + 2 * data_idx, + second_row = start_row_index + 2 * data_idx + 1; + // placing subchunks for first three chunks + for (std::size_t chunk_idx = 0; chunk_idx < 3; chunk_idx++) { + for (std::size_t subchunk_idx = 0; subchunk_idx < 3; subchunk_idx++) { + assignment.witness(component.W(3 * chunk_idx + subchunk_idx), first_row) = + range_chunks[data_idx][chunk_idx][subchunk_idx]; + } + } + // placing subchunk for the last chunk + for (std::size_t subchunk_idx = 0; subchunk_idx < 3; subchunk_idx++) { + assignment.witness(component.W(subchunk_idx), second_row) = + range_chunks[data_idx][3][subchunk_idx]; + } + // placing chunks + for (std::size_t chunk_idx = 0; chunk_idx < 4; chunk_idx++) { + assignment.witness(component.W(3 + chunk_idx), second_row) = + output_chunks[data_idx][chunk_idx]; + } + // placing the original data + assignment.witness(component.W(7), second_row) = data[data_idx]; } - - assignment.witness(component.W(8), row) = data[0]; - assignment.witness(component.W(8), row + 2) = data[1]; - - assignment.witness(component.W(3), row + 1) = range_chunks[1] * (65536) + range_chunks[0]; - assignment.witness(component.W(2), row + 1) = range_chunks[3] * (65536) + range_chunks[2]; - assignment.witness(component.W(1), row + 1) = range_chunks[5] * (65536) + range_chunks[4]; - assignment.witness(component.W(0), row + 1) = range_chunks[7] * (65536) + range_chunks[6]; - - assignment.witness(component.W(7), row + 1) = range_chunks[9] * (65536) + range_chunks[8]; - assignment.witness(component.W(6), row + 1) = range_chunks[11] * (65536) + range_chunks[10]; - assignment.witness(component.W(5), row + 1) = range_chunks[13] * (65536) + range_chunks[12]; - assignment.witness(component.W(4), row + 1) = range_chunks[15] * (65536) + range_chunks[14]; return typename plonk_native_decomposition::result_type( component, start_row_index); @@ -256,33 +285,58 @@ namespace nil { using var = typename plonk_native_decomposition::var; - auto constraint_1 = - var(component.W(8), -1) - (var(component.W(3), 0) + var(component.W(2), 0) * 0x100000000_cppui255 + - var(component.W(1), 0) * 0x10000000000000000_cppui255 + - var(component.W(0), 0) * 0x1000000000000000000000000_cppui255); - auto constraint_2 = - var(component.W(8), 1) - (var(component.W(7), 0) + var(component.W(6), 0) * 0x100000000_cppui255 + - var(component.W(5), 0) * 0x10000000000000000_cppui255 + - var(component.W(4), 0) * 0x1000000000000000000000000_cppui255); - auto constraint_3 = var(component.W(3), 0) - - (var(component.W(0), -1) + var(component.W(1), -1) * (65536)); - auto constraint_4 = var(component.W(2), 0) - - (var(component.W(2), -1) + var(component.W(3), -1) * (65536)); - auto constraint_5 = var(component.W(1), 0) - - (var(component.W(4), -1) + var(component.W(5), -1) * (65536)); - auto constraint_6 = var(component.W(0), 0) - - (var(component.W(6), -1) + var(component.W(7), -1) * (65536)); - auto constraint_7 = var(component.W(7), 0) - - (var(component.W(0), +1) + var(component.W(1), +1) * (65536)); - auto constraint_8 = var(component.W(6), 0) - - (var(component.W(2), +1) + var(component.W(3), +1) * (65536)); - auto constraint_9 = var(component.W(5), 0) - - (var(component.W(4), +1) + var(component.W(5), +1) * (65536)); - auto constraint_10 = var(component.W(4), 0) - - (var(component.W(6), +1) + var(component.W(7), +1) * (65536)); - return bp.add_gate( - {constraint_1, constraint_2, constraint_3, constraint_4, constraint_5, constraint_6, - constraint_7, constraint_8, constraint_9, constraint_10}); + const typename BlueprintFieldType::integral_type one = 1; + std::array selectors; + + std::vector subchunk_lookup_constraints(12); + // lookup constraints for the first three chunks + for (std::size_t chunk_idx = 0; chunk_idx < 3; chunk_idx++) { + subchunk_lookup_constraints[3 * chunk_idx] = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(3 * chunk_idx), -1)}}; + subchunk_lookup_constraints[3 * chunk_idx + 1] = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(3 * chunk_idx + 1), -1)}}; + subchunk_lookup_constraints[3 * chunk_idx + 2] = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {1024 * var(component.W(3 * chunk_idx + 2), -1)}}; + } + // lookup constraints for the last chunk + subchunk_lookup_constraints[9] = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(0), 0)}}; + subchunk_lookup_constraints[10] = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {var(component.W(1), 0)}}; + subchunk_lookup_constraints[11] = + {lookup_tables_indices.at("sha256_sparse_base4/first_column"), + {1024 * var(component.W(2), 0)}}; + + selectors[0] = bp.add_lookup_gate(subchunk_lookup_constraints); + + std::vector chunk_constraints(5); + // chunk sum constraints for the first three chunks + for (std::size_t chunk_idx = 0; chunk_idx < 3; chunk_idx++) { + chunk_constraints[chunk_idx] = + var(component.W(3 * chunk_idx), -1) * (1 << 18) + + var(component.W(3 * chunk_idx + 1), -1) * (1 << 4) + + var(component.W(3 * chunk_idx + 2), -1) - + var(component.W(3 + chunk_idx), 0); + } + // chunk sum constraints for the last chunk + chunk_constraints[3] = + var(component.W(0), 0) * (1 << 18) + + var(component.W(1), 0) * (1 << 4) + + var(component.W(2), 0) - + var(component.W(6), 0); + // chunk sum constraint for input + chunk_constraints[4] = + var(component.W(3), 0) + var(component.W(4), 0) * (one << 32) + + var(component.W(5), 0) * (one << 64) + var(component.W(6), 0) * (one << 96) - + var(component.W(7), 0); + selectors[1] = bp.add_gate(chunk_constraints); + + return selectors; } template @@ -296,11 +350,9 @@ namespace nil { const std::size_t start_row_index) { using var = typename plonk_native_decomposition::var; - // CRITICAL: these copy constraints might not be sufficient, but are definitely required. - // I've added copy constraints for the inputs, but internal ones might be missing - // Proceed with care - bp.add_copy_constraint({instance_input.data[0], var(component.W(8), start_row_index, false)}); - bp.add_copy_constraint({instance_input.data[1], var(component.W(8), start_row_index + 2, false)}); + + bp.add_copy_constraint({instance_input.data[0], var(component.W(7), start_row_index + 1, false)}); + bp.add_copy_constraint({instance_input.data[1], var(component.W(7), start_row_index + 3, false)}); } template @@ -314,10 +366,14 @@ namespace nil { &instance_input, const std::size_t start_row_index) { - std::size_t j = start_row_index + 1; - std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + std::array selector_indices = + generate_gates(component, bp, assignment, instance_input, bp.get_reserved_indices()); + + assignment.enable_selector(selector_indices[0], start_row_index + 1); + assignment.enable_selector(selector_indices[0], start_row_index + 3); + assignment.enable_selector(selector_indices[1], start_row_index + 1); + assignment.enable_selector(selector_indices[1], start_row_index + 3); - assignment.enable_selector(selector_index, j); generate_copy_constraints(component, bp, assignment, instance_input, start_row_index); return typename plonk_native_decomposition::result_type( diff --git a/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp b/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp index c22c91497..fcba78e10 100644 --- a/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp +++ b/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp @@ -149,30 +149,6 @@ namespace nil { using lookup_table_definition = typename nil::crypto3::zk::snark::lookup_table_definition; - std::vector> component_custom_lookup_tables(){ - std::vector> result = {}; - - auto sparse_values_base4 = std::shared_ptr(new typename sha256_process_type::sparse_values_base4_table()); - result.push_back(sparse_values_base4); - - auto sparse_values_base7 = std::shared_ptr(new typename sha256_process_type::sparse_values_base7_table()); - result.push_back(sparse_values_base7); - - auto maj = std::shared_ptr(new typename sha256_process_type::maj_function_table()); - result.push_back(maj); - - auto reverse_sparse_sigmas_base4 = std::shared_ptr(new typename sha256_process_type::reverse_sparse_sigmas_base4_table()); - result.push_back(reverse_sparse_sigmas_base4); - - auto reverse_sparse_sigmas_base7 = std::shared_ptr(new typename sha256_process_type::reverse_sparse_sigmas_base7_table()); - result.push_back(reverse_sparse_sigmas_base7); - - auto ch = std::shared_ptr(new typename sha256_process_type::ch_function_table()); - result.push_back(ch); - - return result; - } - std::map component_lookup_tables(){ std::map lookup_tables; lookup_tables["sha256_sparse_base4/full"] = 0; // REQUIRED_TABLE diff --git a/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp b/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp index 56dc7f8b7..a988e4cff 100644 --- a/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp +++ b/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp @@ -1804,57 +1804,6 @@ namespace nil { assignment.witness(component.W(6), row + i) = ( ((1 << 14) - 1) & (integral_a2 >> 28) ); assignment.witness(component.W(7), row + i) = integral_a2; } - /*std::vector value_sizes = {14}; - // lookup table for sparse values with base = 4 - for (typename CurveType::scalar_field_type::integral_type i = 0; - i < typename CurveType::scalar_field_type::integral_type(16384); - i++) { - std::vector value(14); - for (std::size_t j = 0; j < 14; j++) { - value[14 - j - 1] = multiprecision::bit_test(i, j); - } - std::array, 2> value_chunks = - detail::split_and_sparse(value, value_sizes, - plonk_sha256_process::base4); - assignment.constant(0)[start_row_index + std::size_t(i)] = value_chunks[0][0]; - assignment.constant(1)[start_row_index + std::size_t(i)] = value_chunks[1][0]; - } - // lookup table for sparse values with base = 7 - for (typename CurveType::scalar_field_type::integral_type i = 0; - i < typename CurveType::scalar_field_type::integral_type(16384); - i++) { - std::vector value(14); - for (std::size_t j = 0; j < 14; j++) { - value[14 - j - 1] = multiprecision::bit_test(i, j); - } - std::array, 2> value_chunks = - detail::split_and_sparse(value, value_sizes, - plonk_sha256_process::base7); - assignment.constant(2)[start_row_index + std::size_t(i)] = value_chunks[0][0]; - assignment.constant(3)[start_row_index + std::size_t(i)] = value_chunks[1][0]; - } - // lookup table for maj function - value_sizes = {8}; - for (typename CurveType::scalar_field_type::integral_type i = 0; - i < typename CurveType::scalar_field_type::integral_type(65535); - i++) { - static std::array, 2> - value = detail::reversed_sparse_and_split(i, value_sizes, - plonk_sha256_process::base4); - assignment.constant(4)[start_row_index + std::size_t(i)] = value[0][0]; - assignment.constant(5)[start_row_index + std::size_t(i)] = i; - } - - // lookup table for ch function - for (typename CurveType::scalar_field_type::integral_type i = 0; - i < typename CurveType::scalar_field_type::integral_type(5765041); - i++) { - static std::array, 2> - value = detail::reversed_sparse_and_split(i, value_sizes, - plonk_sha256_process::base7); - assignment.constant(4)[start_row_index + std::size_t(i)] = value[0][0]; - assignment.constant(5)[start_row_index + std::size_t(i)] = i; - }*/ return typename plonk_sha256_process::result_type( component, start_row_index); diff --git a/include/nil/blueprint/utils/satisfiability_check.hpp b/include/nil/blueprint/utils/satisfiability_check.hpp index e89653529..79543f57b 100644 --- a/include/nil/blueprint/utils/satisfiability_check.hpp +++ b/include/nil/blueprint/utils/satisfiability_check.hpp @@ -195,4 +195,4 @@ namespace nil { } // namespace blueprint } // namespace nil -#endif // CRYPTO3_BLUEPRINT_UTILS_PLONK_SATISFIABILITY_CHECK_HPP +#endif // CRYPTO3_BLUEPRINT_UTILS_PLONK_SATISFIABILITY_CHECK_HPP \ No newline at end of file diff --git a/test/hashes/plonk/decomposition.cpp b/test/hashes/plonk/decomposition.cpp index 6115c6f62..50d6c971c 100644 --- a/test/hashes/plonk/decomposition.cpp +++ b/test/hashes/plonk/decomposition.cpp @@ -49,8 +49,8 @@ void test_decomposition(std::vector pub constexpr std::size_t WitnessColumns = 9; constexpr std::size_t PublicInputColumns = 1; - constexpr std::size_t ConstantColumns = 0; - constexpr std::size_t SelectorColumns = 1; + constexpr std::size_t ConstantColumns = 8; + constexpr std::size_t SelectorColumns = 3; using hash_type = crypto3::hashes::keccak_1600<256>; constexpr std::size_t Lambda = 40; @@ -75,6 +75,12 @@ void test_decomposition(std::vector pub auto result_check = [&expected_res](AssignmentType &assignment, typename component_type::result_type &real_res) { + for (std::size_t i = 0; i < real_res.output.size(); i++){ + std::cout << var_value(assignment, real_res.output[i]).data << std::endl; + } + for (std::size_t i = 0; i < expected_res.size(); i++){ + std::cout << expected_res[i].data << std::endl; + } for (std::size_t i = 0; i < real_res.output.size(); i++){ assert(expected_res[i] == var_value(assignment, real_res.output[i])); } @@ -85,55 +91,50 @@ void test_decomposition(std::vector pub component_type component_instance({0, 1, 2, 3, 4, 5, 6, 7, 8},{},{}); if (expected_to_pass) { - crypto3::test_component( - component_instance, desc, public_input, result_check, instance_input); - crypto3::test_empty_component( - component_instance, desc, public_input, result_check, instance_input); + crypto3::test_component( + component_instance, desc, public_input, result_check, instance_input, + nil::crypto3::detail::connectedness_check_type::WEAK); } else { - crypto3::test_component_to_fail( - component_instance, desc, public_input, result_check_to_fail, instance_input); + crypto3::test_component_to_fail( + component_instance, desc, public_input, result_check_to_fail, instance_input, + nil::crypto3::detail::connectedness_check_type::WEAK); } } BOOST_AUTO_TEST_SUITE(blueprint_plonk_test_suite) template -std::vector calculate_decomposition(std::vector data_value) { - std::array data = { - typename FieldType::integral_type(data_value[0].data), - typename FieldType::integral_type(data_value[1].data)}; - std::array range_chunks; - std::size_t shift = 0; - - for (std::size_t i = 0; i < 8; i++) { - range_chunks[i] = (data[0] >> shift) & ((1 << 16) - 1); - range_chunks[i + 8] = (data[1] >> shift) & ((1 << 16) - 1); - shift += 16; - } - - std::array output; - - output[0] = range_chunks[7] * (1 << 16) + range_chunks[6]; - output[1] = range_chunks[5] * (1 << 16) + range_chunks[4]; - output[2] = range_chunks[3] * (1 << 16) + range_chunks[2]; - output[3] = range_chunks[1] * (1 << 16) + range_chunks[0]; - output[4] = range_chunks[15] * (1 << 16) + range_chunks[14]; - output[5] = range_chunks[13] * (1 << 16) + range_chunks[12]; - output[6] = range_chunks[11] * (1 << 16) + range_chunks[10]; - output[7] = range_chunks[9] * (1 << 16) + range_chunks[8]; - - std::vector output_value; - - for (std::size_t i = 0; i < output.size(); i++){ - output_value.push_back(typename FieldType::value_type(output[i])); - } - - return output_value; +std::vector calculate_decomposition( + const std::vector &data_value) { + + std::array data = { + typename FieldType::integral_type(data_value[0].data), + typename FieldType::integral_type(data_value[1].data)}; + std::size_t shift = 0; + std::array output; + const typename FieldType::integral_type one = 1; + + for (std::size_t i = 0; i < 4; i++, shift += 32) { + output[i + 4] = (data[0] >> shift) & ((one << 32) - 1); + output[i] = (data[1] >> shift) & ((one << 32) - 1); + } + + std::vector output_value(output.size()); + + for (std::size_t i = 0; i < output.size(); i++){ + output_value[output.size() - 1 - i] = typename FieldType::value_type(output[i]); + } + return output_value; } BOOST_AUTO_TEST_CASE(blueprint_plonk_decomposition_test0) { using field_type = typename crypto3::algebra::curves::pallas::base_field_type; + test_decomposition( + {0x1_cppui255, 0x2_cppui255}, + calculate_decomposition({0x1_cppui255, 0x2_cppui255}), + true); + test_decomposition( {0x8d741211e928fdd4d33a13970d0ce7f3_cppui255, 0x92f209334030f9ec8fa8a025e987a5dd_cppui255}, calculate_decomposition({0x8d741211e928fdd4d33a13970d0ce7f3_cppui255, 0x92f209334030f9ec8fa8a025e987a5dd_cppui255}), diff --git a/test/hashes/plonk/sha256_process.cpp b/test/hashes/plonk/sha256_process.cpp index 1a10cbe50..a84c92050 100644 --- a/test/hashes/plonk/sha256_process.cpp +++ b/test/hashes/plonk/sha256_process.cpp @@ -50,8 +50,8 @@ BOOST_AUTO_TEST_CASE(blueprint_plonk_sha256_process) { using BlueprintFieldType = typename curve_type::base_field_type; constexpr std::size_t WitnessColumns = 9; constexpr std::size_t PublicInputColumns = 1; - constexpr std::size_t ConstantColumns = 33; - constexpr std::size_t SelectorColumns = 50; + constexpr std::size_t ConstantColumns = 20; + constexpr std::size_t SelectorColumns = 30; using hash_type = nil::crypto3::hashes::keccak_1600<256>; constexpr std::size_t Lambda = 1;