diff --git a/crypto3.nix b/crypto3.nix index 4881142cbd..acc179a767 100644 --- a/crypto3.nix +++ b/crypto3.nix @@ -48,6 +48,7 @@ in stdenv.mkDerivation { cd crypto3 # remove || true after all tests are fixed under clang-sanitizers check: ctest --verbose --output-on-failure -R > test_errors.txt || true + cat test_errors.txt cd .. mkdir -p ${placeholder "out"}/test-logs find .. -type f -name '*_test.xml' -exec cp {} ${placeholder "out"}/test-logs \; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp index d43e8ec496..80d67ab8ab 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_machine_interface.hpp @@ -340,10 +340,7 @@ namespace nil { word_type a = stack_pop(); word_type input_b = stack_pop(); int shift = (input_b < 256) ? int(input_b) : 256; - // TODO(ioxid): FIXME: this should be right shift - word_type r = a << shift; - // word_type b = word_type(1) << shift; - stack.push(r); + stack.push(a >> shift); pc++; gas -= 3; break; } @@ -352,7 +349,7 @@ namespace nil { word_type input_b = stack_pop(); word_type a = abs_word(input_a); int shift = (input_b < 256) ? int(input_b) : 256; - word_type r = a << shift; + word_type r = a >> shift; word_type result = is_negative(input_a) ? ((r == 0) ? neg_one : negate_word(r)) : r; stack.push(result); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/exp.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/exp.hpp new file mode 100644 index 0000000000..8656f1f9fe --- /dev/null +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/exp.hpp @@ -0,0 +1,421 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Valeh Farzaliyev +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// +// @file Declaration of interfaces for PLONK BBF exp component class +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace bbf { + + template + class exponentiation : public generic_component { + using typename generic_component::context_type; + using generic_component::allocate; + using generic_component::copy_constrain; + using generic_component::constrain; + using generic_component::lookup; + using generic_component::lookup_table; + + public: + using input_type = typename std::conditional>, std::nullptr_t>::type; + using ExpTable = exp_table; + + using typename generic_component::TYPE; + using word_type = zkevm_word_type; + using field_integral_type = typename FieldType::integral_type; + + std::size_t max_rows; + std::size_t max_exponentiations; + + + constexpr static std::size_t num_chunks = 2; + constexpr static const typename FieldType::value_type one = 1; + constexpr static const typename FieldType::value_type two_16 = 65536; + constexpr static const typename FieldType::value_type two_32 = 4294967296; + constexpr static const typename FieldType::value_type two_48 = 281474976710656; + constexpr static const typename FieldType::value_type two_64 = 0x10000000000000000_big_uint254; + constexpr static const typename FieldType::value_type two_80 = 0x100000000000000000000_big_uint254; + constexpr static const typename FieldType::value_type two_96 = 0x1000000000000000000000000_big_uint254; + constexpr static const typename FieldType::value_type two112 = 0x10000000000000000000000000000_big_uint254; + constexpr static const typename FieldType::value_type two128 = 0x100000000000000000000000000000000_big_uint254; + constexpr static const typename FieldType::value_type two192 = 0x1000000000000000000000000000000000000000000000000_big_uint254; + + template + T chunk_sum_64(const std::vector &chunks, const unsigned char chunk_idx) const { + BOOST_ASSERT(chunk_idx < 4); + return chunks[4 * chunk_idx] + chunks[4 * chunk_idx + 1] * two_16 + + chunks[4 * chunk_idx + 2] * two_32 + chunks[4 * chunk_idx + 3] * two_48; + } + + template + T chunk_sum_128(const std::vector &chunks, const unsigned char chunk_idx) const { + BOOST_ASSERT(chunk_idx < 2); + return chunks[8 * chunk_idx] + chunks[8 * chunk_idx + 1] * two_16 + + chunks[8 * chunk_idx + 2] * two_32 + chunks[8 * chunk_idx + 3] * two_48 + + chunks[8 * chunk_idx + 4] * two_64 + chunks[8 * chunk_idx + 5] * two_80 + + chunks[8 * chunk_idx + 6] * two_96 + chunks[8 * chunk_idx + 7] * two112; + } + + template + T first_carryless_consrtruct(const std::vector &a_64_chunks, + const std::vector &b_64_chunks, + const std::vector &r_64_chunks) const { + return a_64_chunks[0] * b_64_chunks[0] + + two_64 * + (a_64_chunks[0] * b_64_chunks[1] + a_64_chunks[1] * b_64_chunks[0]) - + r_64_chunks[0] - two_64 * r_64_chunks[1]; + } + + template + T second_carryless_construct(const std::vector &a_64_chunks, + const std::vector &b_64_chunks, + const std::vector &r_64_chunks) { + return (a_64_chunks[0] * b_64_chunks[2] + a_64_chunks[1] * b_64_chunks[1] + + a_64_chunks[2] * b_64_chunks[0] - r_64_chunks[2]) + + two_64 * + (a_64_chunks[0] * b_64_chunks[3] + a_64_chunks[1] * b_64_chunks[2] + + a_64_chunks[2] * b_64_chunks[1] + a_64_chunks[3] * b_64_chunks[0] - + r_64_chunks[3]); + } + + public: + + static nil::crypto3::zk::snark::plonk_table_description get_table_description( + std::size_t max_rows_amount, + std::size_t max_exponentiations_ + ){ + nil::crypto3::zk::snark::plonk_table_description desc(48 + ExpTable::get_witness_amount(), 1, 2, 13); + desc.usable_rows_amount = max_rows_amount; + return desc; + } + + exponentiation( + context_type &context_object, + const input_type &input, + std::size_t max_rows_amount, + std::size_t max_exponentiations_ + ) : + max_rows(max_rows_amount), + max_exponentiations(max_exponentiations_), + generic_component(context_object) + { + std::size_t num_proving_blocks = (max_rows) / 3; + std::vector> base = std::vector>(max_rows); + std::vector> exponent = std::vector>(max_rows); + std::vector> exponentiation = std::vector>(max_rows); + + std::vector> a_chunks = std::vector>(num_proving_blocks, std::vector(16)); + std::vector> b_chunks = std::vector>(num_proving_blocks, std::vector(16)); + std::vector> r_chunks = std::vector>(num_proving_blocks, std::vector(16)); + + std::vector> c_1_chunks = std::vector>(num_proving_blocks, std::vector(4)); + std::vector> c_3_chunks = std::vector>(num_proving_blocks, std::vector(4)); + + std::vector c_2 = std::vector(num_proving_blocks); + std::vector c_4 = std::vector(num_proving_blocks); + std::vector> itermediate_exponents = std::vector>(max_rows); + std::vector hi_last = std::vector(num_proving_blocks); + std::vector exp_is_even = std::vector(num_proving_blocks); + std::vector is_last = std::vector(max_rows); + std::vector header_selector = std::vector(max_rows); + + std::size_t current_column = 48; + std::vector exp_lookup_area; + for( std::size_t i = 0; i < ExpTable::get_witness_amount(); i++){ + exp_lookup_area.push_back(current_column++); + } + context_type exp_ct = context_object.subcontext( exp_lookup_area, 1, max_exponentiations + 1); + ExpTable exp_t = ExpTable(exp_ct, input, max_exponentiations); + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + std::size_t cur = 0; + BOOST_ASSERT(input.size() <= max_exponentiations); + for(std::size_t i = 0; i < input.size(); i++){ + word_type exp_a = input[i].first; + word_type exp_d = input[i].second; + word_type exp_A = exp_by_squaring(exp_a, exp_d); + // We don't prove zero and one exponent by lookup table + if( exp_d == 0 || exp_d == 1 ) continue; + + word_type e = exp_d; + std::vector bitmap; + std::vector tmp_exp; + while( e >= 2){ + tmp_exp.push_back(e); + if(e & 1){ + bitmap.push_back(true); + e = e - 1; + }else{ + bitmap.push_back(false); + e = e >> 1; + } + } + std::reverse(bitmap.begin(), bitmap.end()); + std::vector> intermediate_triplets; + word_type a, b; + + e = exp_a; + for(const auto &bit : bitmap){ + a = e; + if(!bit){ + b = a; + }else{ + b = exp_a; + } + e = wrapping_mul(a, b); + intermediate_triplets.push_back({a, b, e}); + } + BOOST_ASSERT(intermediate_triplets.size() == bitmap.size()); + BOOST_ASSERT(tmp_exp.size() == bitmap.size()); + + std::size_t its = intermediate_triplets.size(); + + for(std::size_t j = 0; j < its; j++){ + BOOST_ASSERT(cur < num_proving_blocks); + + header_selector[3*cur] = (j == 0) ? 1 : 0; + header_selector[3*cur + 1] = 0; + header_selector[3*cur + 2] = 0; + + base[3*cur][0] = w_hi(exp_a); + base[3*cur][1] = w_lo(exp_a); + + base[3*cur + 1][0] = base[3*cur][0]; + base[3*cur + 1][1] = base[3*cur][1]; + + base[3*cur + 2][0] = base[3*cur][0]; + base[3*cur + 2][1] = base[3*cur][1]; + + + exponent[3*cur][0] = w_hi(tmp_exp[j]); + exponent[3*cur][1] = w_lo(tmp_exp[j]); + + exponent[3*cur + 1][0] = exponent[3*cur][0]; + exponent[3*cur + 1][1] = exponent[3*cur][1]; + + exponent[3*cur + 2][0] = exponent[3*cur][0]; + exponent[3*cur + 2][1] = exponent[3*cur][1]; + + exponentiation[3*cur][0] = w_hi(intermediate_triplets[its - j - 1][2]); + exponentiation[3*cur][1] = w_lo(intermediate_triplets[its - j - 1][2]); + + exponentiation[3*cur + 1][0] = exponentiation[3*cur][0]; + exponentiation[3*cur + 1][1] = exponentiation[3*cur][1]; + + exponentiation[3*cur + 2][0] = exponentiation[3*cur][0]; + exponentiation[3*cur + 2][1] = exponentiation[3*cur][1]; + + exp_is_even[cur] = (bitmap[its - j - 1]) ? 0 : 1; + hi_last[cur] = field_integral_type(exponent[3*cur][0].data) & 1; + + is_last[3*cur] = (j == its - 1) ? 1 : 0; + is_last[3*cur + 1] = is_last[3*cur]; + is_last[3*cur + 2] = is_last[3*cur]; + + + a_chunks[cur] = zkevm_word_to_field_element(intermediate_triplets[its - j - 1][0]); + b_chunks[cur] = zkevm_word_to_field_element(intermediate_triplets[its - j - 1][1]); + r_chunks[cur] = zkevm_word_to_field_element(intermediate_triplets[its - j - 1][2]); + + std::vector a_64_chunks, b_64_chunks, r_64_chunks; + for (std::size_t k = 0; k < 4; k++) { + a_64_chunks.push_back(chunk_sum_64(a_chunks[cur], k)); + b_64_chunks.push_back(chunk_sum_64(b_chunks[cur], k)); + r_64_chunks.push_back(chunk_sum_64(r_chunks[cur], k)); + } + + auto first_row_carries = field_integral_type(first_carryless_consrtruct(a_64_chunks, b_64_chunks, r_64_chunks).data) >> 128; + TYPE c_1 = first_row_carries & field_integral_type((two_64 - 1).data); + c_2[cur] = first_row_carries >> 64; + c_1_chunks[cur] = chunk_64_to_16(c_1); + // no need for c_2 chunks as there is only a single chunk + auto second_row_carries = field_integral_type((second_carryless_construct(a_64_chunks, b_64_chunks, r_64_chunks) + c_1 + c_2[cur] * two_64).data) >> 128; + TYPE c_3 = second_row_carries & field_integral_type((two_64 - 1).data); + c_4[cur] = second_row_carries >> 64; + c_3_chunks[cur] = chunk_64_to_16(c_3); + + cur++; + } + + } + while(cur < num_proving_blocks){ + // unused rows will be filled with zeros. To satisfy all constraints + // exp_is_even is set 1 (because 0 is even) + exp_is_even[cur++] = 1; + } + } + + for(std::size_t i = 0; i < max_rows; i++){ + allocate(header_selector[i], 0, i); + + for(std::size_t j = 0; j < num_chunks; j++){ + allocate(base[i][j], j + 1, i); + allocate(exponent[i][j], num_chunks + j + 1, i); + allocate(exponentiation[i][j], 2*num_chunks + j + 1,i); + } + allocate(is_last[i], 3*num_chunks + 1, i); + } + + for(std::size_t i=0; i < num_proving_blocks; i++){ + for(std::size_t j = 0; j < 16; j++){ + allocate(r_chunks[i][j], 3*num_chunks + j + 2, 3*i); + allocate(b_chunks[i][j], 3*num_chunks + j + 2, 3*i + 1); + allocate(a_chunks[i][j], 3*num_chunks + j + 2, 3*i + 2); + } + for(std::size_t j = 0; j < 4; j++){ + allocate(c_1_chunks[i][j], 3*num_chunks + j + 18, 3*i); + allocate(c_3_chunks[i][j], 3*num_chunks + j + 18, 3*i + 1); + } + allocate(c_2[i], 3*num_chunks + 18, 3*i + 2); + allocate(c_4[i], 3*num_chunks + 19, 3*i + 2); + + allocate(exp_is_even[i], 3*num_chunks + 20, 3*i + 2); + allocate(hi_last[i], 3*num_chunks + 21, 3*i + 2); + } + + for(std::size_t i = 0; i < max_rows; i++){ + lookup(std::vector({ + header_selector[i], + header_selector[i]*base[i][0], + header_selector[i]*base[i][1], + header_selector[i]*exponent[i][0], + header_selector[i]*exponent[i][1], + header_selector[i]*exponentiation[i][0], + header_selector[i]*exponentiation[i][1] + }), "zkevm_exp"); + + constrain(header_selector[i]*(1-header_selector[i])); + constrain(is_last[i]*(1-is_last[i])); + } + + for(std::size_t i = 0; i < num_proving_blocks; i++){ + copy_constrain(base[3*i][0], base[3*i+1][0]); + copy_constrain(base[3*i][0], base[3*i+2][0]); + + copy_constrain(exponent[3*i][0], exponent[3*i+1][0]); + copy_constrain(exponent[3*i][0], exponent[3*i+2][0]); + + copy_constrain(exponentiation[3*i][0], exponentiation[3*i+1][0]); + copy_constrain(exponentiation[3*i][0], exponentiation[3*i+2][0]); + + copy_constrain(base[3*i][1], base[3*i+1][1]); + copy_constrain(base[3*i][1], base[3*i+2][1]); + + copy_constrain(exponent[3*i][1], exponent[3*i+1][1]); + copy_constrain(exponent[3*i][1], exponent[3*i+2][1]); + + copy_constrain(exponentiation[3*i][1], exponentiation[3*i+1][1]); + copy_constrain(exponentiation[3*i][1], exponentiation[3*i+2][1]); + + copy_constrain(is_last[3*i], is_last[3*i+1]); + copy_constrain(is_last[3*i], is_last[3*i+2]); + + constrain(exp_is_even[i]*(1-exp_is_even[i])); + constrain(hi_last[i]*(1-hi_last[i])); + + constrain(chunk_sum_128(r_chunks[i], 0) - exponentiation[3*i][1]); // exponent_lo = r_chunk[0:7] + constrain(chunk_sum_128(r_chunks[i], 1) - exponentiation[3*i][0]); // exponent_hi = r_chunk[8:15] + for(std::size_t j = 0; j < 16; j++){ + constrain(exp_is_even[i]*(a_chunks[i][j] - b_chunks[i][j])); // if exp is even a = b + } + constrain((1-exp_is_even[i])*(chunk_sum_128(b_chunks[i], 0) - base[3*i][1])); // if exp is odd b[0:7] = base_lo + constrain((1-exp_is_even[i])*(chunk_sum_128(b_chunks[i], 1) - base[3*i][0])); // if exp is odd b[8:15] = base_hi + + // if is_last ==> exp_is_even and exp_lo = 2, exp_lo = 0, and a = b = base + // One constraint is enough. The other can be decuded from exp_is_even => a=b constraint + constrain(is_last[3*i]*(exponent[3*i][1]- 2)); + constrain(is_last[3*i]*(exponent[3*i][0])); + constrain(is_last[3*i]*(1 - exp_is_even[i])); + constrain(is_last[3*i]*(chunk_sum_128(b_chunks[i], 1) - base[3*i][0])); + constrain(is_last[3*i]*(chunk_sum_128(b_chunks[i], 0) - base[3*i][1])); + + if( i > 1){ + // if prev_exp is odd prev_exp = curr_exp + 1 + constrain((1 - exp_is_even[i-1])*(exponent[3*i - 1][0] - exponent[3*i][0])); // that is prev_exp_hi = cur_exp_hi + constrain((1 - exp_is_even[i-1])*(exponent[3*i - 1][1] - exponent[3*i][1] - 1)); // prev_exp_lo = cur_exp_lo + 1 + // if prev_exp is even prev_exp = cur_exp * 2 except is_last = 1 + constrain((1-is_last[3*i-1])*exp_is_even[i-1]*(exponent[3*i - 1][0] - 2 * exponent[3*i][0] - hi_last[i-1])); // that is prev_exp_hi = 2* cur_exp_hi + prev_exp_hi & 2 + constrain((1-is_last[3*i-1])*exp_is_even[i-1]*(exponent[3*i - 1][1] - 2 * exponent[3*i][1] + two128*hi_last[i-1])); // prev_exp_lo = cur_exp_lo * 2 - (prev_exp_hi & 2) << 128 + } + + + std::vector a_64_chunks = { + chunk_sum_64(a_chunks[i], 0), + chunk_sum_64(a_chunks[i], 1), + chunk_sum_64(a_chunks[i], 2), + chunk_sum_64(a_chunks[i], 3) + }; + std::vector b_64_chunks = { + chunk_sum_64(b_chunks[i], 0), + chunk_sum_64(b_chunks[i], 1), + chunk_sum_64(b_chunks[i], 2), + chunk_sum_64(b_chunks[i], 3) + }; + std::vector r_64_chunks = { + chunk_sum_64(r_chunks[i], 0), + chunk_sum_64(r_chunks[i], 1), + chunk_sum_64(r_chunks[i], 2), + chunk_sum_64(r_chunks[i], 3) + }; + + TYPE c_1_64 = chunk_sum_64(c_1_chunks[i], 0); + TYPE c_3_64 = chunk_sum_64(c_3_chunks[i], 0); + TYPE first_carryless = first_carryless_consrtruct(a_64_chunks, b_64_chunks, r_64_chunks); + constrain(first_carryless - c_1_64 * two128 - c_2[i] * two192); + + TYPE second_carryless = second_carryless_construct(a_64_chunks, b_64_chunks, r_64_chunks); + constrain(second_carryless + c_1_64 + c_2[i] * two_64 - c_3_64 * two128 - c_4[i] * two192); + constrain(c_2[i] * (c_2[i] - 1)); + constrain(c_4[i] * (c_4[i] - 1) * (c_4[i] - 2) * (c_4[i] - 3)); + } + lookup_table("exp_prover", {0,1,2,3,4,5,6}, 0 ,max_rows_amount); + for( std::size_t i = 0; i < max_exponentiations; i++){ + lookup({ + exp_t.selector[i], + exp_t.selector[i] * exp_t.base_hi[i], + exp_t.selector[i] * exp_t.base_lo[i], + exp_t.selector[i] * exp_t.exponent_hi[i], + exp_t.selector[i] * exp_t.exponent_lo[i], + exp_t.selector[i] * exp_t.exponentiation_hi[i], + exp_t.selector[i] * exp_t.exponentiation_lo[i] + }, "exp_prover"); + } + } + }; + } // namespace bbf + } // namespace blueprint +} // namespace nil \ No newline at end of file diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp index 38b32053da..84b83563e4 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp @@ -110,7 +110,7 @@ namespace nil { zkevm_word_type b = stack.back(); stack.pop_back(); _rw_operations.push_back(stack_rw_operation(call_id, stack.size(), rw_counter++, false, b)); - zkevm_word_type result = (a * b); + zkevm_word_type result = wrapping_mul(a, b); _rw_operations.push_back(stack_rw_operation(call_id, stack.size(), rw_counter++, true, result)); stack.push_back(result); pc++; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/exp.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/exp.hpp index ebec8e2dbd..494c3f6679 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/exp.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/exp.hpp @@ -56,6 +56,7 @@ namespace nil { std::vector d_log(8); std::vector d_inv(8); TYPE d_sum; + TYPE r_sum; TYPE d_sum_inv; TYPE d_sum_inv_1; TYPE s; @@ -86,6 +87,7 @@ namespace nil { allocate(A[i], i, 0); allocate(D[i], i + 16, 0); allocate(R[i], i, 1); + r_sum += R[i]; } allocate(d_sum, 32, 0); allocate(d_sum_inv, 33, 0); @@ -102,6 +104,15 @@ namespace nil { constrain(d_sum * (d_sum_inv * d_sum - 1)); constrain(d_sum_inv * (d_sum_inv * d_sum - 1)); constrain(d_sum_inv_1 * (d_sum_inv_1 * (d_sum - 1) - 1)); + constrain((s - 1) * (d_sum - 1) * d_sum); // s == 0 => (d == 0 & d == 1) + // s == 0 && d == 1 => R == A + for( std::size_t i = 0; i < 16; i++){ + constrain( (s - 1) * d_sum * (R[i] - A[i])); + } + + // (s == 0 && d == 0) => R == 1 + constrain((s - 1) * (d_sum - 1) * (R[15] - 1)); + constrain((s - 1) * (d_sum - 1) * (r_sum - R[15])); auto A_128 = chunks16_to_chunks128(A); auto D_128 = chunks16_to_chunks128(D); @@ -110,6 +121,7 @@ namespace nil { constrain( (1 - (d_sum - 1) * d_sum_inv_1) * (A_128.second - R_128.second) ); constrain( (1 - (d_sum - 1) * d_sum_inv_1) * s ); + if constexpr( stage == GenerationStage::CONSTRAINTS ){ constrain(current_state.pc_next() - current_state.pc(1) - 1); // PC transition // constrain(current_state.gas(1) - current_state.gas_next() - 5); // GAS transition diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/mul.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/mul.hpp index f4a89dfa7b..536bd9743f 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/mul.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/mul.hpp @@ -103,10 +103,10 @@ namespace nil { if constexpr( stage == GenerationStage::ASSIGNMENT ){ auto a = w_to_16(current_state.stack_top()); auto b = w_to_16(current_state.stack_top(1)); - auto r = w_to_16(current_state.stack_top() * current_state.stack_top(1)); + auto r = w_to_16(wrapping_mul(current_state.stack_top(), current_state.stack_top(1))); std::cout << "\ta = " << std::hex << current_state.stack_top() << std::dec << std::endl; std::cout << "\tb = " << std::hex << current_state.stack_top(1) << std::dec << std::endl; - std::cout << "\tr = " << std::hex << current_state.stack_top() * current_state.stack_top(1) << std::dec << std::endl; + std::cout << "\tr = " << std::hex << wrapping_mul(current_state.stack_top(), current_state.stack_top(1)) << std::dec << std::endl; for( std::size_t i = 0; i < 16; i++){ A[i] = a[i]; B[i] = b[i]; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/rw.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/rw.hpp index 6cb04ed611..a744200658 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/rw.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/rw.hpp @@ -497,7 +497,7 @@ namespace nil { // field_tag is 0 // value is boolean // initial_value is 0 - // state_root eqauls state_root_prev + // state_root equals state_root_prev // value column at previous rotation equals value_prev at current rotation special_constraints[TX_ACCESS_LIST_ACCOUNT_STORAGE_OP].push_back(context_object.relativize(tx_access_list_account_selector * field_type[1], -1)); special_constraints[TX_ACCESS_LIST_ACCOUNT_STORAGE_OP].push_back(context_object.relativize(tx_access_list_account_selector * value_hi[1], -1)); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/exp_table.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/exp_table.hpp index 5e833c99a0..2c8862d011 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/exp_table.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/exp_table.hpp @@ -82,7 +82,7 @@ namespace nil { exponentiation_lo(max_exponentiations) { if constexpr (stage == GenerationStage::ASSIGNMENT) { - BOOST_ASSERT(input.size() <= max_exponentiations); + BOOST_ASSERT(input.size() < max_exponentiations); std::size_t i = 0; std::cout << "Exp table:" << std::endl; @@ -91,6 +91,8 @@ namespace nil { zkevm_word_type base = input[i].first; zkevm_word_type exponent = input[i].second; zkevm_word_type exponentiation = exp_by_squaring(base, exponent); + // We don't prove zero and one exponent by lookup table + if( exponent == 0 || exponent == 1 ) continue; std::cout <<"\t" << base << " ^ " << exponent << " = " << exponentiation << std::endl; selector[i] = 1; @@ -98,15 +100,6 @@ namespace nil { exponent_hi[i] = w_hi(exponent); exponent_lo[i] = w_lo(exponent); exponentiation_hi[i] = w_hi(exponentiation); exponentiation_lo[i] = w_lo(exponentiation); } - - // if there are unused rows, fill in with valid exp triplets (0^1 = 0) - // while (i < max_exponentiations) { - // selector[i] = 0; - // base_hi[i] = 0; base_lo[i] = 0; - // exponent_hi[i] = 0; exponent_lo[i] = 0; - // exponentiation_hi[i] = 0; exponentiation_lo[i] = 0; - // i++; - // } } for (std::size_t i = 0; i < max_exponentiations; i++) { diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/zkevm.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/zkevm.hpp index 45420f5052..c90c6f0eb8 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/zkevm.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/zkevm.hpp @@ -77,7 +77,7 @@ namespace nil { std::size_t max_zkevm_rows, std::size_t max_copy, std::size_t max_rw, - std::size_t max_keccak_blocks, + std::size_t max_exponentations, std::size_t max_bytecode ){ std::size_t implemented_opcodes_amount = get_implemented_opcodes_list().size(); @@ -89,7 +89,7 @@ namespace nil { witness_amount += CopyTable::get_witness_amount(); witness_amount += 10; nil::crypto3::zk::snark::plonk_table_description desc(witness_amount, 1, 5, 20); - desc.usable_rows_amount = std::max(max_zkevm_rows, std::max(std::max(max_copy, max_rw), std::max(max_keccak_blocks, max_bytecode)) + 1); + desc.usable_rows_amount = std::max(max_zkevm_rows, std::max(std::max(max_copy, max_rw), std::max(max_exponentations, max_bytecode)) + 1); return desc; } @@ -99,7 +99,7 @@ namespace nil { std::size_t max_zkevm_rows, std::size_t max_copy, std::size_t max_rw, - std::size_t max_keccak_blocks, + std::size_t max_exponentations, std::size_t max_bytecode, std::size_t max_exponentiations = 50 // TODO:remove it later ) :generic_component(context_object), implemented_opcodes(get_implemented_opcodes_list()) { @@ -527,7 +527,7 @@ namespace nil { tmp[3] = context_object.relativize(evm_opcode_constraint, -1); tmp[4] = context_object.relativize(evm_opcode_constraint * all_states[1].bytecode_hash_hi, -1); tmp[5] = context_object.relativize(evm_opcode_constraint * all_states[1].bytecode_hash_lo, -1); - + // TODO(oclaw): bytecode check is disabled since hash algorithm for circuits is not finalized yet // https://github.com/NilFoundation/placeholder/issues/205 // context_object.relative_lookup(tmp, "zkevm_bytecode", 1, max_zkevm_rows-1); diff --git a/crypto3/libs/blueprint/test/CMakeLists.txt b/crypto3/libs/blueprint/test/CMakeLists.txt index 0c6b7f321d..9079c2a4e7 100644 --- a/crypto3/libs/blueprint/test/CMakeLists.txt +++ b/crypto3/libs/blueprint/test/CMakeLists.txt @@ -238,6 +238,7 @@ set(ZKEVM_BBF_TESTS_FILES "zkevm_bbf/rw" "zkevm_bbf/bytecode" "zkevm_bbf/copy" + "zkevm_bbf/exp" "zkevm_bbf/opcodes/pushx" "zkevm_bbf/opcodes/iszero" "zkevm_bbf/opcodes/mod_ops" diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/exp.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/exp.cpp new file mode 100644 index 0000000000..e8964b55ba --- /dev/null +++ b/crypto3/libs/blueprint/test/zkevm_bbf/exp.cpp @@ -0,0 +1,87 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_exp_test + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "./test_l1_wrapper.hpp" + +using namespace nil::crypto3; +using namespace nil::blueprint; + +class zkEVMExpTestFixture: public BBFTestFixture { +public: + zkEVMExpTestFixture():BBFTestFixture(){} + + template + void test_zkevm_exp( + const std::vector> &exps, + std::size_t max_exponentiations, + std::size_t max_exp_rows, + bool expected_result = true + ){ + typename nil::blueprint::bbf::exponentiation::input_type exp_assignment_input = exps; + typename nil::blueprint::bbf::exponentiation::input_type exp_constraint_input; + + bool result = test_bbf_component( + "exp", + {} , // Public input + exp_assignment_input, // Assignment input + exp_constraint_input, // Circuit input + max_exp_rows, // Maximum size of exp circuit + max_exponentiations // Maximum rows in exponentiation dynamic lookup table + ); + BOOST_CHECK((!check_satisfiability && !generate_proof) || result == expected_result); // Max_rw, Max_mpt + } +}; + +BOOST_FIXTURE_TEST_SUITE(zkevm_bbf_exp, zkEVMExpTestFixture) + using field_type = nil::crypto3::algebra::curves::alt_bn128_254::scalar_field_type; +BOOST_AUTO_TEST_CASE(one_exponent){ + std::vector> exp_input; + exp_input.push_back({2, 2}); + exp_input.push_back({3, 3}); + test_zkevm_exp(exp_input, 50, 3000); +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/hardhat.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/hardhat.cpp index 610d2db759..175409e57d 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/hardhat.cpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/hardhat.cpp @@ -47,12 +47,13 @@ #include #include -#include + #include #include #include #include #include +#include #include "./test_l1_wrapper.hpp" @@ -85,6 +86,8 @@ class zkEVMHardhatTestFixture: public BBFTestFixture { std::size_t max_rw = max_sizes.max_rw; std::size_t max_copy = max_sizes.max_copy; std::size_t max_zkevm_rows = max_sizes.max_zkevm_rows; + std::size_t max_exponentiations = max_sizes.max_exponentiations; + std::size_t max_exp_rows = max_sizes.max_exp_rows; typename nil::blueprint::bbf::copy::input_type copy_assignment_input; typename nil::blueprint::bbf::copy::input_type copy_constraint_input; @@ -118,6 +121,10 @@ class zkEVMHardhatTestFixture: public BBFTestFixture { bytecode_assignment_input.keccak_buffers = circuit_inputs.keccaks(); bool result; + typename nil::blueprint::bbf::exponentiation::input_type exp_assignment_input; + typename nil::blueprint::bbf::exponentiation::input_type exp_constraint_input; + exp_assignment_input = circuit_inputs.exponentiations(); + // Max_rows, max_bytecode, max_rw result = test_bbf_component( "zkevm", @@ -125,10 +132,20 @@ class zkEVMHardhatTestFixture: public BBFTestFixture { max_zkevm_rows, max_copy, max_rw, - max_keccak_blocks, + max_exponentiations, max_bytecode ); BOOST_ASSERT(result); + // std::cout << std::endl; + + // Max_copy, Max_rw, Max_keccak, Max_bytecode + result =test_bbf_component( + "exp", + {}, exp_assignment_input, exp_constraint_input, + max_exp_rows, + max_exponentiations + ); + BOOST_ASSERT(result); std::cout << std::endl; // Max_copy, Max_rw, Max_keccak, Max_bytecode @@ -184,6 +201,7 @@ BOOST_AUTO_TEST_CASE(minimal_math) { max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 500; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 500; complex_test(bytecodes, pts, max_sizes); } @@ -200,6 +218,7 @@ BOOST_AUTO_TEST_CASE(modular_operations) { max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 500; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 500; complex_test(bytecodes, pts, max_sizes); } @@ -215,6 +234,7 @@ BOOST_AUTO_TEST_CASE(exp) { max_sizes.max_rw = 1000; max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 2000; + max_sizes.max_exp_rows = 500; max_sizes.max_exponentiations = 50; complex_test(bytecodes, pts, max_sizes); @@ -232,6 +252,7 @@ BOOST_AUTO_TEST_CASE(keccak) { max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 500; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 500; complex_test(bytecodes, pts, max_sizes); } @@ -248,6 +269,7 @@ BOOST_AUTO_TEST_CASE(mstore8) { max_sizes.max_copy = 3000; max_sizes.max_zkevm_rows = 4500; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 500; complex_test(bytecodes, pts, max_sizes); } @@ -264,6 +286,7 @@ BOOST_AUTO_TEST_CASE(meminit) { max_sizes.max_copy = 3000; max_sizes.max_zkevm_rows = 10000; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 500; complex_test(bytecodes, pts, max_sizes); } @@ -280,7 +303,10 @@ BOOST_AUTO_TEST_CASE(calldatacopy) { max_sizes.max_copy = 3000; max_sizes.max_zkevm_rows = 4500; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 500; complex_test(bytecodes, pts, max_sizes); } + + BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/exp.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/exp.cpp index e50bd218d7..09d144f296 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/exp.cpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/exp.cpp @@ -83,7 +83,8 @@ BOOST_AUTO_TEST_CASE(exp) { opcode_tester.push_opcode(zkevm_opcode::PUSH32, 2); opcode_tester.push_opcode( zkevm_opcode::PUSH32, - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_big_uint256); + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_big_uint256 + ); opcode_tester.push_opcode(zkevm_opcode::EXP); opcode_tester.push_opcode( zkevm_opcode::PUSH32, @@ -126,6 +127,7 @@ BOOST_AUTO_TEST_CASE(exp) { max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 300; max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 10000; complex_opcode_test(opcode_tester, max_sizes); } BOOST_AUTO_TEST_SUITE_END() diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/opcode_test_fixture.hpp b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/opcode_test_fixture.hpp index d9c197906e..061fed26a3 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/opcode_test_fixture.hpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/opcode_test_fixture.hpp @@ -46,6 +46,13 @@ #include #include +#include +#include +#include +#include +#include +#include + #include "../test_l1_wrapper.hpp" using namespace nil::crypto3; @@ -74,6 +81,8 @@ class zkEVMOpcodeTestFixture: public BBFTestFixture { std::size_t max_rw = max_sizes.max_rw; std::size_t max_copy = max_sizes.max_copy; std::size_t max_zkevm_rows = max_sizes.max_zkevm_rows; + std::size_t max_exponentiations = max_sizes.max_exponentiations; + std::size_t max_exp_rows = max_sizes.max_exp_rows; typename nil::blueprint::bbf::copy::input_type copy_assignment_input; typename nil::blueprint::bbf::copy::input_type copy_constraint_input; @@ -105,6 +114,12 @@ class zkEVMOpcodeTestFixture: public BBFTestFixture { bytecode_assignment_input.rlc_challenge = 7; bytecode_assignment_input.bytecodes = circuit_inputs.bytecodes(); bytecode_assignment_input.keccak_buffers = circuit_inputs.keccaks(); + + + typename nil::blueprint::bbf::exponentiation::input_type exp_assignment_input; + typename nil::blueprint::bbf::exponentiation::input_type exp_constraint_input; + exp_assignment_input = circuit_inputs.exponentiations(); + bool result; // Max_rows, max_bytecode, max_rw @@ -114,12 +129,21 @@ class zkEVMOpcodeTestFixture: public BBFTestFixture { max_zkevm_rows, max_copy, max_rw, - max_keccak_blocks, + max_exponentiations, max_bytecode ); BOOST_CHECK(result); std::cout << std::endl; + result = test_bbf_component( + "exp", + {}, exp_assignment_input, exp_constraint_input, + max_exp_rows, + max_exponentiations + ); + BOOST_CHECK(result); + std::cout << std::endl; + // Max_bytecode, max_bytecode std::cout << "Bytecode circuit" << std::endl; result = test_bbf_component( diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/pushx.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/pushx.cpp index b8e6f2f4ec..772f5d51c3 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/pushx.cpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/pushx.cpp @@ -56,7 +56,7 @@ using namespace nil::crypto3; using namespace nil::blueprint::bbf; -BOOST_FIXTURE_TEST_SUITE(zkevm_opcode_test_suite, zkEVMOpcodeTestFixture) +BOOST_FIXTURE_TEST_SUITE(zkevm_opcode_bbf_test_suite, zkEVMOpcodeTestFixture) BOOST_AUTO_TEST_CASE(pushx_strings) { using field_type = typename algebra::curves::pallas::base_field_type; zkevm_opcode_tester opcode_tester; @@ -104,6 +104,8 @@ BOOST_AUTO_TEST_CASE(pushx_strings) { max_sizes.max_rw = 500; max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 100; + max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 100; complex_opcode_test(opcode_tester, max_sizes); } @@ -176,6 +178,8 @@ BOOST_AUTO_TEST_CASE(pushx) { max_sizes.max_rw = 500; max_sizes.max_copy = 500; max_sizes.max_zkevm_rows = 100; + max_sizes.max_exponentiations = 50; + max_sizes.max_exp_rows = 100; complex_opcode_test(opcode_tester, max_sizes); } diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/test_l1_wrapper.hpp b/crypto3/libs/blueprint/test/zkevm_bbf/test_l1_wrapper.hpp index 4c20d606f9..e9ad5e92e5 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/test_l1_wrapper.hpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/test_l1_wrapper.hpp @@ -46,11 +46,7 @@ #include #include #include -#include -#include -#include -#include -#include +#include #include "../test_plonk_component.hpp" @@ -66,6 +62,7 @@ struct l1_size_restrictions{ std::size_t max_rw; std::size_t max_copy; std::size_t max_zkevm_rows; + std::size_t max_exp_rows; }; std::vector hex_string_to_bytes(std::string const &hex_string) {