diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index 018021e33f9..85fbaf19f38 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -63,6 +63,7 @@ endif() add_subdirectory(barretenberg/client_ivc) add_subdirectory(barretenberg/bb) +add_subdirectory(barretenberg/boomerang_value_detection) add_subdirectory(barretenberg/circuit_checker) add_subdirectory(barretenberg/commitment_schemes) add_subdirectory(barretenberg/commitment_schemes_recursion) diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/CMakeLists.txt new file mode 100644 index 00000000000..dae39eee2de --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/CMakeLists.txt @@ -0,0 +1 @@ +barretenberg_module(boomerang_value_detection stdlib_circuit_builders circuit_checker stdlib_primitives numeric stdlib_aes128 stdlib_sha256 stdlib_blake2s stdlib_blake3s) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp new file mode 100644 index 00000000000..1fe02660f08 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.cpp @@ -0,0 +1,824 @@ +#include "graph.hpp" +#include +#include + +using namespace bb::plookup; +using namespace bb; + +/** + * @brief this method removes duplicate variables from a gate, + * converts variables from a gate to real variables, and then + * updates variable gates count for real variable indexes + */ + +template +inline void Graph_::process_gate_variables(UltraCircuitBuilder& ultra_circuit_builder, + std::vector& gate_variables) +{ + auto unique_variables = std::unique(gate_variables.begin(), gate_variables.end()); + gate_variables.erase(unique_variables, gate_variables.end()); + if (gate_variables.empty()) { + return; + } + for (size_t i = 0; i < gate_variables.size(); i++) { + gate_variables[i] = this->to_real(ultra_circuit_builder, gate_variables[i]); + } + for (const auto& variable_index : gate_variables) { + variables_gate_counts[variable_index] += 1; + } +} + +/** + * @brief this method implements connected components from arithmetic gates + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_arithmetic_gate_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + auto& arithmetic_block = ultra_circuit_builder.blocks.arithmetic; + uint32_t left_idx = arithmetic_block.w_l()[index]; + uint32_t right_idx = arithmetic_block.w_r()[index]; + uint32_t out_idx = arithmetic_block.w_o()[index]; + uint32_t fourth_idx = arithmetic_block.w_4()[index]; + auto q_m = arithmetic_block.q_m()[index]; + auto q_1 = arithmetic_block.q_1()[index]; + auto q_2 = arithmetic_block.q_2()[index]; + auto q_3 = arithmetic_block.q_3()[index]; + auto q_4 = arithmetic_block.q_4()[index]; + std::vector gate_variables = {}; + if (q_m != 0 || q_1 != 1 || q_2 != 0 || q_3 != 0 || q_4 != 0) { + // this is not the gate for fix_witness, so we have to process this gate + if (arithmetic_block.q_arith()[index] > 0) { + if (q_m != 0) { + gate_variables.emplace_back(left_idx); + gate_variables.emplace_back(right_idx); + } + if (q_1 != 0) { + gate_variables.emplace_back(left_idx); + } + if (q_2 != 0) { + gate_variables.emplace_back(right_idx); + } + if (q_3 != 0) { + gate_variables.emplace_back(out_idx); + } + if (q_4 != 0) { + gate_variables.emplace_back(fourth_idx); + } + if (arithmetic_block.q_arith()[index] == 2) { + // We have to use w_4_shift from the next gate + // if and only if the current gate isn't last, cause we can't + // look into the next gate + if (index != arithmetic_block.size() - 1) { + uint32_t fourth_shift_idx = arithmetic_block.w_4()[index + 1]; + gate_variables.emplace_back(fourth_shift_idx); + } + } + if (arithmetic_block.q_arith()[index] == 3) { + // TODO(daniel): want to process this case later + ASSERT(false); + } + } + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief this method creates connected components from elliptic gates + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_elliptic_gate_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + auto& elliptic_block = ultra_circuit_builder.blocks.elliptic; + std::vector gate_variables = {}; + bool is_elliptic_gate = elliptic_block.q_elliptic()[index] == 1; + bool is_elliptic_add_gate = elliptic_block.q_1()[index] != 0 && elliptic_block.q_m()[index] == 0; + bool is_elliptic_dbl_gate = elliptic_block.q_1()[index] == 0 && elliptic_block.q_m()[index] == 1; + if (is_elliptic_gate) { + auto right_idx = elliptic_block.w_r()[index]; + auto out_idx = elliptic_block.w_o()[index]; + gate_variables.emplace_back(right_idx); + gate_variables.emplace_back(out_idx); + if (index != elliptic_block.size() - 1) { + if (is_elliptic_add_gate) { + // if this gate is ecc_add_gate, we have to get indices x2, x3, y3, y2 from the next gate + gate_variables.emplace_back(elliptic_block.w_l()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_r()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_o()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_4()[index + 1]); + } + if (is_elliptic_dbl_gate) { + // if this gate is ecc_dbl_gate, we have to indices x3, y3 from right and output wires + gate_variables.emplace_back(elliptic_block.w_r()[index + 1]); + gate_variables.emplace_back(elliptic_block.w_o()[index + 1]); + } + } + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief this method creates connected components from sorted constraints + * + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_sort_constraint_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + auto& delta_range_block = ultra_circuit_builder.blocks.delta_range; + std::vector gate_variables = {}; + if (delta_range_block.q_delta_range()[index] == 1) { + auto left_idx = delta_range_block.w_l()[index]; + auto right_idx = delta_range_block.w_r()[index]; + auto out_idx = delta_range_block.w_o()[index]; + auto fourth_idx = delta_range_block.w_4()[index]; + gate_variables.insert(gate_variables.end(), { left_idx, right_idx, out_idx, fourth_idx }); + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief this method creates connected components from plookup gates + * + * @tparam FF + * @param ultra_circuit_builder + * @param index + * @return std::vector + */ + +template +inline std::vector Graph_::get_plookup_gate_connected_component( + bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index) +{ + std::vector gate_variables; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + auto q_2 = lookup_block.q_2()[index]; + auto q_m = lookup_block.q_m()[index]; + auto q_c = lookup_block.q_c()[index]; + auto left_idx = lookup_block.w_l()[index]; + auto right_idx = lookup_block.w_r()[index]; + auto out_idx = lookup_block.w_o()[index]; + gate_variables.emplace_back(left_idx); + gate_variables.emplace_back(right_idx); + gate_variables.emplace_back(out_idx); + if (index < lookup_block.size() - 1) { + if (q_2 != 0 || q_m != 0 || q_c != 0) { + if (q_2 != 0) { + gate_variables.emplace_back(lookup_block.w_l()[index + 1]); + } + if (q_m != 0) { + gate_variables.emplace_back(lookup_block.w_r()[index + 1]); + } + if (q_c != 0) { + gate_variables.emplace_back(lookup_block.w_o()[index + 1]); + } + } + } + this->process_gate_variables(ultra_circuit_builder, gate_variables); + return gate_variables; +} + +/** + * @brief Construct a new Graph from Ultra Circuit Builder + * @tparam FF + * @param ultra_circuit_constructor + */ + +template Graph_::Graph_(bb::UltraCircuitBuilder& ultra_circuit_constructor) +{ + this->variables_gate_counts = + std::unordered_map(ultra_circuit_constructor.real_variable_index.size()); + this->variable_adjacency_lists = + std::unordered_map>(ultra_circuit_constructor.real_variable_index.size()); + this->variables_degree = std::unordered_map(ultra_circuit_constructor.real_variable_index.size()); + for (const auto& variable_index : ultra_circuit_constructor.real_variable_index) { + variables_gate_counts[variable_index] = 0; + variables_degree[variable_index] = 0; + variable_adjacency_lists[variable_index] = {}; + } + + std::map constant_variable_indices = ultra_circuit_constructor.constant_variable_indices; + const auto& arithmetic_block = ultra_circuit_constructor.blocks.arithmetic; + auto arithmetic_gates_numbers = arithmetic_block.size(); + bool arithmetic_gates_exist = arithmetic_gates_numbers > 0; + if (arithmetic_gates_exist) { + for (size_t i = 0; i < arithmetic_gates_numbers; i++) { + auto gate_variables = this->get_arithmetic_gate_connected_component(ultra_circuit_constructor, i); + this->connect_all_variables_in_vector(ultra_circuit_constructor, gate_variables, false); + } + } + const auto& elliptic_block = ultra_circuit_constructor.blocks.elliptic; + auto elliptic_gates_numbers = elliptic_block.size(); + bool elliptic_gates_exist = elliptic_gates_numbers > 0; + if (elliptic_gates_exist) { + for (size_t i = 0; i < elliptic_gates_numbers; i++) { + std::vector gate_variables = + this->get_elliptic_gate_connected_component(ultra_circuit_constructor, i); + this->connect_all_variables_in_vector(ultra_circuit_constructor, gate_variables, false); + } + } + const auto& range_block = ultra_circuit_constructor.blocks.delta_range; + auto range_gates = range_block.size(); + bool range_gates_exists = range_gates > 0; + if (range_gates_exists) { + std::vector sorted_variables; + for (size_t i = 0; i < range_gates; i++) { + auto current_gate = this->get_sort_constraint_connected_component(ultra_circuit_constructor, i); + if (current_gate.empty()) { + this->connect_all_variables_in_vector(ultra_circuit_constructor, sorted_variables, true); + sorted_variables.clear(); + } else { + sorted_variables.insert(sorted_variables.end(), current_gate.begin(), current_gate.end()); + } + } + } + + const auto& lookup_block = ultra_circuit_constructor.blocks.lookup; + auto lookup_gates = lookup_block.size(); + bool lookup_gates_exists = lookup_gates > 0; + if (lookup_gates_exists) { + for (size_t i = 0; i < lookup_gates; i++) { + std::vector variable_indices = + this->get_plookup_gate_connected_component(ultra_circuit_constructor, i); + this->connect_all_variables_in_vector(ultra_circuit_constructor, variable_indices, false); + } + } +} + +/** + * @brief this method checks whether the variable with given index is not constant + * @tparam FF + * @param ultra_circuit_builder + * @param variable_index + * @return true + * @return false + */ + +template +bool Graph_::check_is_not_constant_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, + const uint32_t& variable_index) +{ + bool is_not_constant = true; + auto constant_variable_indices = ultra_circuit_builder.constant_variable_indices; + for (const auto& pair : constant_variable_indices) { + if (pair.second == ultra_circuit_builder.real_variable_index[variable_index]) { + is_not_constant = false; + break; + } + } + return is_not_constant; +} + +/** + * @brief this method adds connection between 2 variables, if they are in one gate, they are not constrant variables, + * and they have different indexes + * @tparam FF + * @param ultra_circuit_builder + * @param variables_vector + * @param is_sorted_variables + */ + +template +void Graph_::connect_all_variables_in_vector(bb::UltraCircuitBuilder& ultra_circuit_builder, + const std::vector& variables_vector, + bool is_sorted_variables) +{ + if (variables_vector.empty()) { + return; + } + if (is_sorted_variables) { + for (size_t i = 0; i < variables_vector.size() - 1; i++) { + if (variables_vector[i] != ultra_circuit_builder.zero_idx && + variables_vector[i + 1] != ultra_circuit_builder.zero_idx && + variables_vector[i] != variables_vector[i + 1]) { + { + bool first_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[i]); + bool second_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[i + 1]); + if (first_variable_is_not_constant && second_variable_is_not_constant) { + this->add_new_edge(variables_vector[i], variables_vector[i + 1]); + } + } + } + } + } else { + for (size_t i = 0; i < variables_vector.size() - 1; i++) { + for (size_t j = i + 1; j < variables_vector.size(); j++) { + if (variables_vector[i] != ultra_circuit_builder.zero_idx && + variables_vector[j] != ultra_circuit_builder.zero_idx && + variables_vector[i] != variables_vector[j]) { + + bool first_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[i]); + bool second_variable_is_not_constant = + this->check_is_not_constant_variable(ultra_circuit_builder, variables_vector[j]); + if (first_variable_is_not_constant && second_variable_is_not_constant) { + this->add_new_edge(variables_vector[i], variables_vector[j]); + } + } + } + } + } +} + +/** + * @brief this method creates an edge between two variables in graph. All needed checks in a function above + * @tparam FF + * @param first_variable_index + * @param second_variable_index + */ + +template +void Graph_::add_new_edge(const uint32_t& first_variable_index, const uint32_t& second_variable_index) +{ + variable_adjacency_lists[first_variable_index].emplace_back(second_variable_index); + variable_adjacency_lists[second_variable_index].emplace_back(first_variable_index); + variables_degree[first_variable_index] += 1; + variables_degree[second_variable_index] += 1; +} + +/** + * @brief this method implements depth-first search algorithm for undirected graphs + * @tparam FF + * @param variable_index + * @param is_used + * @param connected_component + */ + +template +void Graph_::depth_first_search(const uint32_t& variable_index, + std::unordered_set& is_used, + std::vector& connected_component) +{ + std::stack variable_stack; + variable_stack.push(variable_index); + while (!variable_stack.empty()) { + uint32_t current_index = variable_stack.top(); + variable_stack.pop(); + if (!is_used.contains(current_index)) { + is_used.insert(current_index); + connected_component.emplace_back(current_index); + for (const auto& it : variable_adjacency_lists[current_index]) { + variable_stack.push(it); + } + } + } +} + +/** + * @brief this methond finds all connected components in the graph described by adjacency lists + * @tparam FF + * @return std::vector> + */ + +template std::vector> Graph_::find_connected_components() +{ + std::unordered_set is_used; + std::vector> connected_components; + for (const auto& pair : variable_adjacency_lists) { + if (pair.first != 0 && variables_degree[pair.first] > 0) { + if (!is_used.contains(pair.first)) { + std::vector connected_component; + this->depth_first_search(pair.first, is_used, connected_component); + std::sort(connected_component.begin(), connected_component.end()); + connected_components.emplace_back(connected_component); + } + } + } + return connected_components; +} + +/** + * @brief this method removes variables that were created in a function decompose_into_default_range + * because they are false cases and don't give any useful information about security of the circuit. + * decompose_into_default_range function creates addition gates with shifts for intermediate variables, + * i.e. variables from left, right and output wires. They have variable gates count = 1 or 2, but they are not + * dangerous. so, we have to remove these variables from the analyzer. The situation is dangerous, if first variable + * from accumulators have variables gate count = 1. It means that it was used only in decompose gate, and it's not + * properly constrained. + * @tparam FF + * @param ultra_circuit_constructor + * @param variables_in_one_gate + * @param index + * @return size_t + */ + +template +inline size_t Graph_::process_current_decompose_chain(bb::UltraCircuitBuilder& ultra_circuit_constructor, + std::unordered_set& variables_in_one_gate, + size_t index) +{ + auto& arithmetic_block = ultra_circuit_constructor.blocks.arithmetic; + auto zero_idx = ultra_circuit_constructor.zero_idx; + size_t current_index = index; + std::vector accumulators_indices; + while (true) { + // we have to remove left, right and output wires of the current gate, cause they'are new_limbs, and they are + // useless for the analyzer + auto fourth_idx = arithmetic_block.w_4()[current_index]; + accumulators_indices.emplace_back(this->to_real(ultra_circuit_constructor, fourth_idx)); + auto left_idx = arithmetic_block.w_l()[current_index]; + if (left_idx != zero_idx) { + variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, left_idx)); + } + auto right_idx = arithmetic_block.w_r()[current_index]; + if (right_idx != zero_idx) { + variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, right_idx)); + } + auto out_idx = arithmetic_block.w_o()[current_index]; + if (out_idx != zero_idx) { + variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, out_idx)); + } + auto q_arith = arithmetic_block.q_arith()[current_index]; + if (q_arith == 1 || current_index == arithmetic_block.size() - 1) { + // this is the last gate in this chain, or we can't go next, so we have to stop a loop + break; + } + current_index++; + } + for (size_t i = 0; i < accumulators_indices.size(); i++) { + if (i == 0) { + // the first variable in accumulators is the variable which decompose was created. So, we have to decrement + // variable_gate_counts for this variable + variables_gate_counts[accumulators_indices[i]] -= 1; + } else { + // next accumulators are useless variables that are not interested for the analyzer. So, for these variables + // we can nullify variables_gate_counts + variables_gate_counts[accumulators_indices[i]] = 0; + } + } + // we don't want to make variables_gate_counts for intermediate variables negative, so, can go to the next gates + return current_index; +} + +/** + * @brief this method gets the endpoints of the decompose chains. For that it has to clean variable_index + from unnecessary variables for example, left, right, output wires and go through all decompose chain + * @tparam FF + * @param ultra_circuit_builder + * @param variables_in_one_gate + * @param decompose_variables + */ + +template +inline void Graph_::remove_unnecessary_decompose_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate, + const std::unordered_set& decompose_variables) +{ + auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); }; + auto find_position = [&](uint32_t variable_index) { + return decompose_variables.contains(this->to_real(ultra_circuit_builder, variable_index)); + }; + auto& arithmetic_block = ultra_circuit_builder.blocks.arithmetic; + if (arithmetic_block.size() > 0) { + for (size_t i = 0; i < arithmetic_block.size(); i++) { + auto q_1 = arithmetic_block.q_1()[i]; + auto q_2 = arithmetic_block.q_2()[i]; + auto q_3 = arithmetic_block.q_3()[i]; + // big addition gate from decompose has selectors, which have the next property: + // q_1 = (1) << shifts[0], target_range_bitnum * (3 * i), + // q_2 = (1) << shifts[1], target_range_bitnum * (3 * i + 1), + // q_3 = (1) << shifts[2], target_range_bitnum * (3 * i + 2) + // so, they are power of two and satisfying the following equality: q_2 * q_2 = q_1 * q_3 + // this way we can differ them from other arithmetic gates + bool q_1_is_power_two = is_power_two(q_1); + bool q_2_is_power_two = is_power_two(q_2); + bool q_3_is_power_two = is_power_two(q_3); + if (q_2 * q_2 == q_1 * q_3 && q_1_is_power_two && q_2_is_power_two && q_3_is_power_two) { + uint32_t left_idx = arithmetic_block.w_l()[i]; + uint32_t right_idx = arithmetic_block.w_r()[i]; + uint32_t out_idx = arithmetic_block.w_o()[i]; + uint32_t fourth_idx = arithmetic_block.w_4()[i]; + bool find_left = find_position(left_idx); + bool find_right = find_position(right_idx); + bool find_out = find_position(out_idx); + bool find_fourth = find_position(fourth_idx); + if (((find_left && find_right && find_out) || (find_left && find_right && !find_out) || + (find_left && find_right && !find_out) || (find_left && !find_right && !find_out)) && + !find_fourth) { + i = this->process_current_decompose_chain(ultra_circuit_builder, variables_in_one_gate, i); + } + } + } + } +} +/** + * @brief this method removes false positive cass variables from aes plookup tables. + * AES_SBOX_MAP, AES_SPARSE_MAP, AES_SPARSE_NORMALIZE tables are used in read_from_1_to_2_table function which + * return values C2[0], so C3[0] isn't used anymore in these cases, but this situation isn't dangerous. + * So, we have to remove these variables. + * @tparam FF + * @param variables_in_one_gate + * @param ultra_circuit_builder + * @param table_id + * @param gate_index + */ +template +inline void Graph_::remove_unnecessary_aes_plookup_variables(std::unordered_set& variables_in_one_gate, + UltraCircuitBuilder& ultra_circuit_builder, + BasicTableId& table_id, + size_t gate_index) +{ + + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + std::unordered_set aes_plookup_tables{ BasicTableId::AES_SBOX_MAP, + BasicTableId::AES_SPARSE_MAP, + BasicTableId::AES_SPARSE_NORMALIZE }; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + if (aes_plookup_tables.contains(table_id)) { + uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]); + uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]); + if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) { + bool find_out = find_position(real_out_idx); + auto q_c = lookup_block.q_c()[gate_index]; + if (q_c == 0) { + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + } + } +} + +/** + * @brief this method removes false cases in sha256 lookup tables. + * tables which are enumerated in the unordered set sha256_plookup_tables + * are used in read_from_1_to_2_table function which return C2[0], so C3[0] + * isn't used anymore, but this situation isn't dangerous. So, we have to remove these variables. + * @tparam FF + * @param variables_in_one_gate + * @param ultra_circuit_builder + * @param table_id + * @param gate_index + */ + +template +inline void Graph_::remove_unnecessary_sha256_plookup_variables(std::unordered_set& variables_in_one_gate, + UltraCircuitBuilder& ultra_circuit_builder, + BasicTableId& table_id, + size_t gate_index) +{ + + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + std::unordered_set sha256_plookup_tables{ BasicTableId::SHA256_WITNESS_SLICE_3, + BasicTableId::SHA256_WITNESS_SLICE_7_ROTATE_4, + BasicTableId::SHA256_WITNESS_SLICE_8_ROTATE_7, + BasicTableId::SHA256_WITNESS_SLICE_14_ROTATE_1, + BasicTableId::SHA256_BASE16, + BasicTableId::SHA256_BASE16_ROTATE2, + BasicTableId::SHA256_BASE16_ROTATE6, + BasicTableId::SHA256_BASE16_ROTATE7, + BasicTableId::SHA256_BASE16_ROTATE8, + BasicTableId::SHA256_BASE28, + BasicTableId::SHA256_BASE28_ROTATE3, + BasicTableId::SHA256_BASE28_ROTATE6 }; + if (sha256_plookup_tables.contains(table_id)) { + uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]); + uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]); + if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) { + // auto q_m = lookup_block.q_m()[gate_index]; + auto q_c = lookup_block.q_c()[gate_index]; + bool find_out = find_position(real_out_idx); + // bool find_right = find_position(real_right_idx); + if (q_c == 0) { + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + if (table_id == SHA256_BASE16_ROTATE2 || table_id == SHA256_BASE28_ROTATE6) { + // we want to remove false cases for special tables even though their selectors != 0 + // because they are used in read_from_1_to_2_table function, and they aren't dangerous + variables_in_one_gate.erase(real_out_idx); + } + } + } +} + +/** + * @brief this method removes false cases in lookup table for a given gate. + * it uses all functions above for lookup tables to remove all variables that appear in one gate, + * if they are not dangerous + * @tparam FF + * @param ultra_circuit_builder + * @param variables_in_one_gate + * @param gate_index + */ + +template +inline void Graph_::process_current_plookup_gate(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate, + size_t gate_index) +{ + auto find_position = [&](uint32_t real_variable_index) { + return variables_in_one_gate.contains(real_variable_index); + }; + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + auto& lookup_tables = ultra_circuit_builder.lookup_tables; + auto table_index = static_cast(lookup_block.q_3()[gate_index]); + for (const auto& table : lookup_tables) { + if (table.table_index == table_index) { + std::set column_1(table.column_1.begin(), table.column_1.end()); + std::set column_2(table.column_2.begin(), table.column_2.end()); + std::set column_3(table.column_3.begin(), table.column_3.end()); + bb::plookup::BasicTableId table_id = table.id; + // false cases for AES + this->remove_unnecessary_aes_plookup_variables( + variables_in_one_gate, ultra_circuit_builder, table_id, gate_index); + // false cases for sha256 + this->remove_unnecessary_sha256_plookup_variables( + variables_in_one_gate, ultra_circuit_builder, table_id, gate_index); + // if the amount of unique elements from columns of plookup tables = 1, it means that + // variable from this column aren't used and we can remove it. + if (column_1.size() == 1) { + uint32_t left_idx = lookup_block.w_l()[gate_index]; + uint32_t real_left_idx = this->to_real(ultra_circuit_builder, left_idx); + bool find_left = find_position(real_left_idx); + if (find_left) { + variables_in_one_gate.erase(real_left_idx); + } + } + if (column_2.size() == 1) { + uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]); + bool find_right = find_position(real_right_idx); + if (find_right) { + variables_in_one_gate.erase(real_right_idx); + } + } + if (column_3.size() == 1) { + uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]); + bool find_out = find_position(real_out_idx); + if (find_out) { + variables_in_one_gate.erase(real_out_idx); + } + } + } + } +} + +/** + * @brief this method removes false cases plookup variables from variables in one gate + * @tparam FF + * @param ultra_circuit_builder + * @param variables_in_one_gate + */ + +template +inline void Graph_::remove_unnecessary_plookup_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate) +{ + auto& lookup_block = ultra_circuit_builder.blocks.lookup; + if (lookup_block.size() > 0) { + for (size_t i = 0; i < lookup_block.size(); i++) { + this->process_current_plookup_gate(ultra_circuit_builder, variables_in_one_gate, i); + } + } +} + +/** + * @brief this method returns a final set of variables that were in one gate + * @tparam FF + * @param ultra_circuit_builder + * @return std::unordered_set + */ + +template +std::unordered_set Graph_::show_variables_in_one_gate(bb::UltraCircuitBuilder& ultra_circuit_builder) +{ + std::unordered_set variables_in_one_gate; + for (const auto& pair : variables_gate_counts) { + bool is_not_constant_variable = this->check_is_not_constant_variable(ultra_circuit_builder, pair.first); + if (pair.second == 1 && pair.first != 0 && is_not_constant_variable) { + variables_in_one_gate.insert(pair.first); + } + } + auto range_lists = ultra_circuit_builder.range_lists; + std::unordered_set decompose_varialbes; + for (auto& pair : range_lists) { + for (auto& elem : pair.second.variable_indices) { + bool is_not_constant_variable = this->check_is_not_constant_variable(ultra_circuit_builder, elem); + if (variables_gate_counts[ultra_circuit_builder.real_variable_index[elem]] == 1 && + is_not_constant_variable) { + decompose_varialbes.insert(ultra_circuit_builder.real_variable_index[elem]); + } + } + } + this->remove_unnecessary_decompose_variables(ultra_circuit_builder, variables_in_one_gate, decompose_varialbes); + this->remove_unnecessary_plookup_variables(ultra_circuit_builder, variables_in_one_gate); + return variables_in_one_gate; +} + +/** + * @brief this method returns connected component with a given index and size of this component + * sometimes for debugging we want to check the size one of the connected component, so it would be + * useful to know its size + * @param connected_components + * @param index + * @return std::pair, size_t> + */ + +std::pair, size_t> get_connected_component_with_index( + const std::vector>& connected_components, size_t index) +{ + auto connected_component = connected_components[index]; + auto size = connected_component.size(); + return std::make_pair(connected_component, size); +} + +/** + * @brief this method prints graph as vertices and their adjacency lists + * example: we have an undirected graph from 3 variables: a, b, c. + * we have edges: a - b, b - c, c - a. + * so, there will be next adjacency lists: + * a: b -> c -> 0\ + * b: a -> c -> 0\ + * c: a -> b -> 0\ + * @tparam FF + */ + +template void Graph_::print_graph() +{ + for (const auto& elem : variable_adjacency_lists) { + info("variable with index", elem.first); + if (variable_adjacency_lists[elem.first].empty()) { + info("is isolated"); + } else { + for (const auto& it : elem.second) { + info(it); + } + } + } +} + +/** + * @brief this method prints all connected components that were found in the graph + * @tparam FF + */ + +template void Graph_::print_connected_components() +{ + auto connected_components = find_connected_components(); + for (size_t i = 0; i < connected_components.size(); i++) { + info("printing the ", i + 1, " connected component:"); + for (const auto& it : connected_components[i]) { + info(it, " "); + } + } +} + +/** + * @brief this method prints a number of gates for each variable. + * while processing the arithmetic circuit, we count for each variable the number of gates it has participated in. + * sometimes for debugging purposes it is useful to see how many gates each variable has participated in. + * @tparam FF + */ + +template void Graph_::print_variables_gate_counts() +{ + for (const auto& it : variables_gate_counts) { + info("number of gates with variables ", it.first, " == ", it.second); + } +} + +/** + * @brief this method prints a number of edges for each variable. + * while processing the arithmetic circuit, we conut for each variable the number of edges, i.e. connections with other + * variables though the gates. perhaps in the future counting the number of edges for each vertex can be useful for + * analysis, and this function will be used for debugging. + * @tparam FF + */ + +template void Graph_::print_variables_edge_counts() +{ + for (const auto& it : variables_degree) { + if (it.first != 0) { + info("variable index = ", it.first, "number of edges for this variable = ", it.second); + } + } +} + +template class Graph_; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp new file mode 100644 index 00000000000..c4c88e1e159 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph.hpp @@ -0,0 +1,119 @@ +#pragma once +#include "barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" +#include +#include +#include +#include +#include +#include + +/* + * this class describes arithmetic circuit as an undirected graph, where vertices are variables from circuit. + * edges describe connections between variables through gates. We want to find variables that weren't properly + * constrainted/some connections were missed using additional metrics, like in how much gate variable was and number of + * connected components in the graph. if variable was in one connected component, it means that this variable wasn't + * constrained properly. if number of connected components > 1, it means that there were missed some connections between + * variables. + */ +template class Graph_ { + public: + Graph_() = default; + Graph_(const Graph_& other) = delete; + Graph_(Graph_&& other) = delete; + Graph_& operator=(const Graph_& other) = delete; + Graph_&& operator=(Graph_&& other) = delete; + Graph_(const bb::StandardCircuitBuilder_& circuit_constructor); + Graph_(bb::UltraCircuitBuilder& ultra_circuit_constructor); + + uint32_t to_real(bb::UltraCircuitBuilder& ultra_circuit_constructor, const uint32_t& variable_index) + { + return ultra_circuit_constructor.real_variable_index[variable_index]; + }; + void process_gate_variables(bb::UltraCircuitBuilder& ultra_circuit_constructor, + std::vector& gate_variables); + + std::unordered_map get_variables_gate_counts() { return this->variables_gate_counts; }; + + std::vector get_arithmetic_gate_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + std::vector get_elliptic_gate_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + std::vector get_plookup_gate_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + std::vector get_sort_constraint_connected_component(bb::UltraCircuitBuilder& ultra_circuit_builder, + size_t index); + + void add_new_edge(const uint32_t& first_variable_index, const uint32_t& second_variable_index); + std::vector get_variable_adjacency_list(const uint32_t& variable_index) + { + return variable_adjacency_lists[variable_index]; + }; + + void depth_first_search(const uint32_t& variable_index, + std::unordered_set& is_used, + std::vector& connected_component); + std::vector> find_connected_components(); + + std::vector find_variables_with_degree_one(); + std::unordered_set get_variables_in_one_gate(); + + bool find_arithmetic_gate_for_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, + const uint32_t& variable_idx); + bool find_elliptic_gate_for_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, const uint32_t& variable_idx); + bool find_lookup_gate_for_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, const uint32_t& variable_idx); + + size_t get_distance_between_variables(const uint32_t& first_variable_index, const uint32_t& second_variable_index); + bool check_vertex_in_connected_component(const std::vector& connected_component, + const uint32_t& var_index); + + void connect_all_variables_in_vector(bb::UltraCircuitBuilder& ultra_circuit_builder, + const std::vector& variables_vector, + bool is_sorted_variables); + bool check_is_not_constant_variable(bb::UltraCircuitBuilder& ultra_circuit_builder, const uint32_t& variable_index); + + std::pair, size_t> get_connected_component_with_index( + const std::vector>& connected_components, size_t index); + + std::unordered_set get_variables_in_one_gate_without_range_constraints( + bb::UltraCircuitBuilder& ultra_circuit_builder); + + size_t process_current_decompose_chain(bb::UltraCircuitBuilder& ultra_circuit_constructor, + std::unordered_set& variables_in_one_gate, + size_t index); + void process_current_plookup_gate(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_one_gate, + size_t gate_index); + void remove_unnecessary_decompose_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_on_gate, + const std::unordered_set& decompose_variables); + void remove_unnecessary_plookup_variables(bb::UltraCircuitBuilder& ultra_circuit_builder, + std::unordered_set& variables_in_on_gate); + std::unordered_set show_variables_in_one_gate(bb::UltraCircuitBuilder& ultra_circuit_builder); + + void remove_unnecessary_aes_plookup_variables(std::unordered_set& variables_in_one_gate, + bb::UltraCircuitBuilder& ultra_circuit_builder, + bb::plookup::BasicTableId& table_id, + size_t gate_index); + void remove_unnecessary_sha256_plookup_variables(std::unordered_set& variables_in_one_gate, + bb::UltraCircuitBuilder& ultra_circuit_builder, + bb::plookup::BasicTableId& table_id, + size_t gate_index); + + void print_graph(); + void print_connected_components(); + void print_variables_gate_counts(); + void print_variables_edge_counts(); + ~Graph_() = default; + + private: + std::unordered_map> + variable_adjacency_lists; // we use this data structure to contain information about variables and their + // connections between each other + std::unordered_map + variables_gate_counts; // we use this data structure to count, how many gates use every variable + std::unordered_map + variables_degree; // we use this data structure to count, how many every variable have edges +}; + +using Graph = Graph_; \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description.test.cpp new file mode 100644 index 00000000000..26f50cc8db1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description.test.cpp @@ -0,0 +1,661 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/generators/generator_data.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib/encryption/aes128/aes128.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; + +/** + * @brief this test checks graph description of the circuit with arithmetic gates + the number of connected components = the number of pair (i, j), 0<=i, j <16, i.e 256 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_arithmetic_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 16; ++i) { + for (size_t j = 0; j < 16; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + } + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + auto variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + bool result = num_connected_components == 256; + EXPECT_EQ(result, true); +} + +/** + * @brief This test checks graph description of Ultra Circuit Builder with arithmetic gates with shifts + * It must be one connected component, cause all gates have shifts + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_arithmetic_gates_with_shifts) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + for (size_t i = 0; i < 16; ++i) { + for (size_t j = 0; j < 16; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }, true); + } + } + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks graph description of the circuit with boolean gates. + all variables must be isolated and the number of connected components = 0, all variables in one gate + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_boolean_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 20; ++i) { + fr a = fr::zero(); + uint32_t a_idx = circuit_constructor.add_variable(a); + circuit_constructor.create_bool_gate(a_idx); + } + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + auto variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + bool result = num_connected_components == 0; + EXPECT_EQ(result, true); + EXPECT_EQ(variables_in_one_gate.size(), 20); +} + +/** + * @brief this test checks graph decription for the circuit with one elliptic addition gate. + * The result is one connected component for 6 variables: + * x1, y1, x2, y2, x3, y3 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_elliptic_add_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + + affine_element p2 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 1); + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x2 = circuit_constructor.add_variable(p2.x); + uint32_t y2 = circuit_constructor.add_variable(p2.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks graph description of the circuit with one elliptic double gate. + The result is one connected component for 4 variables: + x1, y1, x3, y3 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_elliptic_double_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + affine_element p3(element(p1).dbl()); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_dbl_gate({ x1, y1, x3, y3 }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks the graph description of the circuit has elliptic addition and multiplication + gates together. The result is 2 connected components: + x1, y1, x2, y2, x3, y3, x4, y4 + x5, y5, x6, y6, x7, y7, x8, y8 + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_elliptic_together) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + affine_element p2 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 1); + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x2 = circuit_constructor.add_variable(p2.x); + uint32_t y2 = circuit_constructor.add_variable(p2.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 }); + affine_element p4(element(p3).dbl()); + uint32_t x4 = circuit_constructor.add_variable(p4.x); + uint32_t y4 = circuit_constructor.add_variable(p4.y); + circuit_constructor.create_ecc_dbl_gate({ x3, y3, x4, y4 }); + + affine_element p5 = crypto::pedersen_commitment::commit_native({ bb::fr(2) }, 1); + affine_element p6 = crypto::pedersen_commitment::commit_native({ bb::fr(3) }, 1); + affine_element p7(element(p5) + element(p6)); + + uint32_t x5 = circuit_constructor.add_variable(p5.x); + uint32_t y5 = circuit_constructor.add_variable(p5.y); + uint32_t x6 = circuit_constructor.add_variable(p6.x); + uint32_t y6 = circuit_constructor.add_variable(p6.y); + uint32_t x7 = circuit_constructor.add_variable(p7.x); + uint32_t y7 = circuit_constructor.add_variable(p7.y); + + circuit_constructor.create_ecc_add_gate({ x5, y5, x6, y6, x7, y7, 1 }); + affine_element p8(element(p7).dbl()); + uint32_t x8 = circuit_constructor.add_variable(p8.x); + uint32_t y8 = circuit_constructor.add_variable(p8.y); + circuit_constructor.create_ecc_dbl_gate({ x7, y7, x8, y8 }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 2; + EXPECT_EQ(result, true); +} + +/** + * @brief this test check graph description of the circuit with 2 sort_constraint. The result is 2 connected components: + a_idx, b_idx, c_idx, d_idx + e_idx, f_idx, g_idx, h_idx + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_sort_constraints) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + circuit_constructor.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint({ e_idx, f_idx, g_idx, h_idx }); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components[0].size(), 4); + EXPECT_EQ(connected_components[1].size(), 4); + EXPECT_EQ(connected_components.size(), 2); +} + +/** + * @brief this test checks graph description of the circuit with 2 sorted_constraints with edges. + The result is 2 connected components: + a_idx, b_idx, ... , h_idx + a1_idx, b1_idx, ..., h1_idx + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_sort_constraints_with_edges) +{ + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + + UltraCircuitBuilder circuit_constructor; + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint_with_edges( + { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); + + fr a1 = fr(9); + fr b1 = fr(10); + fr c1 = fr(11); + fr d1 = fr(12); + fr e1 = fr(13); + fr f1 = fr(14); + fr g1 = fr(15); + fr h1 = fr(16); + + auto a1_idx = circuit_constructor.add_variable(a1); + auto b1_idx = circuit_constructor.add_variable(b1); + auto c1_idx = circuit_constructor.add_variable(c1); + auto d1_idx = circuit_constructor.add_variable(d1); + auto e1_idx = circuit_constructor.add_variable(e1); + auto f1_idx = circuit_constructor.add_variable(f1); + auto g1_idx = circuit_constructor.add_variable(g1); + auto h1_idx = circuit_constructor.add_variable(h1); + + circuit_constructor.create_sort_constraint_with_edges( + { a1_idx, b1_idx, c1_idx, d1_idx, e1_idx, f1_idx, g1_idx, h1_idx }, a1, h1); + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 2; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks graph decription for circuit with gates that were created from plookup accumulators + the result is one connected component + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_with_plookup_accumulators) +{ + UltraCircuitBuilder circuit_builder = UltraCircuitBuilder(); + + fr input_value = fr::random_element(); + const fr input_lo = static_cast(input_value).slice(0, plookup::fixed_base::table::BITS_PER_LO_SCALAR); + const auto input_lo_index = circuit_builder.add_variable(input_lo); + + const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::FIXED_BASE_LEFT_LO, input_lo); + + const auto lookup_witnesses = circuit_builder.create_gates_from_plookup_accumulators( + plookup::MultiTableId::FIXED_BASE_LEFT_LO, sequence_data_lo, input_lo_index); + + const size_t num_lookups = plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE; + + EXPECT_EQ(num_lookups, lookup_witnesses[plookup::ColumnIdx::C1].size()); + + Graph graph = Graph(circuit_builder); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool result = num_connected_components == 1; + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variable gates counts for variable from arithmetic gates without shifts + in circuit + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_arithmetic_gate) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 25; ++i) { + for (size_t j = 0; j < 25; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + } + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + bool result = true; + for (const auto pair : variables_gate_counts) { + result = result && (pair.first > 0 ? (pair.second == 1) : (pair.second == 0)); + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variable in circuit with gates with shifts. + * All variables except for zero index, which index == 0 mod 4 and index != 4 have gates count == 2. + * Other variables have gates count = 1. + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_arithmetic_gate_with_shifts) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 25; ++i) { + for (size_t j = 0; j < 25; ++j) { + uint64_t left = static_cast(j); + uint64_t right = static_cast(i); + uint32_t left_idx = circuit_constructor.add_variable(fr(left)); + uint32_t right_idx = circuit_constructor.add_variable(fr(right)); + uint32_t result_idx = circuit_constructor.add_variable(fr(left ^ right)); + + uint32_t add_idx = + circuit_constructor.add_variable(fr(left) + fr(right) + circuit_constructor.get_variable(result_idx)); + circuit_constructor.create_big_add_gate( + { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }, true); + } + } + + Graph graph = Graph(circuit_constructor); + bool result = true; + auto variables_gate_counts = graph.get_variables_gate_counts(); + for (const auto& pair : variables_gate_counts) { + if (pair.first > 0) { + result = result && (pair.first % 4 == 0 && pair.first != 4 ? (pair.second == 2) : (pair.second == 1)); + } else { + result = result && (pair.second == 0); + } + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variables in circuit with boolean gates + * all variables except for zero index must have gates count = 1. + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_boolean_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + for (size_t i = 0; i < 20; ++i) { + fr a = fr::zero(); + uint32_t a_idx = circuit_constructor.add_variable(a); + circuit_constructor.create_bool_gate(a_idx); + } + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + bool result = true; + for (const auto& part : variables_gate_counts) { + result = result && (part.first == 0 ? (part.second == 0) : (part.second == 1)); + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gate counts in circuit with sorted constraints. + * all variables in 2 connected components must have gates count = 1 + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_sorted_constraints) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + circuit_constructor.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx }); + + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint({ e_idx, f_idx, g_idx, h_idx }); + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 2); + bool result = true; + for (size_t i = 0; i < connected_components[0].size(); i++) { + result = result && (variables_gate_counts[connected_components[0][i]] == 1); + } + + for (size_t i = 0; i < connected_components[1].size(); i++) { + result = result && (variables_gate_counts[connected_components[1][i]] == 1); + } + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variable gates count for variables in circuit with sorted constraints with edges + * all variables in 2 connected components must have gates count = 1 + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_sorted_constraints_with_edges) +{ + fr a = fr::one(); + fr b = fr(2); + fr c = fr(3); + fr d = fr(4); + fr e = fr(5); + fr f = fr(6); + fr g = fr(7); + fr h = fr(8); + + UltraCircuitBuilder circuit_constructor; + auto a_idx = circuit_constructor.add_variable(a); + auto b_idx = circuit_constructor.add_variable(b); + auto c_idx = circuit_constructor.add_variable(c); + auto d_idx = circuit_constructor.add_variable(d); + auto e_idx = circuit_constructor.add_variable(e); + auto f_idx = circuit_constructor.add_variable(f); + auto g_idx = circuit_constructor.add_variable(g); + auto h_idx = circuit_constructor.add_variable(h); + circuit_constructor.create_sort_constraint_with_edges( + { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h); + + fr a1 = fr(9); + fr b1 = fr(10); + fr c1 = fr(11); + fr d1 = fr(12); + fr e1 = fr(13); + fr f1 = fr(14); + fr g1 = fr(15); + fr h1 = fr(16); + + auto a1_idx = circuit_constructor.add_variable(a1); + auto b1_idx = circuit_constructor.add_variable(b1); + auto c1_idx = circuit_constructor.add_variable(c1); + auto d1_idx = circuit_constructor.add_variable(d1); + auto e1_idx = circuit_constructor.add_variable(e1); + auto f1_idx = circuit_constructor.add_variable(f1); + auto g1_idx = circuit_constructor.add_variable(g1); + auto h1_idx = circuit_constructor.add_variable(h1); + + circuit_constructor.create_sort_constraint_with_edges( + { a1_idx, b1_idx, c1_idx, d1_idx, e1_idx, f1_idx, g1_idx, h1_idx }, a1, h1); + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + auto variables_gate_counts = graph.get_variables_gate_counts(); + bool result = true; + for (size_t i = 0; i < connected_components[0].size(); i++) { + result = result && (variables_gate_counts[connected_components[0][i]] == 1); + } + + for (size_t i = 0; i < connected_components[1].size(); i++) { + result = result && (variables_gate_counts[connected_components[1][i]] == 1); + } + EXPECT_EQ(connected_components.size(), 2); + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variables in circuit with 1 elliptic addition gates + * all variables in connected components must have gates count = 1 + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_ecc_add_gates) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + + affine_element p2 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 1); + affine_element p3(element(p1) + element(p2)); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x2 = circuit_constructor.add_variable(p2.x); + uint32_t y2 = circuit_constructor.add_variable(p2.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 }); + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + auto connected_components = graph.find_connected_components(); + bool result = (variables_gate_counts[connected_components[0][0]] == 1) && + (variables_gate_counts[connected_components[0][1]] == 1) && + (variables_gate_counts[connected_components[0][2]] == 1) && + (variables_gate_counts[connected_components[0][3]] == 1) && + (variables_gate_counts[connected_components[0][4]] == 1) && + (variables_gate_counts[connected_components[0][5]] == 1); + EXPECT_EQ(connected_components.size(), 1); + EXPECT_EQ(result, true); +} + +/** + * @brief this test checks variables gates count for variables in circuit with 1 elliptic double gates + * all variables in connected components must have gates count = 1. + */ + +TEST(boomerang_ultra_circuit_constructor, test_variables_gates_counts_for_ecc_dbl_gate) +{ + typedef grumpkin::g1::affine_element affine_element; + typedef grumpkin::g1::element element; + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + + affine_element p1 = crypto::pedersen_commitment::commit_native({ bb::fr(1) }, 0); + affine_element p3(element(p1).dbl()); + + uint32_t x1 = circuit_constructor.add_variable(p1.x); + uint32_t y1 = circuit_constructor.add_variable(p1.y); + uint32_t x3 = circuit_constructor.add_variable(p3.x); + uint32_t y3 = circuit_constructor.add_variable(p3.y); + + circuit_constructor.create_ecc_dbl_gate({ x1, y1, x3, y3 }); + + Graph graph = Graph(circuit_constructor); + auto variables_gate_counts = graph.get_variables_gate_counts(); + auto connected_components = graph.find_connected_components(); + + bool result = (variables_gate_counts[connected_components[0][0]] == 1) && + (variables_gate_counts[connected_components[0][1]] == 1) && + (variables_gate_counts[connected_components[0][2]] == 1) && + (variables_gate_counts[connected_components[0][3]] == 1); + + EXPECT_EQ(connected_components.size(), 1); + EXPECT_EQ(result, true); +} + +std::vector add_variables(UltraCircuitBuilder& circuit_constructor, std::vector variables) +{ + std::vector res; + for (size_t i = 0; i < variables.size(); i++) { + res.emplace_back(circuit_constructor.add_variable(variables[i])); + } + return res; +} + +/** + * @brief this test checks graph description of circuit with range constraints. + * all variables must be in one connected component. + */ + +TEST(boomerang_ultra_circuit_constructor, test_graph_for_range_constraints) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto indices = add_variables(circuit_constructor, { 1, 2, 3, 4 }); + for (size_t i = 0; i < indices.size(); i++) { + circuit_constructor.create_new_range_constraint(indices[i], 5); + } + circuit_constructor.create_sort_constraint(indices); + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +/** + * @brief this checks graph description of circuit with decompose function. + * all variables must be in one connected component + */ + +TEST(boomerang_ultra_circuit_constructor, composed_range_constraint) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(e)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 134); + + Graph graph = Graph(circuit_constructor); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_aes128.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_aes128.test.cpp new file mode 100644 index 00000000000..9db54f96429 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_aes128.test.cpp @@ -0,0 +1,123 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/generators/generator_data.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib/encryption/aes128/aes128.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; +typedef stdlib::field_t field_pt; +typedef stdlib::witness_t witness_pt; + +bool check_in_vector(const std::vector& input_vector, const uint32_t& real_var_index) +{ + for (const auto& elem : input_vector) { + if (elem.witness_index == real_var_index) { + return true; + } + } + return false; +} + +/** + * @brief this test checks graph description of circuit for AES128CBC + * graph must be consist from one connected component + */ + +TEST(boomerang_stdlib_aes, test_graph_for_aes_64_bytes) +{ + uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; + + const auto convert_bytes = [](uint8_t* data) { + uint256_t converted(0); + for (uint64_t i = 0; i < 16; ++i) { + uint256_t to_add = uint256_t((uint64_t)(data[i])) << uint256_t((15 - i) * 8); + converted += to_add; + } + return converted; + }; + + auto builder = Builder(); + + std::vector in_field{ + witness_pt(&builder, fr(convert_bytes(in))), + witness_pt(&builder, fr(convert_bytes(in + 16))), + witness_pt(&builder, fr(convert_bytes(in + 32))), + witness_pt(&builder, fr(convert_bytes(in + 48))), + }; + + field_pt key_field(witness_pt(&builder, fr(convert_bytes(key)))); + field_pt iv_field(witness_pt(&builder, fr(convert_bytes(iv)))); + + const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + auto num_connected_components = connected_components.size(); + bool graph_result = num_connected_components == 1; + + EXPECT_EQ(graph_result, true); +} + +/** + * @brief this test checks variables gate counts for variables in circuit for AES128CBC + * Some variables can be from input/output vectors, or they are key and iv, and they have variable + * gates count = 1, because it's the circuit for test. So, we can ignore these variables + */ + +TEST(boomerang_stdlib_aes, test_variable_gates_count_for_aes128cbc) +{ + + uint8_t key[16]{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; + uint8_t iv[16]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + uint8_t in[64]{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }; + + const auto convert_bytes = [](uint8_t* data) { + uint256_t converted(0); + for (uint64_t i = 0; i < 16; ++i) { + uint256_t to_add = uint256_t((uint64_t)(data[i])) << uint256_t((15 - i) * 8); + converted += to_add; + } + return converted; + }; + + auto builder = Builder(); + + std::vector in_field{ + witness_pt(&builder, fr(convert_bytes(in))), + witness_pt(&builder, fr(convert_bytes(in + 16))), + witness_pt(&builder, fr(convert_bytes(in + 32))), + witness_pt(&builder, fr(convert_bytes(in + 48))), + }; + + field_pt key_field(witness_pt(&builder, fr(convert_bytes(key)))); + field_pt iv_field(witness_pt(&builder, fr(convert_bytes(iv)))); + + const auto result = stdlib::aes128::encrypt_buffer_cbc(in_field, iv_field, key_field); + + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_vector(in_field, elem); + bool result2 = check_in_vector(result, elem); + bool check = + (result1 == 1) || (result2 == 1) || (elem == key_field.witness_index) || (elem == iv_field.witness_index); + EXPECT_EQ(check, true); + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake2s.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake2s.test.cpp new file mode 100644 index 00000000000..934e92b568a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake2s.test.cpp @@ -0,0 +1,57 @@ +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/crypto/blake2s/blake2s.hpp" +#include "barretenberg/stdlib/hash/blake2s/blake2s.hpp" +#include "barretenberg/stdlib/hash/blake2s/blake2s_plookup.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" +#include "graph.hpp" +#include + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; + +using field_ct = field_t; +using witness_ct = witness_t; +using byte_array_ct = byte_array; +using byte_array_plookup = byte_array; +using public_witness_t = public_witness_t; + +/** + * @brief this tests check graph description of circuit for blake2s for one and two blocks. + * all graphs must have one connected component. + */ + +TEST(boomerang_stdlib_blake2s, test_graph_for_blake2s_single_block_plookup) +{ + Builder builder; + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = blake2s(input_arr); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +TEST(boomerang_stdlib_blake2s, test_graph_for_blake2s_double_block_plookup) +{ + Builder builder; + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = blake2s(input_arr); + + auto expected = crypto::blake2s(input_v); + + EXPECT_EQ(output.get_value(), std::vector(expected.begin(), expected.end())); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake3s.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake3s.test.cpp new file mode 100644 index 00000000000..d826f1e080b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_blake3s.test.cpp @@ -0,0 +1,58 @@ +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/streams.hpp" +#include "barretenberg/crypto/blake3s/blake3s.hpp" +#include "barretenberg/stdlib/hash/blake3s/blake3s.hpp" +#include "barretenberg/stdlib/hash/blake3s/blake3s_plookup.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "graph.hpp" +#include + +using namespace bb; + +using byte_array = stdlib::byte_array; +using public_witness_t = stdlib::public_witness_t; +using byte_array_plookup = stdlib::byte_array; +using public_witness_t_plookup = stdlib::public_witness_t; +using UltraBuilder = UltraCircuitBuilder; + +/** + * @brief this tests check that graph description of circuit for blake3s for different blocks. + * All graphs must have one connected component + */ + +TEST(boomerang_stdlib_blake3s, test_single_block_plookup) +{ + auto builder = UltraBuilder(); + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = stdlib::blake3s(input_arr); + + std::vector expected = blake3::blake3s(input_v); + + EXPECT_EQ(output.get_value(), expected); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +TEST(boomerang_stdlib_blake3s, test_double_block_plookup) +{ + auto builder = UltraBuilder(); + std::string input = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789"; + std::vector input_v(input.begin(), input.end()); + + byte_array_plookup input_arr(&builder, input_v); + byte_array_plookup output = stdlib::blake3s(input_arr); + + std::vector expected = blake3::blake3s(input_v); + + EXPECT_EQ(output.get_value(), expected); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_sha256.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_sha256.test.cpp new file mode 100644 index 00000000000..0370398d81d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_sha256.test.cpp @@ -0,0 +1,71 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/crypto/sha256/sha256.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include "barretenberg/numeric/bitop/rotate.hpp" +#include "barretenberg/numeric/bitop/sparse_form.hpp" +#include "barretenberg/numeric/random/engine.hpp" + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; + +using byte_array_ct = byte_array; +using packed_byte_array_ct = packed_byte_array; +using field_ct = field_t; + +/** + all these tests check graph description for sha256 circuits. All circuits have to consist from 1 connected component + */ + +TEST(boomerang_stdlib_sha256, test_graph_for_sha256_55_bytes) +{ + // 55 bytes is the largest number of bytes that can be hashed in a single block, + // accounting for the single padding bit, and the 64 size bits required by the SHA-256 standard. + auto builder = Builder(); + packed_byte_array_ct input(&builder, "An 8 character password? Snow White and the 7 Dwarves.."); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + + std::vector output = output_bits.to_unverified_byte_slices(4); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} + +HEAVY_TEST(boomerang_stdlib_sha256, test_graph_for_sha256_NIST_vector_five) +{ + typedef stdlib::field_t field_pt; + typedef stdlib::packed_byte_array packed_byte_array_pt; + + auto builder = UltraCircuitBuilder(); + + packed_byte_array_pt input( + &builder, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAA"); + + packed_byte_array_pt output_bits = stdlib::sha256(input); + + std::vector output = output_bits.to_unverified_byte_slices(4); + + Graph graph = Graph(builder); + auto connected_components = graph.find_connected_components(); + EXPECT_EQ(connected_components.size(), 1); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variable_gates_count.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variable_gates_count.test.cpp new file mode 100644 index 00000000000..d07ebeeac4f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variable_gates_count.test.cpp @@ -0,0 +1,130 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/crypto/aes128/aes128.hpp" +#include "barretenberg/crypto/generators/generator_data.hpp" +#include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib/encryption/aes128/aes128.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include + +using namespace bb; + +TEST(boomerang_ultra_circuit_constructor, test_variable_gates_count_for_decompose) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 133); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(e)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 134); + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_on_gate.size(), 1); +} + +TEST(boomerang_ultra_circuit_constructor, test_variable_gates_count_for_decompose2) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 41); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(e)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 42); + + Graph graph = Graph(circuit_constructor); + auto variables_in_on_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_on_gate.size(), 1); +} + +TEST(boomerang_utils, test_selectors_for_decompose) +{ + auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); }; + const uint64_t target_range_bitnum = 14; + size_t i = 0; + const uint64_t shifts[3]{ + target_range_bitnum * (3 * i), + target_range_bitnum * (3 * i + 1), + target_range_bitnum * (3 * i + 2), + }; + uint256_t q_1 = uint256_t(1) << shifts[0]; + uint256_t q_2 = uint256_t(1) << shifts[1]; + uint256_t q_3 = uint256_t(1) << shifts[2]; + bool q_1_is_power_two = is_power_two(q_1); + bool q_2_is_power_two = is_power_two(q_2); + bool q_3_is_power_two = is_power_two(q_3); + EXPECT_EQ(q_2 * q_2, q_1 * q_3); + EXPECT_EQ(q_1_is_power_two, true); + EXPECT_EQ(q_2_is_power_two, true); + EXPECT_EQ(q_3_is_power_two, true); +} + +TEST(boomerang_ultra_circuit_constructor, test_variable_gates_count_for_two_decomposes) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c1 = fr::random_element(); + auto c2 = fr::random_element(); + auto d1 = uint256_t(c1).slice(0, 41); + auto d2 = uint256_t(c2).slice(0, 41); + auto e1 = fr(d1); + auto e2 = fr(d2); + auto a1_idx = circuit_constructor.add_variable(fr(e1)); + auto a2_idx = circuit_constructor.add_variable(fr(e2)); + circuit_constructor.create_add_gate( + { a1_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e1) }); + circuit_constructor.create_add_gate( + { a2_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e2) }); + circuit_constructor.decompose_into_default_range(a1_idx, 42); + circuit_constructor.decompose_into_default_range(a2_idx, 42); + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_one_gate.size(), 2); +} + +TEST(boomerang_ultra_circuit_constructor, test_decompose_with_boolean_gates) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c1 = fr::random_element(); + auto c2 = fr::random_element(); + auto d1 = uint256_t(c1).slice(0, 41); + auto d2 = uint256_t(c2).slice(0, 41); + auto e1 = fr(d1); + auto e2 = fr(d2); + auto a1_idx = circuit_constructor.add_variable(fr(e1)); + auto a2_idx = circuit_constructor.add_variable(fr(e2)); + circuit_constructor.decompose_into_default_range(a1_idx, 42); + circuit_constructor.decompose_into_default_range(a2_idx, 42); + + for (size_t i = 0; i < 20; ++i) { + fr a = fr::zero(); + uint32_t a_idx = circuit_constructor.add_variable(a); + circuit_constructor.create_bool_gate(a_idx); + } + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_one_gate.size(), 22); +} + +TEST(boomerang_ultra_circuit_constructor, test_decompose_for_6_bit_number) +{ + UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); + auto c = fr::random_element(); + auto d = uint256_t(c).slice(0, 5); + auto e = fr(d); + auto a_idx = circuit_constructor.add_variable(fr(d)); + circuit_constructor.create_add_gate( + { a_idx, circuit_constructor.zero_idx, circuit_constructor.zero_idx, 1, 0, 0, -fr(e) }); + circuit_constructor.decompose_into_default_range(a_idx, 6); + + Graph graph = Graph(circuit_constructor); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(circuit_constructor); + EXPECT_EQ(variables_in_on_gate.size(), 1); +} diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variables_gate_counts.sha256.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variables_gate_counts.sha256.test.cpp new file mode 100644 index 00000000000..c85b25a5217 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/variables_gate_counts.sha256.test.cpp @@ -0,0 +1,180 @@ +#include "barretenberg/boomerang_value_detection/graph.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/crypto/sha256/sha256.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" +#include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" + +#include "barretenberg/numeric/bitop/rotate.hpp" +#include "barretenberg/numeric/bitop/sparse_form.hpp" +#include "barretenberg/numeric/random/engine.hpp" + +#include + +#include + +using namespace bb; +using namespace bb::stdlib; + +using Builder = UltraCircuitBuilder; + +using byte_array_ct = byte_array; +using packed_byte_array_ct = packed_byte_array; +using witness_ct = stdlib::witness_t; +using field_ct = field_t; + +bool check_in_byte_array(const uint32_t& real_var_index, const packed_byte_array_ct& byte_array) +{ + std::vector> limbs = byte_array.get_limbs(); + for (const auto& elem : limbs) { + if (elem.witness_index == real_var_index) { + return true; + } + } + return false; +} + +bool check_in_range_lists(const uint32_t& real_var_index, const uint64_t& target_range, const Builder& builder) +{ + auto range_lists = builder.range_lists; + auto target_list = range_lists[target_range]; + for (const auto elem : target_list.variable_indices) { + if (elem == real_var_index) { + return true; + } + } + return false; +} + +/** + * @brief all these tests check circuits for sha256 NIST VECTORS to find variables that won't properly constrained, + * i.e. have variable gates count = 1. Some variables can be from input/output vectors or from range_constraints, + * and they are not dangerous. + */ + +TEST(boomerang_stdlib_sha256, test_variables_gate_counts_for_sha256_55_bytes) +{ + // 55 bytes is the largest number of bytes that can be hashed in a single block, + // accounting for the single padding bit, and the 64 size bits required by the SHA-256 standard. + auto builder = Builder(); + packed_byte_array_ct input(&builder, "An 8 character password? Snow White and the 7 Dwarves.."); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + + // std::vector output = output_bits.to_unverified_byte_slices(4); + + Graph graph = Graph(builder); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(builder); + std::vector vector_variables_in_on_gate(variables_in_on_gate.begin(), variables_in_on_gate.end()); + std::sort(vector_variables_in_on_gate.begin(), vector_variables_in_on_gate.end()); + for (const auto& elem : vector_variables_in_on_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_for_sha256_NIST_vector_one) +{ + auto builder = Builder(); + packed_byte_array_ct input(&builder, "abc"); + packed_byte_array_ct output_bits = stdlib::sha256(input); + + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_for_sha256_NIST_vector_two) +{ + auto builder = Builder(); + + packed_byte_array_ct input(&builder, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_sha256_NIST_vector_three) +{ + auto builder = Builder(); + + // one byte, 0xbd + packed_byte_array_ct input(&builder, std::vector{ 0xbd }); + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +TEST(boomerang_stdlib_sha256, test_variable_gates_count_sha256_NIST_vector_four) +{ + auto builder = Builder(); + + // 4 bytes, 0xc98c8e55 + packed_byte_array_ct input(&builder, std::vector{ 0xc9, 0x8c, 0x8e, 0x55 }); + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_one_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_one_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} + +HEAVY_TEST(boomerang_stdlib_sha256, test_variable_gates_count_for_sha256_NIST_vector_five) +{ + auto builder = Builder(); + + packed_byte_array_ct input( + &builder, + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAA"); + + packed_byte_array_ct output_bits = stdlib::sha256(input); + Graph graph = Graph(builder); + std::unordered_set variables_in_on_gate = graph.show_variables_in_one_gate(builder); + for (const auto& elem : variables_in_on_gate) { + bool result1 = check_in_byte_array(elem, input); + bool result2 = check_in_byte_array(elem, output_bits); + bool result3 = check_in_range_lists(elem, 3, builder); + bool check = (result1 == 1) || (result2 == 1) || (result3 == 1); + EXPECT_EQ(check, true); + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp index 3fa8f939c25..c64afbeb10d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256_plookup.cpp @@ -279,11 +279,11 @@ std::array, 8> sha256_block(const std::array, sparse_value a = sparse_value(h_init[0]); auto b = map_into_maj_sparse_form(h_init[1]); auto c = map_into_maj_sparse_form(h_init[2]); - auto d = map_into_maj_sparse_form(h_init[3]); + sparse_value d = sparse_value(h_init[3]); sparse_value e = sparse_value(h_init[4]); auto f = map_into_choose_sparse_form(h_init[5]); auto g = map_into_choose_sparse_form(h_init[6]); - auto h = map_into_choose_sparse_form(h_init[7]); + sparse_value h = sparse_value(h_init[7]); /** * Extend witness diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp index 375211e12a0..9e26b77eb5b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/plookup/plookup.cpp @@ -48,6 +48,7 @@ plookup::ReadData> plookup_read::get_lookup_accumulato if (rhs_index == IS_CONSTANT) { key_b_witness = std::nullopt; } + const auto accumulator_witnesses = ctx->create_gates_from_plookup_accumulators(id, lookup_data, lhs_index, key_b_witness);