diff --git a/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp b/include/nil/blueprint/components/hashes/sha2/plonk/decomposition.hpp index a696afbb7..c74e86370 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,11 +81,11 @@ 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; } const std::size_t rows_amount = get_rows_amount(this->witness_amount(), 0); - constexpr static const std::size_t gates_amount = 1; + constexpr static const std::size_t gates_amount = 2; struct input_type { std::array data; @@ -98,14 +99,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)}; } std::vector all_vars() const { @@ -130,6 +131,13 @@ namespace nil { std::initializer_list public_inputs) : component_type(witnesses, constants, public_inputs, get_manifest()) {}; + + std::map component_lookup_tables(){ + std::map lookup_tables; + lookup_tables["sha256_sparse_base4/first_column"] = 0; // REQUIRED_TABLE + + return lookup_tables; + } }; template @@ -146,77 +154,126 @@ 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); } template - std::size_t generate_gates( + std::array generate_gates( const plonk_native_decomposition &component, circuit> &bp, assignment> &assignment, const typename plonk_native_decomposition::input_type - &instance_input) { + &instance_input, + const typename lookup_library::left_reserved_type &lookup_tables_indices) { 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}); + using lookup_constraint = crypto3::zk::snark::plonk_lookup_constraint; + using constraint = crypto3::zk::snark::plonk_constraint; + + 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 @@ -230,11 +287,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 @@ -248,10 +303,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 4951539d1..43f86f5b7 100644 --- a/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp +++ b/include/nil/blueprint/components/hashes/sha2/plonk/sha256.hpp @@ -139,30 +139,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 a2a8f15ec..90770d5ce 100644 --- a/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp +++ b/include/nil/blueprint/components/hashes/sha2/plonk/sha256_process.hpp @@ -1529,57 +1529,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 25a72d329..b433c8f9c 100644 --- a/include/nil/blueprint/utils/satisfiability_check.hpp +++ b/include/nil/blueprint/utils/satisfiability_check.hpp @@ -36,6 +36,7 @@ #include #include #include +#include namespace nil { namespace blueprint { @@ -43,7 +44,7 @@ namespace nil { template bool is_satisfied(const circuit> &bp, + ArithmetizationParams>> &bp, const assignment> &assignments){ @@ -125,14 +126,14 @@ namespace nil { } } if (!found) { + std::cout << "Constraint " << j << " from lookup gate " << i + << " from table " << table_name << " on row " << selector_row + << " is not satisfied." << std::endl; std::cout << "Input values:"; for(std::size_t k = 0; k < input_values.size(); k++){ std::cout << input_values[k] << " "; } - std::cout << std::endl; - std::cout << "Constraint " << j << " from lookup gate " << i << " from table " << table_name << " on row " << selector_row - << " is not satisfied." << std::endl; - std::cout << "Offending Lookup Gate: " << std::endl; + std::cout << std::endl << "Offending Lookup Gate: " << std::endl; for (const auto &constraint : lookup_gates[i].constraints) { std::cout << "Table id: " << constraint.table_id << std::endl; for (auto &lookup_input : constraint.lookup_input) { diff --git a/test/hashes/plonk/decomposition.cpp b/test/hashes/plonk/decomposition.cpp index 85bead3a0..30e5c0984 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; @@ -69,6 +69,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])); } @@ -80,52 +86,49 @@ void test_decomposition(std::vector pub if (expected_to_pass) { crypto3::test_component( - component_instance, public_input, result_check, instance_input); + component_instance, public_input, result_check, instance_input, + nil::crypto3::detail::connectedness_check_type::WEAK); } else { crypto3::test_component_to_fail( - component_instance, public_input, result_check_to_fail, instance_input); + component_instance, 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[1] * (1 << 16) + range_chunks[0]; - output[1] = range_chunks[3] * (1 << 16) + range_chunks[2]; - output[2] = range_chunks[5] * (1 << 16) + range_chunks[4]; - output[3] = range_chunks[7] * (1 << 16) + range_chunks[6]; - output[4] = range_chunks[9] * (1 << 16) + range_chunks[8]; - output[5] = range_chunks[11] * (1 << 16) + range_chunks[10]; - output[6] = range_chunks[13] * (1 << 16) + range_chunks[12]; - output[7] = range_chunks[15] * (1 << 16) + range_chunks[14]; - - 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 ed65db25b..c4e4a55c9 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;