diff --git a/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp b/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp index ead2c80e7..6c80260f5 100644 --- a/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp +++ b/libs/blueprint/include/nil/blueprint/utils/satisfiability_check.hpp @@ -131,6 +131,10 @@ namespace nil { if (!constraint_result.is_zero()) { std::cout << "Constraint " << j << " from gate " << i << " on row " << selector_row << " is not satisfied." << std::endl; + for(std::size_t k = 0; k < assignments.rows_amount(); k++){ + std::cout << gates[i].constraints[j].evaluate(k, assignments) << " "; + } + std::cout << std::endl; std::cout << "Constraint: " << gates[i].constraints[j] << std::endl; std::cout << "Constraint result: " << constraint_result << std::endl; std::cout << "Offending gate:" << std::endl; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/index_selector.hpp b/libs/blueprint/include/nil/blueprint/zkevm/index_selector.hpp new file mode 100644 index 000000000..77100fc46 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/index_selector.hpp @@ -0,0 +1,255 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Dmitrii Tabalin +// Copyright (c) 2024 Alexey Yashunsky +// +// 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. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace nil { + namespace blueprint { + namespace components { + // activates a witness column based on an input value + // is used to achieve dynamic selector behavior + // actual implementation + template + class index_selector; + + template + class index_selector, + BlueprintFieldType> + : public plonk_component { + + public: + using component_type = plonk_component; + + using var = typename component_type::var; + using manifest_type = plonk_component_manifest; + using constraint_type = crypto3::zk::snark::plonk_constraint; + using state_var = state_var; + + std::size_t options_amount; + + class gate_manifest_type : public component_gate_manifest { + private: + std::size_t options_amount; + + public: + gate_manifest_type(std::size_t options_amount_) : + options_amount(options_amount_) {}; + + bool operator<(gate_manifest_type const& other) const { + return (options_amount < other.options_amount); + } + + std::uint32_t gates_amount() const override { + return index_selector::gates_amount; + } + }; + + static gate_manifest get_gate_manifest( + std::size_t options_amount + ) { + gate_manifest manifest = gate_manifest(gate_manifest_type(options_amount)); + return manifest; + } + + static manifest_type get_manifest(std::size_t options_amount) { + manifest_type manifest = manifest_type( + // TODO: make the manifest depend on options_amount + // this requires the manifest rework + std::shared_ptr(new manifest_single_value_param( + options_amount + )), + false + ); + return manifest; + } + + constexpr static std::size_t get_rows_amount( + std::size_t options_amount + ) { + return 1; + } + + constexpr static const std::size_t gates_amount = 1; + const std::size_t rows_amount = get_rows_amount(options_amount); + const std::string component_name = "index selector component"; + + struct input_type { + std::size_t index; + std::vector> all_vars() { + return {}; + } + }; + + struct result_type { + result_type(const index_selector &component, std::size_t start_row_index) {} + std::vector> all_vars() { + return {}; + } + }; + + template + explicit index_selector(ContainerType witness, std::size_t options_amount_) : + component_type(witness, {}, {}, get_manifest(options_amount_)), + options_amount(options_amount_){ + BOOST_ASSERT(this->witness_amount() >= options_amount); + }; + + template + index_selector( + WitnessContainerType witness, ConstantContainerType constant, + PublicInputContainerType public_input, std::size_t options_amount_) : + component_type(witness, constant, public_input, get_manifest(options_amount_)), + options_amount(options_amount_){ + BOOST_ASSERT(this->witness_amount() >= options_amount); + }; + + index_selector( + std::initializer_list witnesses, + std::initializer_list + constants, + std::initializer_list + public_inputs, + std::size_t options_amount_, bool is_compressed_ = false) : + component_type(witnesses, constants, public_inputs, get_manifest(options_amount_)), + options_amount(options_amount_){ + BOOST_ASSERT(this->witness_amount() >= this->options_amount); + }; + + + // Here we only check that all variables are zeroes and ones, and their sum is 1 + std::vector generate_constraints() const { + std::cout << "Index selector generate_constraints options_amount = " << this->options_amount << std::endl; + std::vector constraints; + + std::size_t option_cells_amount = (this->options_amount + 1)/2, + option_WA = this->witness_amount() - 1; + for (std::size_t i = 0; i < options_amount; i++) { + var curr_var = var(this->W(i),0); + constraints.push_back(curr_var * (curr_var - 1)); + } + return constraints; + } + + // Allows conveniently connect sum_constraints from different areas; + constraint_type sum_constraint(std::size_t rotation = 0){ + constraint_type sum_to_one; + for (std::size_t i = 0; i < options_amount; i++) { + var curr_var = var(this->W(i), rotation); + sum_to_one += curr_var; + } + return sum_to_one; + } + + constraint_type index_constraint(std::size_t rotation = 0){ + constraint_type compose_constraint; + for (std::size_t i = 0; i < options_amount; i++){ + var curr_var = var(this->W(i),rotation); + compose_constraint += i * curr_var; + } + return compose_constraint; + } + + state_var index(std::size_t i){ + return state_var(this->W(i)); + } + }; + + template + using plonk_index_selector = + index_selector, + BlueprintFieldType>; + + template + typename plonk_index_selector::result_type generate_assignments( + const plonk_index_selector &component, + assignment> + &assignment, + const typename plonk_index_selector::input_type + &instance_input, + const std::uint32_t start_row_index) { + + using component_type = plonk_index_selector; + using value_type = typename BlueprintFieldType::value_type; + using integral_type = typename BlueprintFieldType::integral_type; + + std::size_t index = instance_input.index; + //if( index >= component.options_amount ) std::cout << index << ">=" << component.options_amount << std::endl; + BOOST_ASSERT(index < component.options_amount); + + // calculating this is somehow very unintuitive + std::size_t option_WA = component.witness_amount() - 1; + + const integral_type parity = index & 1; // index%2 + + for (std::size_t i = 1; i < component.witness_amount() - 1; i++) { // zerofy all + assignment.witness(component.W(i), start_row_index) = 0; + } + assignment.witness(component.W(index), start_row_index) = 1; + + return typename component_type::result_type(component, start_row_index); + } + + template + std::size_t generate_gates( + const plonk_index_selector &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_index_selector::input_type + &instance_input) { + + return bp.add_gate(component.generate_constraints()); + } + + template + typename plonk_index_selector::result_type generate_circuit( + const plonk_index_selector &component, + circuit> &bp, + assignment> + &assignment, + const typename plonk_index_selector::input_type + &instance_input, + const std::size_t start_row_index) { + + using component_type = plonk_index_selector; + using state_var = state_var; + + std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); + assignment.enable_selector(selector_index, start_row_index, start_row_index); + + return typename component_type::result_type(component, start_row_index); + } + + } // namespace components + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp index 56c4ffd85..0e6727e99 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/add_sub.hpp @@ -49,7 +49,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_add_sub_operation(bool _is_add) : is_add(_is_add) {} + zkevm_add_sub_operation(bool _is_add) : is_add(_is_add) { + this->stack_input = 2; + this->stack_output = 1; + } bool is_add; @@ -126,8 +129,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; word_type a = stack.pop(); word_type b = stack.pop(); diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/addmod.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/addmod.hpp index f29294c07..f89a645f0 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/addmod.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/addmod.hpp @@ -56,6 +56,12 @@ namespace nil { constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + zkevm_addmod_operation(){ + this->stack_input = 3; + this->stack_output = 1; + this->gas_cost = 8; + } + template T chunk_sum_64(const std::vector &chunks, const unsigned char chunk_idx) const { BOOST_ASSERT(chunk_idx < 4); @@ -318,12 +324,12 @@ namespace nil { return { {gate_class::MIDDLE_OP, {constraints, {} }} }; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; - zkevm_stack &stack = machine.stack; + zkevm_stack stack = machine.stack; word_type a = stack.pop(); word_type b = stack.pop(); diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/bitwise.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/bitwise.hpp index 802acba71..4d27dbf37 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/bitwise.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/bitwise.hpp @@ -50,7 +50,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_bitwise_operation(bitwise_type _bit_operation) : bit_operation(_bit_operation) {} + zkevm_bitwise_operation(bitwise_type _bit_operation) : bit_operation(_bit_operation) { + this->stack_input = 2; + this->stack_output = 1; + } bitwise_type bit_operation; @@ -100,8 +103,8 @@ namespace nil { return { {gate_class::MIDDLE_OP, {constraints, lookup_constraints}} }; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = typename BlueprintFieldType::integral_type; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/byte.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/byte.hpp index dbd189452..a42beecc5 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/byte.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/byte.hpp @@ -48,6 +48,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; + zkevm_byte_operation(){ + this->stack_input = 2; + this->stack_output = 1; + } + std::map>, std::vector> @@ -124,8 +129,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, lookup_constraints}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/calldataload.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/calldataload.hpp index fb199f7bd..475593bed 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/calldataload.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/calldataload.hpp @@ -48,7 +48,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_calldataload_operation() {} + zkevm_calldataload_operation() { + this->stack_input = 1; + this->stack_output = 1; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,8 +69,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for CALLDATALOAD" << std::endl; stack.push(0); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/calldatasize.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/calldatasize.hpp index 71a61ae88..902edfa0a 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/calldatasize.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/calldatasize.hpp @@ -48,7 +48,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_calldatasize_operation() {} + zkevm_calldatasize_operation() { + this->stack_input = 0; + this->stack_output = 1; + this->gas_cost = 2; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,8 +70,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for CALLDATASIZE" << std::endl; stack.push(0); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/callvalue.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/callvalue.hpp index 6e9ffd95b..095496aa4 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/callvalue.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/callvalue.hpp @@ -48,7 +48,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_callvalue_operation() {} + zkevm_callvalue_operation() { + this->stack_input = 0; + this->stack_output = 1; + this->gas_cost = 2; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,14 +70,14 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for CALLVALUE" << std::endl; stack.push(0); } std::size_t rows_amount() override { - return 3; + return 1; } }; } // namespace blueprint diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/cmp.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/cmp.hpp index b1cc06ea5..e94a7a986 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/cmp.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/cmp.hpp @@ -50,7 +50,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_cmp_operation(cmp_type _cmp_operation) : cmp_operation(_cmp_operation) {} + zkevm_cmp_operation(cmp_type _cmp_operation) : cmp_operation(_cmp_operation) { + this->stack_input = 2; + this->stack_output = 1; + } cmp_type cmp_operation; @@ -170,8 +173,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/div_mod.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/div_mod.hpp index 0e1ca9661..5ed3a04cd 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/div_mod.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/div_mod.hpp @@ -49,7 +49,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_div_mod_operation(bool _is_div) : is_div(_is_div) {} + zkevm_div_mod_operation(bool _is_div) : is_div(_is_div) { + this->stack_input = 2; + this->stack_output = 1; + this->gas_cost = 5; + } bool is_div; @@ -287,9 +291,9 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { using word_type = typename zkevm_stack::word_type; - zkevm_stack &stack = machine.stack; + zkevm_stack stack = machine.stack; word_type a = stack.pop(); word_type b = stack.pop(); using integral_type = boost::multiprecision::number< diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/dupx.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/dupx.hpp index 47285339b..d1c8da34a 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/dupx.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/dupx.hpp @@ -50,6 +50,8 @@ namespace nil { zkevm_dupx_operation(std::size_t _x) : byte_count(_x) { BOOST_ASSERT(_x <= 16); // the maximum possible dup + this->stack_input = _x; + this->stack_output = _x+1; } std::size_t byte_count; @@ -63,8 +65,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override{ - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override{ + zkevm_stack stack = machine.stack; std::cout << "Generate assignments for DUPx opcodes" << std::endl; stack.push(0); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/err0.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/err0.hpp index 0b4a0443f..1f114d800 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/err0.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/err0.hpp @@ -46,6 +46,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; + zkevm_err0_operation(){ + this->gas_cost = 0; + } + std::map>, std::vector> @@ -123,10 +127,10 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine, + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine, zkevm_word_type opcode_num) { - zkevm_stack &stack = machine.stack; + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number>; using circuit_integral_type = typename BlueprintFieldType::integral_type; @@ -176,7 +180,7 @@ namespace nil { assignment.witness(witness_cols[2], curr_row + 1) = d2; assignment.witness(witness_cols[3], curr_row + 1) = so; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { generate_assignments(zkevm_table, machine, 0); // just to have a default } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp index d6c18e158..46d8dca60 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/iszero.hpp @@ -49,7 +49,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_iszero_operation() = default; + zkevm_iszero_operation() { + this->stack_input = 1; + this->stack_output = 1; + }; std::map>, @@ -83,8 +86,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; word_type a = stack.pop(); const std::vector chunks = zkevm_word_to_field_element(a); diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/jump.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/jump.hpp index f9265e02f..1d995aaa7 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/jump.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/jump.hpp @@ -39,7 +39,7 @@ namespace nil { class zkevm_table; template - class zkevm_jumpi_operation : public zkevm_operation { + class zkevm_jump_operation : public zkevm_operation { public: using op_type = zkevm_operation; using gate_class = typename op_type::gate_class; @@ -50,8 +50,13 @@ namespace nil { using assignment_type = typename op_type::assignment_type; using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; + using state_var = state_var; - zkevm_jumpi_operation() {} + zkevm_jump_operation() { + this->stack_input = 1; + this->stack_output = 0; + this->gas_cost = 8; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -63,63 +68,35 @@ namespace nil { std::map>, std::vector> - >> - generate_gates(zkevm_circuit_type &zkevm_circuit) override { - // TODO : generate gates + >> generate_gates(zkevm_circuit_type &zkevm_circuit) override { + // TODO: + // Lookup to RW stack + // Lookup to bytecode JUMPDEST's return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; - std::cout << "Generate assignments and gates for JUMPI" << std::endl; - stack.pop(); - stack.pop(); - } + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + std::cout << "Generate assignments for JUMP" << std::endl; + assignment_type &assignment = zkevm_table.get_assignment(); + const std::size_t curr_row = zkevm_table.get_current_row(); + auto witness_cols = zkevm_table.get_opcode_cols(); - std::size_t rows_amount() override { - return 3; + zkevm_word_type dest = machine.stack.top(); + std::cout << "JUMP assign destination = " << dest << std::endl; + assignment.witness(witness_cols[0], curr_row) = w_lo(dest); } - }; - - template - class zkevm_jump_operation : public zkevm_operation { - public: - using op_type = zkevm_operation; - using gate_class = typename op_type::gate_class; - using constraint_type = typename op_type::constraint_type; - using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; - using zkevm_circuit_type = typename op_type::zkevm_circuit_type; - using zkevm_table_type = typename op_type::zkevm_table_type; - using assignment_type = typename op_type::assignment_type; - using value_type = typename BlueprintFieldType::value_type; - using var = typename op_type::var; - - zkevm_jump_operation() {} - constexpr static const value_type two_16 = 65536; - constexpr static const value_type two_32 = 4294967296; - constexpr static const value_type two_48 = 281474976710656; - constexpr static const value_type two_64 = 0x10000000000000000_cppui_modular254; - constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; - constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + virtual constraint_type pc_transition(const zkevm_circuit_type &zkevm_circuit) override{ + auto witness_cols = zkevm_circuit.get_opcode_cols(); + const auto &state = zkevm_circuit.get_state(); + constraint_type c; - std::map>, - std::vector> - >> - generate_gates(zkevm_circuit_type &zkevm_circuit) override { - // TODO : generate gates - return {{gate_class::MIDDLE_OP, {{}, {}}}}; - } - - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; - std::cout << "Generate assignments and gates for JUMP" << std::endl; - stack.pop(); + c = state.pc.next() - var(witness_cols[0], -1); + return c; } std::size_t rows_amount() override { - return 3; + return 1; } }; @@ -136,7 +113,9 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_jumpdest_operation() {} + zkevm_jumpdest_operation() { + this->gas_cost = 1; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -154,13 +133,13 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for JUMPDEST" << std::endl; } std::size_t rows_amount() override { - return 3; + return 1; } }; } // namespace blueprint diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/jumpi.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/jumpi.hpp new file mode 100644 index 000000000..d91ccc211 --- /dev/null +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/jumpi.hpp @@ -0,0 +1,157 @@ +//---------------------------------------------------------------------------// +// 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. +//---------------------------------------------------------------------------// + +#pragma once + +#include +#include + +#include +#include + +namespace nil { + namespace blueprint { + template + class zkevm_operation; + + template + class zkevm_table; + + template + class zkevm_jumpi_operation : public zkevm_operation { + public: + using op_type = zkevm_operation; + using gate_class = typename op_type::gate_class; + using constraint_type = typename op_type::constraint_type; + using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; + using zkevm_circuit_type = typename op_type::zkevm_circuit_type; + using zkevm_table_type = typename op_type::zkevm_table_type; + using assignment_type = typename op_type::assignment_type; + using value_type = typename BlueprintFieldType::value_type; + using var = typename op_type::var; + using state_var = state_var; + + zkevm_jumpi_operation() { + this->stack_input = 2; + this->stack_output = 0; + this->gas_cost = 10; + } + + constexpr static const value_type two_16 = 65536; + constexpr static const value_type two_32 = 4294967296; + constexpr static const value_type two_48 = 281474976710656; + constexpr static const value_type two_64 = 0x10000000000000000_cppui_modular254; + constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; + constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + + // Table layout Row # + // +------------------+-+----------------+---+--------------+ + // | condition |r| |1/A| dest | | 0 + // +------------------+-+----------------+---+--------------+ + + struct jumpi_map{ + jumpi_map(std::vector W, std::size_t range_checked_cols_amount = 32){ + for(std::size_t i = 0; i < 16; i++){ + chunks[i] = var(W[i],0); // No rotations used, so, use just var instead of state_var + } + non_zero = state_var(W[17]); + dest = state_var(W[18]); + + // dest will be range checked by the bytecode table. + // but range_checks are free. So we place it to range check columns + s_inv = state_var(W[range_checked_cols_amount]); + } + std::array chunks; + state_var non_zero; + state_var s_inv; + state_var dest; + }; + + std::map>, + std::vector> + >> generate_gates(zkevm_circuit_type &zkevm_circuit) override { + // TODO : add lookups + // 2 lookups to RW circuit + // Lookup to bytecode with JUMPDEST + + auto witness_cols = zkevm_circuit.get_opcode_cols(); + std::size_t range_checked_cols_amount = zkevm_circuit.get_opcode_range_checked_cols_amount(); + jumpi_map m(witness_cols, range_checked_cols_amount); + + std::vector> constraints; + + // May be checked not all chunks but lower for example + // Should be checked for EVM + constraint_type sum_constraint; + for(std::size_t i = 0; i < m.chunks.size(); i++){ + sum_constraint += m.chunks[i]; + } + constraints.push_back({0, m.non_zero() * 1 - m.non_zero()}); + constraints.push_back({0, m.non_zero() - sum_constraint * m.s_inv()}); + return {{gate_class::MIDDLE_OP, {constraints, {}}}}; + } + + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_word_type dest = machine.stack.top(); + zkevm_word_type condition = machine.stack.top(1); + const std::vector chunks = zkevm_word_to_field_element(condition); + + assignment_type &assignment = zkevm_table.get_assignment(); + const std::size_t curr_row = zkevm_table.get_current_row(); + auto witness_cols = zkevm_table.get_opcode_cols(); + std::size_t range_checked_cols_amount = zkevm_table.get_opcode_range_checked_cols_amount(); + + jumpi_map m(witness_cols, range_checked_cols_amount); + + // TODO: replace with memory access, which would also do range checks! + value_type c = 0; + for (std::size_t i = 0; i < chunks.size(); i++) { + assignment.witness(m.chunks[i].index, curr_row) = chunks[i]; + c += chunks[i]; + } + assignment.witness(m.non_zero.index, curr_row) = (c != 0); + assignment.witness(m.s_inv.index, curr_row) = (c==0 ? 0 : c.inversed()); + assignment.witness(m.dest.index, curr_row) = w_lo(dest); + } + + virtual constraint_type pc_transition(const zkevm_circuit_type &zkevm_circuit) override{ + // pc_transition switched on opcode's last row. All meaningful data is placed on 0-th row. + // So, we'll have -1 rotation + auto witness_cols = zkevm_circuit.get_opcode_cols(); + std::size_t range_checked_cols_amount = zkevm_circuit.get_opcode_range_checked_cols_amount(); + const auto &state = zkevm_circuit.get_state(); + constraint_type c; + + jumpi_map m(witness_cols, range_checked_cols_amount); + c = state.pc.next() - m.non_zero.prev() * m.dest.prev() - (1 - m.non_zero.prev()) * (state.pc() + 1); + return c; + } + + std::size_t rows_amount() override { + return 1; + } + }; + } // namespace blueprint +} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/memory.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/memory.hpp index 77e06217a..6ea081815 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/memory.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/memory.hpp @@ -48,7 +48,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_mstore_operation() {} + zkevm_mstore_operation() { + this->stack_input = 2; + this->stack_output = 0; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,16 +69,19 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { std::cout << "Generate assignments and gates for MSTORE" << std::endl; - stack.pop(); - stack.pop(); } std::size_t rows_amount() override { return 3; } + + virtual constraint_type gas_transition(const zkevm_circuit_type &zkevm_circuit) override { + std::cout << "Implement MLOAD dynamic gas transition" << std::endl; + constraint_type c; + return c; + } }; @@ -92,7 +98,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_mload_operation() {} + zkevm_mload_operation() { + this->stack_input = 1; + this->stack_output = 1; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -110,12 +119,18 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for MLOAD" << std::endl; stack.pop(); } + virtual constraint_type gas_transition(const zkevm_circuit_type &zkevm_circuit) override { + std::cout << "Implement MLOAD dynamic gas transition" << std::endl; + constraint_type c; + return c; + } + std::size_t rows_amount() override { return 3; } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp index 5f3c8c19e..066e42ac5 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/mul.hpp @@ -49,7 +49,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_mul_operation() {} + zkevm_mul_operation() { + this->stack_input = 2; + this->stack_output = 1; + this->gas_cost = 5; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -152,8 +156,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; word_type a = stack.pop(); word_type b = stack.pop(); diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/mulmod.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/mulmod.hpp index 84327a7ae..1df0138aa 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/mulmod.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/mulmod.hpp @@ -56,6 +56,12 @@ namespace nil { constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + zkevm_mulmod_operation(){ + this->stack_input = 3; + this->stack_output = 1; + this->gas_cost = 8; + } + template T chunk_sum_64(const std::vector &chunks, const unsigned char chunk_idx) const { BOOST_ASSERT(chunk_idx < 8); // corrected to allow 512-bit numbers @@ -447,12 +453,12 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number>; using extended_integral_type = boost::multiprecision::number>; - zkevm_stack &stack = machine.stack; + zkevm_stack stack = machine.stack; word_type input_a = stack.pop(); word_type b = stack.pop(); diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/not.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/not.hpp index 38b0cad35..5d73a4e4e 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/not.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/not.hpp @@ -48,7 +48,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_not_operation() {} + zkevm_not_operation() { + this->stack_input = 1; + this->stack_output = 1; + } constexpr static const std::size_t carry_amount = 16 / 3 + 1; constexpr static const value_type two_16 = 65536; @@ -117,8 +120,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/padding.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/padding.hpp index b6ea68cb7..4d16dc334 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/padding.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/padding.hpp @@ -48,7 +48,9 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_padding_operation() {} + zkevm_padding_operation() { + this->gas_cost = 0; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -68,12 +70,17 @@ namespace nil { void generate_assignments(zkevm_circuit_type &zkevm_circuit) {} - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { } std::size_t rows_amount() override { return 1; } + + constraint_type pc_transition(const zkevm_circuit_type &zkevm_circuit) override { + constraint_type c; + return c; + } }; } // namespace blueprint } // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/pop.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/pop.hpp index 19c28f8a4..3ff17404c 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/pop.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/pop.hpp @@ -48,7 +48,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_pop_operation() {} + zkevm_pop_operation() { + this->stack_input = 1; + this->stack_output = 0; + this->gas_cost = 2; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,14 +70,14 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for POP" << std::endl; stack.pop(); } std::size_t rows_amount() override { - return 3; + return 1; } }; } // namespace blueprint diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/pushx.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/pushx.hpp index 7014f4c77..8c47d013e 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/pushx.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/pushx.hpp @@ -49,6 +49,10 @@ namespace nil { using var = typename op_type::var; zkevm_pushx_operation(std::size_t _x) : byte_count(_x) { + this->pc_gap = _x + 1; + this->stack_input = 0; + this->stack_output = 1; + if(_x == 0) this->gas_cost = 2; BOOST_ASSERT(_x < 33); // the maximum push is 32 bytes } @@ -85,9 +89,9 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine, + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine, zkevm_word_type bytecode_input) { - zkevm_stack &stack = machine.stack; + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; @@ -105,10 +109,8 @@ namespace nil { for (std::size_t i = 0; i < chunk_amount; i++) { assignment.witness(witness_cols[i], curr_row) = chunks[i]; } - // really push into stack for now - stack.push(a); } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { generate_assignments(zkevm_table, machine, 0); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/return.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/return.hpp index 1bac94b03..d2a0e26cb 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/return.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/return.hpp @@ -48,7 +48,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_return_operation() {} + zkevm_return_operation() { + this->stack_input = 2; + this->gas_cost = 0; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,14 +69,29 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for RETURN" << std::endl; stack.pop(); } + constraint_type pc_transition(const zkevm_circuit_type &zkevm_circuit) override { + constraint_type c; + return c; + } + + constraint_type gas_transition(const zkevm_circuit_type &zkevm_circuit) override { + constraint_type c; + return c; + } + + constraint_type stack_size_transition(const zkevm_circuit_type &zkevm_circuit) override { + constraint_type c; + return c; + } + std::size_t rows_amount() override { - return 3; + return 1; } }; } // namespace blueprint diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/sar.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/sar.hpp index 39dabb5d4..074552de1 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/sar.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/sar.hpp @@ -56,6 +56,11 @@ namespace nil { constexpr static const value_type two128 = 0x100000000000000000000000000000000_cppui_modular254; constexpr static const value_type two192 = 0x1000000000000000000000000000000000000000000000000_cppui_modular254; + zkevm_sar_operation(){ + this->stack_input = 2; + this->stack_output = 1; + } + template T chunk_sum_64(const std::vector &chunks, const unsigned char chunk_idx) const { BOOST_ASSERT(chunk_idx < 4); @@ -425,8 +430,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, lookup_constraints}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/sdiv_smod.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/sdiv_smod.hpp index 2c23be318..6682f603b 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/sdiv_smod.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/sdiv_smod.hpp @@ -48,7 +48,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_sdiv_smod_operation(bool _is_div) : is_div(_is_div) {} + zkevm_sdiv_smod_operation(bool _is_div) : is_div(_is_div) { + this->stack_input = 2; + this->stack_output = 1; + this->gas_cost = 5; + } bool is_div; @@ -473,12 +477,12 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; - zkevm_stack &stack = machine.stack; + zkevm_stack stack = machine.stack; word_type a = stack.pop(); word_type b_input = stack.pop(); diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/shl.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/shl.hpp index e773a45e4..664d2cad2 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/shl.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/shl.hpp @@ -48,7 +48,10 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_shl_operation() {} + zkevm_shl_operation() { + this->stack_input = 2; + this->stack_output = 1; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -218,8 +221,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, lookup_constraints}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/shr.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/shr.hpp index 065f02c7a..76f0c5ed4 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/shr.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/shr.hpp @@ -38,6 +38,11 @@ namespace nil { template class zkevm_shr_operation : public zkevm_operation { public: + zkevm_shr_operation(){ + this->stack_input = 2; + this->stack_output = 1; + } + using op_type = zkevm_operation; using gate_class = typename op_type::gate_class; using constraint_type = typename op_type::constraint_type; @@ -322,9 +327,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, lookup_constraints}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - std::cout << "SHR assignmetns cur_row =" << zkevm_table.get_current_row() << std::endl; - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>>; diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/signextend.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/signextend.hpp index 013bbdd03..5e5134b07 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/signextend.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/signextend.hpp @@ -48,6 +48,12 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; + zkevm_signextend_operation(){ + this->stack_input = 2; + this->stack_output = 1; + this->gas_cost = 5; + } + std::map>, std::vector> @@ -149,8 +155,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {constraints, lookup_constraints}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; using word_type = typename zkevm_stack::word_type; using integral_type = boost::multiprecision::number< diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/storage.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/storage.hpp index 346398e8e..064861d69 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/storage.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/storage.hpp @@ -48,7 +48,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_sstore_operation() {} + zkevm_sstore_operation() { + this->stack_input = 2; + this->stack_output = 0; + this->gas_cost = 100; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -66,8 +70,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for SSTORE" << std::endl; stack.pop(); stack.pop(); @@ -92,7 +96,11 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using var = typename op_type::var; - zkevm_sload_operation() {} + zkevm_sload_operation() { + this->stack_input = 1; + this->stack_output = 1; + this->gas_cost = 100; + } constexpr static const value_type two_16 = 65536; constexpr static const value_type two_32 = 4294967296; @@ -110,8 +118,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override { - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override { + zkevm_stack stack = machine.stack; std::cout << "Generate assignments and gates for SLOAD" << std::endl; stack.pop(); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/operations/swapx.hpp b/libs/blueprint/include/nil/blueprint/zkevm/operations/swapx.hpp index e31b48d26..20a4414a8 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/operations/swapx.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/operations/swapx.hpp @@ -50,6 +50,8 @@ namespace nil { zkevm_swapx_operation(std::size_t _x) : byte_count(_x) { BOOST_ASSERT(_x <= 16); // the maximum possible swap + this->stack_input = _x+1; + this->stack_output = _x+1; } std::size_t byte_count; @@ -63,8 +65,8 @@ namespace nil { return {{gate_class::MIDDLE_OP, {{}, {}}}}; } - void generate_assignments(zkevm_table_type &zkevm_table, zkevm_machine_interface &machine) override{ - zkevm_stack &stack = machine.stack; + void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) override{ + zkevm_stack stack = machine.stack; std::cout << "Generate assignments for SWAPx opcodes" << std::endl; stack.push(0); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp b/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp index 800055bce..91f24d140 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/stack.hpp @@ -45,23 +45,10 @@ namespace nil { return word; } - word_type top() { - return stack.back(); + word_type top(std::size_t depth=0) const{ + return stack[stack.size() - 1 - depth]; } -/* void swap() { - word_type a = pop(); - word_type b = pop(); - push(a); - push(b); - } - - void dup() { - word_type a = pop(); - push(a); - push(a); - } -*/ std::size_t size() const { return stack.size(); } diff --git a/libs/blueprint/include/nil/blueprint/zkevm/state.hpp b/libs/blueprint/include/nil/blueprint/zkevm/state.hpp index 685173a2b..cb11d4fdb 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/state.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/state.hpp @@ -36,245 +36,24 @@ namespace nil { namespace blueprint { - - // we use value_type as a default here in order to have easier time testing weird assignments like -1 - // I don't like this because value is directly connected with variable -/* template - struct state_var { - using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; - using assignment_type = nil::blueprint::assignment; - using var = crypto3::zk::snark::plonk_variable; - using column_type = typename var::column_type; - - std::size_t column_id; - column_type type; - T value; - - state_var(const std::size_t column_id_, const column_type type_, const T& value_) - : column_id(column_id_), type(type_), value(value_) {} - - state_var() = default; - - void set_value(const T &new_value) { - value = new_value; - } - - void assign_value(assignment_type &assignment, std::size_t row) const { - switch (type) { - case column_type::witness: - assignment.witness(column_id, row) = value; - break; - case column_type::constant: - assignment.constant(column_id, row) = value; - BOOST_ASSERT("We should not assign a state value to constant column"); - break; - case column_type::selector: - assignment.selector(column_id, row) = value; - BOOST_ASSERT("We should not assign a state value to selector column"); - break; - case column_type::public_input: - BOOST_ASSERT("We should not assign a state value to public input column"); - default: - BOOST_ASSERT("Unknown column type"); - } - } - - void set_and_assign_value(assignment_type &assignment, std::size_t row, const T &new_value) { - set_value(new_value); - assign_value(assignment, row); - } - - var variable(int32_t offset = 0) const { - BOOST_ASSERT(offset == 0 || offset == -1 || offset == 1); - return var(column_id, offset, true, type); - } - }; - - #define zkevm_STATE_LIST_FOR_TRANSITIONS(X) \ - X(pc) \ - X(stack_size) \ - X(memory_size) \ - X(gas) \ - - // Every variable which should be tracked between rows - template - struct zkevm_state { - using state_var_type = state_var; - using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; - using assignment_type = nil::blueprint::assignment; - // variables which have custom state transitions - #define X(name) state_var_type name; - zkevm_STATE_LIST_FOR_TRANSITIONS(X) - #undef X - // variables which have generic transition rules and are not handled via - // state transition mechanism - state_var_type step_selection; // 1 in first line of new opcode, 0 otherwise - state_var_type rows_until_next_op_inv; - state_var_type last_row_indicator; - - void assign_state(assignment_type &assignment, std::size_t row) const { - #define X(name) name.assign_value(assignment, row); - zkevm_STATE_LIST_FOR_TRANSITIONS(X) - #undef X - step_selection.assign_value(assignment, row); - rows_until_next_op_inv.assign_value(assignment, row); - last_row_indicator.assign_value(assignment, row); - } - }; - - struct transition_type { - enum type { - DEFAULT, - ANY, - SAME_VALUE, - DELTA, - NEW_VALUE, - }; - transition_type() - :t(DEFAULT), - value(0){} - - type t; - // either new value or delta; optional - // technically we could do arbirary field values here, but unlikely to be actually required - std::int64_t value; - }; - - std::ostream& operator<<(std::ostream &os, const transition_type &t) { - switch (t.t) { - case transition_type::DEFAULT: - os << "DEFAULT"; - break; - case transition_type::ANY: - os << "ANY"; - break; - case transition_type::SAME_VALUE: - os << "SAME_VALUE"; - break; - case transition_type::DELTA: - os << "DELTA(" << t.value << ")"; - break; - case transition_type::NEW_VALUE: - os << "NEW_VALUE(" << t.value << ")"; - break; - } - return os; - } - - struct zkevm_state_transition { - #define X(name) transition_type name; - zkevm_STATE_LIST_FOR_TRANSITIONS(X) - #undef X - }; - - zkevm_state_transition generate_frozen_state_transition() { - zkevm_state_transition transition; - #define X(name) transition.name.t = transition_type::SAME_VALUE; - zkevm_STATE_LIST_FOR_TRANSITIONS(X) - #undef X - return transition; - } - - std::ostream& operator<<(std::ostream &os, const zkevm_state_transition &t) { - #define X(name) os << #name << ": " << t.name << std::endl; - zkevm_STATE_LIST_FOR_TRANSITIONS(X) - #undef X - return os; - } - - template - std::optional> handle_transition( - const state_var &var, - const transition_type &transition, - std::function( - const state_var&, const transition_type&)> default_handler - ) { - switch (transition.t) { - case transition_type::SAME_VALUE: - return var.variable(+1) - var.variable(); - case transition_type::DELTA: - return var.variable(+1) - var.variable() - transition.value; - case transition_type::NEW_VALUE: - return var.variable(+1) - transition.value; - case transition_type::DEFAULT: - return default_handler(var, transition); - case transition_type::ANY: - return std::nullopt; - } - } - - template - crypto3::zk::snark::plonk_constraint handle_pc_default( - const state_var &var, - const transition_type &transition - ) { - // same as DELTA(1) - return var.variable(+1) - var.variable() - 1; - } - - template - crypto3::zk::snark::plonk_constraint handle_stack_size_default( - const state_var &var, - const transition_type &transition - ) { - // same as SAME - return var.variable(+1) - var.variable(); - } - - template - crypto3::zk::snark::plonk_constraint handle_memory_size_default( - const state_var &var, - const transition_type &transition - ) { - // same as SAME - return var.variable(+1) - var.variable(); - } - - template - crypto3::zk::snark::plonk_constraint handle_gas_default( - const state_var &var, - const transition_type &transition - ) { - // we shouldn't do this? maybe in error cases or testing? - // later should assert this out, currently SAME - return var.variable(+1) - var.variable(); - } - - template - std::vector> generate_transition_constraints( - const zkevm_state &state, - const zkevm_state_transition &transition - ) { - using constraint_type = crypto3::zk::snark::plonk_constraint; - std::vector result; - #define X(name) \ - if (auto constraint = handle_transition( \ - state.name, transition.name, handle_##name##_default)) { \ - result.push_back(*constraint); \ - } - zkevm_STATE_LIST_FOR_TRANSITIONS(X) - #undef X - return result; - }*/ - // It is really simplified state variable. We assume that each state position uses the whole column. // In this case variable is defined only by witness column id. // It's useful to have some convenient functions for rotations for circuit construction and absolute variables for assignment. template struct state_var:public crypto3::zk::snark::plonk_variable{ using var = crypto3::zk::snark::plonk_variable; - state_var(std::uint32_t witness_id = 0): var(witness_id, 0, true, var::column_type::witness){} + state_var(std::uint32_t witness_id = 0, typename var::column_type t = var::column_type::witness): var(witness_id, 0, true, t){} var operator() () const { - return var(this->index, 0, true, var::column_type::witness); + return var(this->index, 0, true, this->type); } var next() const { - return var(this->index, 1, true, var::column_type::witness); + return var(this->index, 1, true, this->type); } var prev() const { - return var(this->index, -1, true, var::column_type::witness); + return var(this->index, -1, true, this->type); } var abs(std::size_t row) const { - return var(this->index, row, false, var::column_type::witness); + return var(this->index, row, false, this->type); } }; // This class just contains state variables zkEVM state and next state @@ -292,10 +71,14 @@ namespace nil { state_var stack_size; state_var memory_size; state_var gas; - state_var step_selection; // 1 in first line of new opcode, 0 otherwise - state_var rows_counter_inv; - state_var last_row_indicator; // Do we really need it? - state_var option; + state_var opcode; + + state_var row_counter; // Decreasing row counter + state_var step_start; // 1 in first line of new opcode, 0 otherwise + state_var row_counter_inv; + state_var last_row_indicator; // Do we really need it? I don't think so. Last opcode should be RETURN, err or padding. + state_var opcode_parity; // opcode%2 + state_var is_even; // TODO: Do it constant column }; } // namespace blueprint } // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/state_selector.hpp b/libs/blueprint/include/nil/blueprint/zkevm/state_selector.hpp deleted file mode 100644 index f9d7d20fa..000000000 --- a/libs/blueprint/include/nil/blueprint/zkevm/state_selector.hpp +++ /dev/null @@ -1,333 +0,0 @@ -//---------------------------------------------------------------------------// -// Copyright (c) 2024 Dmitrii Tabalin -// Copyright (c) 2024 Alexey Yashunsky -// -// 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. -//---------------------------------------------------------------------------// - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace nil { - namespace blueprint { - namespace components { - // activates a witness column based on an input value - // is used to achieve dynamic selector behavior - // actual implementation - template - class state_selector; - - template - class state_selector, - BlueprintFieldType> - : public plonk_component { - - public: - using component_type = plonk_component; - - using var = typename component_type::var; - using manifest_type = plonk_component_manifest; - using constraint_type = crypto3::zk::snark::plonk_constraint; - using state_var = state_var; - - std::size_t options_amount; - bool is_compressed; - - class gate_manifest_type : public component_gate_manifest { - private: - std::size_t witness_amount; - std::size_t options_amount; - bool is_compressed; - - public: - gate_manifest_type(std::size_t witness_amount_, std::size_t options_amount_, bool is_compressed_) : - witness_amount(witness_amount_), options_amount(options_amount_), is_compressed(is_compressed_) {}; - - bool operator<(gate_manifest_type const& other) const { - return witness_amount < other.witness_amount || - (witness_amount == other.witness_amount && options_amount < other.options_amount) || - (witness_amount == other.witness_amount && options_amount == other.options_amount && - is_compressed < other.is_compressed); - } - - std::uint32_t gates_amount() const override { - return state_selector::gates_amount; - } - }; - - static gate_manifest get_gate_manifest(std::size_t witness_amount, - std::size_t options_amount, - bool is_compressed) { - gate_manifest manifest = gate_manifest(gate_manifest_type(witness_amount, options_amount, is_compressed)); - return manifest; - } - - static manifest_type get_manifest(std::size_t options_amount, bool is_compressed) { - manifest_type manifest = manifest_type( - // TODO: make the manifest depend on options_amount - // this requires the manifest rework - std::shared_ptr(new manifest_single_value_param( - is_compressed ? (options_amount + 1) / 4 + 2 : (options_amount + 1) / 2 + 2 - ) - ), - false - ); - return manifest; - } - - constexpr static std::size_t get_rows_amount(std::size_t witness_amount, - std::size_t options_amount, - bool is_compressed) { - return 1 + is_compressed; - } - - constexpr static const std::size_t gates_amount = 1; - const std::size_t rows_amount = get_rows_amount(this->witness_amount(), options_amount, is_compressed); - const std::string component_name = "state selector component"; - - struct input_type { - var item_index; - std::vector> all_vars() { - return {item_index}; - } - }; - - struct result_type { - result_type(const state_selector &component, std::size_t start_row_index) {} - std::vector> all_vars() { - return {}; - } - }; - - template - explicit state_selector(ContainerType witness, std::size_t options_amount_, bool is_compressed_ = false) : - component_type(witness, {}, {}, get_manifest(options_amount_, is_compressed_)), - options_amount(options_amount_), - is_compressed(is_compressed_) { - - BOOST_ASSERT(this->witness_amount() == - this->is_compressed ? (this->options_amount + 1) / 4 + 2 : (this->options_amount + 1) / 2 + 2); - }; - - template - state_selector(WitnessContainerType witness, ConstantContainerType constant, - PublicInputContainerType public_input, std::size_t options_amount_, bool is_compressed_ = false) : - component_type(witness, constant, public_input, get_manifest(options_amount_,is_compressed_)), - options_amount(options_amount_), - is_compressed(is_compressed_) { - - BOOST_ASSERT(this->witness_amount() == - this->is_compressed ? (this->options_amount + 1) / 4 + 2 : (this->options_amount + 1) / 2 + 2); - }; - - state_selector( - std::initializer_list witnesses, - std::initializer_list - constants, - std::initializer_list - public_inputs, - std::size_t options_amount_, bool is_compressed_ = false) : - component_type(witnesses, constants, public_inputs, get_manifest(options_amount_, is_compressed_)), - options_amount(options_amount_), - is_compressed(is_compressed_) { - - BOOST_ASSERT(this->witness_amount() == - this->is_compressed ? (this->options_amount + 1) / 4 + 2 : (this->options_amount + 1) / 2 + 2); - }; - - std::vector> generate_constraints() const { - using constraint_type = crypto3::zk::snark::plonk_constraint; - std::vector constraints; - - constraint_type sum_to_one; - constraint_type idx_decompose; - std::size_t option_cells_amount = (this->options_amount + 1)/2, - option_WA = this->witness_amount() - 1; - std::size_t idx = 0; - for (std::size_t i = 0; i < option_cells_amount; i++) { - var curr_var = var(this->W(1 + (i % option_WA)), 0 + is_compressed*(i / option_WA), true, var::column_type::witness); - sum_to_one += curr_var; - idx_decompose += idx * curr_var; - idx += 2; - constraints.push_back(curr_var * (curr_var - 1)); - } - sum_to_one -= 1; - constraints.push_back(sum_to_one); - - var pairing_var = var(this->W(this->witness_amount() - 1), 0 + is_compressed, true, var::column_type::witness); - idx_decompose += pairing_var; - idx_decompose -= var(this->W(0), 0, true, var::column_type::witness); - constraints.push_back(idx_decompose); - - if (is_compressed) { - constraints.push_back(var(this->W(0), 0, true, var::column_type::witness) - - var(this->W(0), +1, true, var::column_type::witness)); - } - - constraints.push_back(pairing_var * (pairing_var - 1)); - if (options_amount % 2 != 0) { - var last_pair = var(this->W(1 + ((option_cells_amount - 1) % option_WA)), - 0 + is_compressed*((option_cells_amount - 1) / option_WA), true, var::column_type::witness); - constraints.push_back(last_pair * pairing_var); - } - - return constraints; - } - - constraint_type option_constraint(std::size_t option, bool shift = false) const { - BOOST_ASSERT(option < options_amount); - - std::size_t option_cells_amount = (this->options_amount + 1)/2, - option_WA = this->witness_amount() - 1; - - var option_var = var(this->W(1 + ((option / 2) % option_WA)), - 0 + is_compressed*((option/2 / option_WA) - shift), - true, var::column_type::witness), - parity_var = var(this->W(this->witness_amount() - 1), 0+is_compressed*(1 - shift), true, var::column_type::witness); - - if (option % 2 == 0) { - return option_var * (1 - parity_var); - } else { - return option_var * parity_var; - } - } - - constraint_type option_constraint_even(std::size_t option) const { - return option_constraint(option,true); - } - - constraint_type option_constraint_odd(std::size_t option) const { - return option_constraint(option,false); - } - - state_var option_variable() const { - return state_var(this->W(0)); - } - state_var parity_variable() const { - return state_var(this->W(this->witness_amount() - 1)); - } - - }; - - template - using plonk_state_selector = - state_selector, - BlueprintFieldType>; - - template - typename plonk_state_selector::result_type generate_assignments( - const plonk_state_selector &component, - assignment> - &assignment, - const typename plonk_state_selector::input_type - &instance_input, - const std::uint32_t start_row_index) { - - using component_type = plonk_state_selector; - using value_type = typename BlueprintFieldType::value_type; - using integral_type = typename BlueprintFieldType::integral_type; - - value_type index = var_value(assignment, instance_input.item_index); - //if( index >= component.options_amount ) std::cout << index << ">=" << component.options_amount << std::endl; - BOOST_ASSERT(index < component.options_amount); - - // calculating this is somehow very unintuitive - std::size_t option_WA = component.witness_amount() - 1; - const std::size_t pair_index = std::size_t(integral_type(index.data >> 1)); - const integral_type parity = integral_type(index.data & value_type(1).data); - assignment.witness(component.W(0), start_row_index) = index; - if (component.is_compressed) { - assignment.witness(component.W(0), start_row_index + 1) = index; - } - for (std::size_t i = 1; i < component.witness_amount() - 1; i++) { - assignment.witness(component.W(i), start_row_index) = 0; - if (component.is_compressed) { - assignment.witness(component.W(i), start_row_index + 1) = 0; - } - } - assignment.witness(component.W(1 + (pair_index % option_WA)), - start_row_index + component.is_compressed*(pair_index / option_WA)) = 1; - assignment.witness(component.W(component.witness_amount() - 1), - start_row_index + component.is_compressed) = value_type(parity); - - return typename component_type::result_type(component, start_row_index); - } - - template - std::size_t generate_gates( - const plonk_state_selector &component, - circuit> &bp, - assignment> - &assignment, - const typename plonk_state_selector::input_type - &instance_input) { - - return bp.add_gate(component.generate_constraints()); - } - - template - void generate_copy_constraints( - const plonk_state_selector &component, - circuit> &bp, - assignment> - &assignment, - const typename plonk_state_selector::input_type - &instance_input, - const std::size_t start_row_index) { - - using component_type = plonk_state_selector; - using var = typename component_type::var; - using state_var = state_var; - - bp.add_copy_constraint( - {instance_input.item_index, - var(component.W(0), start_row_index, false, var::column_type::witness)}); - } - - template - typename plonk_state_selector::result_type generate_circuit( - const plonk_state_selector &component, - circuit> &bp, - assignment> - &assignment, - const typename plonk_state_selector::input_type - &instance_input, - const std::size_t start_row_index) { - - using component_type = plonk_state_selector; - using state_var = state_var; - - std::size_t selector_index = generate_gates(component, bp, assignment, instance_input); - assignment.enable_selector(selector_index, start_row_index, start_row_index); - - return typename component_type::result_type(component, start_row_index); - } - - } // namespace components - } // namespace blueprint -} // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp index 6c564ff47..81eb32f95 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_circuit.hpp @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -125,12 +126,12 @@ namespace nil { using arithmetization_type = crypto3::zk::snark::plonk_constraint_system; using assignment_type = nil::blueprint::assignment; using circuit_type = nil::blueprint::circuit; -// using state_var_type = state_var; + using state_var_type = state_var; using zkevm_state_type = zkevm_vars; using columns_manager_type = columns_manager; using zkevm_operation_type = zkevm_operation; using zkevm_opcode_gate_class = typename zkevm_operation::gate_class; - using state_selector_type = components::state_selector; + using index_selector_type = components::index_selector; using constraint_type = crypto3::zk::snark::plonk_constraint; using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; using value_type = typename BlueprintFieldType::value_type; @@ -145,6 +146,7 @@ namespace nil { columns_manager_type col_manager(assignment); // Just helps us to deal with assignment table columns // 5(?) constant columns. I'm not sure we really need them: satisfiability check passes even without them + // We really need them to run lookup argument. Should be removed when we'll use logUp. for(std::size_t i = 0; i < 5; i++) { col_manager.allocate_constant_column(); } @@ -184,6 +186,10 @@ namespace nil { assignment.enable_selector(start_selector, start_row_index); assignment.enable_selector(middle_selector, start_row_index, max_rows-1); + // It is a public column, we shouldn't prove that it is correct; + for(std::size_t i = 0; i < max_rows; i++ ){ + assignment.constant(state.is_even().index, i + start_row_index) = 1 - i%2; + } lookup_tables_indices = circuit.get_reserved_indices(); } @@ -195,7 +201,7 @@ namespace nil { lookup_tables["zkevm_bytecode"] = 1; return lookup_tables; } - + protected: // May be reviewed somehow. Now I'll do the most straight way // Each table has its separate columns and selectors. // They definitely may be packed more effectively @@ -206,12 +212,38 @@ namespace nil { } } - const std::shared_ptr get_opcode_row_selector() const{ - return opcode_row_selector; + // Constraint for all rows for given opcode + constraint_type opcode_selector_constraint(std::size_t opcode_num){ + std::size_t bit1 = (opcode_num % 4 == 3) || (opcode_num % 4 == 2); + state_var o4 = opcode_selector->index(opcode_num/4); + constraint_type o2_constraint = bit1 ? state.is_even() * o4.next() + state.is_even.prev() * o4() : state.is_even() * o4() + state.is_even.prev() * o4.prev(); + constraint_type opcode_parity = opcode_num%2 ? state.opcode_parity(): 1 - state.opcode_parity(); + return o2_constraint * opcode_parity;// Degree 3 + } + + // Constraint for given row for opcode + constraint_type opcode_row_selector_constraint(std::size_t opcode_num, std::size_t row){ + std::size_t bit1 = (opcode_num % 4 == 3) || (opcode_num % 4 == 2); + state_var row_var = row_selector->index(row/2); + state_var o4 = opcode_selector->index(opcode_num/4); + constraint_type o2_constraint; + if(row % 2) + o2_constraint = bit1 ? state.is_even() * o4.next() : state.is_even() * o4(); + else + o2_constraint = bit1 ? state.is_even.prev() * o4() : state.is_even.prev() * o4.prev(); + constraint_type opcode_parity = opcode_num%2 ? state.opcode_parity(): 1 - state.opcode_parity(); + return o2_constraint * opcode_parity * row_var();// Degree 3 + } + public: + const std::size_t get_opcode_range_checked_cols_amount() const { + return opcode_range_checked_cols_amount; + } + const std::shared_ptr get_row_selector() const{ + return row_selector; } - const std::shared_ptr get_state_selector() const{ - return state_selector; + const std::shared_ptr get_opcode_selector() const{ + return opcode_selector; } const opcodes_info get_opcodes_info() const{ @@ -226,8 +258,8 @@ namespace nil { return state; } - const std::vector &get_state_selector_cols() const{ - return state_selector_cols; + const std::vector &get_opcode_selector_cols() const{ + return opcode_selector_cols; } const std::vector &get_opcode_cols() const{ @@ -252,24 +284,24 @@ namespace nil { // for opcode constraints at certain row of opcode execution // note that rows are counted "backwards", starting from opcode rows amount minus one // and ending in zero - constraint_type get_opcode_row_constraint(std::size_t row, std::size_t opcode_height) const { + /*constraint_type get_opcode_row_constraint(std::size_t row, std::size_t opcode_height) const { BOOST_ASSERT(row < opcode_height); var height_var = opcode_row_selector->option_variable(); - var height_var_inv = state.rows_counter_inv; + var height_var_inv = state.row_counter_inv; // ordering here is important: minimising the degree when possible if (row == opcode_height - 1) { - return state.step_selection; + return state.step_start; } if (row == opcode_height - 2) { - return state.step_selection.prev(); + return state.step_start.prev(); } if (row == 0) { return 1 - height_var * height_var_inv; } // TODO: this is probably possible to optimise return opcode_row_selector->option_constraint(row); - } + }*/ const std::vector &get_bytecode_witnesses() const{ return bytecode_witnesses; @@ -281,9 +313,18 @@ namespace nil { state.stack_size = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); state.memory_size = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); state.gas = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); - state.rows_counter_inv = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); - state.step_selection = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); - state.last_row_indicator = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + + state.row_counter = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + state.row_counter_inv = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + state.step_start = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + //state.last_row_indicator = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + state.opcode = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + state.opcode_parity = typename zkevm_state_type::state_var(col_manager.allocate_witness_column()); + state.is_even = typename zkevm_state_type::state_var(col_manager.allocate_constant_column(), var::column_type::constant); + } + + std::vector generate_selectors_constraints(){ + return {}; } std::vector generate_generic_transition_constraints( @@ -292,42 +333,30 @@ namespace nil { ) { std::vector constraints; - state_var rows_counter = opcode_row_selector->option_variable(); - state_var rows_counter_inv = state.rows_counter_inv; - state_var step_selection = state.step_selection; - state_var option = state.option; - - constraints.push_back(rows_counter() * (rows_counter() * rows_counter_inv() - 1 )); //GEN1 - constraints.push_back(rows_counter_inv() * (rows_counter() * rows_counter_inv() - 1)); //GEN2 - // rows_counter decrementing (unless we are at the last row of opcode) - constraints.push_back(rows_counter() * (rows_counter.next() - rows_counter() + 1)); //GEN3 - // step_selection is 0 or 1 - constraints.push_back((1 - step_selection()) * step_selection()); //GEN4 - // step_selection = 1 if previous row_counter is 0 - constraints.push_back(step_selection() * rows_counter.prev()); //GEN5 + state_var row_counter = state.row_counter; + state_var row_counter_inv = state.row_counter_inv; + state_var step_start = state.step_start; + state_var opcode = state.opcode; + + constraints.push_back(row_counter() * (row_counter() * row_counter_inv() - 1 )); //GEN1 + constraints.push_back(row_counter_inv() * (row_counter() * row_counter_inv() - 1)); //GEN2 + // row_counter decrementing (unless we are at the last row of opcode) + constraints.push_back(row_counter() * (row_counter.next() - row_counter() + 1)); //GEN3 + // step_start is 0 or 1 + constraints.push_back((1 - step_start()) * step_start()); //GEN4 + // step_start = 1 if previous row_counter is 0 + constraints.push_back(step_start() * row_counter.prev()); //GEN5 // step is copied unless new opcode is next - constraints.push_back((1 - step_selection()) * (option() - option.prev())); //GEN6 + constraints.push_back((1 - step_start()) * (opcode() - opcode.prev())); //GEN6 // new opcode selection is forced if new opcode is next - constraints.push_back((1 - rows_counter_inv.prev() * rows_counter.prev()) * (1 - step_selection())); //GEN7 + constraints.push_back((1 - row_counter_inv.prev() * row_counter.prev()) * (1 - step_start())); //GEN7 + constraints.push_back((1 - step_start()) * (state.opcode_parity() - state.opcode_parity.prev())); // Other state variables does not changed inside one opcode change it if necessary. - constraints.push_back((1 - step_selection()) * (state.pc() - state.pc.prev())); //GEN8 - constraints.push_back((1 - step_selection()) * (state.gas() - state.gas.prev())); //GEN9 - constraints.push_back((1 - step_selection()) * (state.stack_size() - state.stack_size.prev())); //GEN10 - constraints.push_back((1 - step_selection()) * (state.memory_size() - state.memory_size.prev())); //GEN11 - - // All transitions between states will be done in opcodes - // or we are at the end of the circuit -/* auto partial_state_transition_constraints = generate_transition_constraints( - state, generate_frozen_state_transition()); - // the initially problematic constraints are here \/ \/ \/ - for (auto constraint : partial_state_transition_constraints) { - constraints.push_back( - (1 - last_row_indicator_var) * - // ^^^ this fixes the problem from the commented line below -// (1 - var(end_selector, 0, true, var::column_type::selector)) * - (1 - step_selection_next_var) * constraint); - }*/ + constraints.push_back((1 - step_start()) * (state.pc() - state.pc.prev())); //GEN8 + constraints.push_back((1 - step_start()) * (state.gas() - state.gas.prev())); //GEN9 + constraints.push_back((1 - step_start()) * (state.stack_size() - state.stack_size.prev())); //GEN10 + constraints.push_back((1 - step_start()) * (state.memory_size() - state.memory_size.prev())); //GEN11 return constraints; } @@ -365,8 +394,8 @@ namespace nil { opcodes[zkevm_opcode::MLOAD] = std::make_shared>(); // Storage operations - opcodes[zkevm_opcode::SLOAD] = std::make_shared>(); - opcodes[zkevm_opcode::SSTORE] = std::make_shared>(); + opcodes[zkevm_opcode::SLOAD] = std::make_shared>(); + opcodes[zkevm_opcode::SSTORE] = std::make_shared>(); // CALL operaitions opcodes[zkevm_opcode::CALLVALUE] = std::make_shared>(); @@ -375,7 +404,7 @@ namespace nil { // PC operations opcodes[zkevm_opcode::JUMPI] = std::make_shared>(); - opcodes[zkevm_opcode::JUMP] = std::make_shared>(); + opcodes[zkevm_opcode::JUMP] = std::make_shared>(); opcodes[zkevm_opcode::JUMPDEST] = std::make_shared>(); opcodes[zkevm_opcode::PUSH0] = std::make_shared>(0); @@ -466,74 +495,73 @@ namespace nil { first_constraints.push_back(state.stack_size); // stack size at start is 0. // NB: no need for range checks before first real transition, // it's all ensured by "frozen" transition constraints. - first_constraints.push_back(state.step_selection - 1); // first step is step selection + first_constraints.push_back(state.step_start - 1); // first step is step selection + // Allocate all necessary columns. Selectors col_manager.allocate_selector_column(); // Start col_manager.allocate_selector_column(); // End col_manager.allocate_selector_column(); // Middle - start_selector = circuit.add_gate(first_constraints); - -// middle_constraints.push_back(state.last_row_indicator.prev()); - // ensure that stack_size is always between 0 and max_stack_size. - // This allows simpler transitions of stack size without the need to control validity of updated stack size - - // TODO: stack size validity will be checked by RW table. -// middle_lookup_constraints.push_back({range_check_table_index, { state.stack_size } }); -// middle_lookup_constraints.push_back({range_check_table_index, { state.stack_size + 65535 - max_stack_size } }); - - // TODO: proper end constraints zkevm_padding_operation - last_constraints.push_back(state.last_row_indicator.prev() - 1); - end_selector = circuit.add_gate(last_constraints); - - std::vector opcode_range_checked_cols; - const std::size_t opcodes_amount = opcodes_info_instance.get_opcodes_amount(); - const std::size_t state_selector_cols_amount = - state_selector_type::get_manifest(opcodes_amount,true).witness_amount->max_value_if_sat(); - - for (std::size_t i = 0; i < state_selector_cols_amount; i++) { - state_selector_cols.push_back(col_manager.allocate_witness_column()); + const std::size_t opcode_selector_cols_amount = std::ceil(float(opcodes_amount)/4); + for (std::size_t i = 0; i < opcode_selector_cols_amount; i++) { + opcode_selector_cols.push_back(col_manager.allocate_witness_column()); } + opcode_selector = std::make_shared( + opcode_selector_cols, std::array({}), std::array({}), + std::ceil(float(opcodes_amount)/4) + ); + auto opcode_selector_constraints = opcode_selector->generate_constraints(); + middle_constraints.insert( + middle_constraints.end(), opcode_selector_constraints.begin(), opcode_selector_constraints.end() + ); + middle_constraints.push_back( state.is_even() * (1 - opcode_selector->sum_constraint() - opcode_selector->sum_constraint(1))); + middle_constraints.push_back( + state.opcode + - 4 * ( state.is_even() * (opcode_selector->index_constraint() + opcode_selector->index_constraint(1)) + + state.is_even.prev() * (opcode_selector->index_constraint(-1) + opcode_selector->index_constraint()) ) + - 2 * state.is_even.prev() * opcode_selector->sum_constraint() + - 2 * state.is_even() * opcode_selector->sum_constraint(1) + - state.opcode_parity() + ); + + std::vector opcode_range_checked_cols; for(std::size_t i = 0; i < opcode_range_checked_cols_amount; i++) { opcode_range_checked_cols.push_back(col_manager.allocate_witness_column()); } - opcode_cols = opcode_range_checked_cols; // range-checked columns are the first part of opcode columns for (std::size_t i = 0; i < opcode_other_cols_amount; i++) { // followed by some non-range-checked columns opcode_cols.push_back(col_manager.allocate_witness_column()); } - state_selector = std::make_shared( - state_selector_cols, std::array({}), std::array({}), - opcodes_amount,true + + const std::size_t row_selector_cols_amount = (max_opcode_height + max_opcode_height%2)/2; + for (std::size_t i = 0; i < row_selector_cols_amount; i++) { + row_selector_cols.push_back(col_manager.allocate_witness_column()); + } + row_selector = std::make_shared( + row_selector_cols, std::array({}), std::array({}), + std::ceil(float(max_opcode_height)/2)); + auto row_selector_constraints = row_selector->generate_constraints(); + middle_constraints.insert( + middle_constraints.end(), row_selector_constraints.begin(), row_selector_constraints.end() ); - state.option = state_selector->option_variable(); + middle_constraints.push_back(1 - row_selector->sum_constraint()); + middle_constraints.push_back(state.row_counter() - row_selector->index_constraint() * 2 - state.is_even()); - auto state_selector_constraints = state_selector->generate_constraints(); + start_selector = circuit.add_gate(first_constraints); - const std::size_t opcode_row_selection_cols_amount = - state_selector_type::get_manifest(max_opcode_height,false).witness_amount->max_value_if_sat(); - for (std::size_t i = 0; i < opcode_row_selection_cols_amount; i++) { - opcode_row_selection_cols.push_back(col_manager.allocate_witness_column()); - } - opcode_row_selector = std::make_shared( - opcode_row_selection_cols, std::array({}), std::array({}), - max_opcode_height,false); - auto opcode_row_selector_constraints = opcode_row_selector->generate_constraints(); - - if (state_selector->is_compressed) { - // for a compressed state selector we rely upon opcode_row_selector parity data to apply constraints once in 2 rows - var parity_var = opcode_row_selector->parity_variable(); - for(auto constraint : state_selector_constraints) { - middle_constraints.push_back(constraint * parity_var); - } - } else { - middle_constraints.insert(middle_constraints.end(), state_selector_constraints.begin(), state_selector_constraints.end()); - } +// middle_constraints.push_back(state.last_row_indicator.prev()); + // ensure that stack_size is always between 0 and max_stack_size. + // This allows simpler transitions of stack size without the need to control validity of updated stack size - middle_constraints.insert(middle_constraints.end(), opcode_row_selector_constraints.begin(), - opcode_row_selector_constraints.end()); + // TODO: stack size validity will be checked by RW table. +// middle_lookup_constraints.push_back({range_check_table_index, { state.stack_size } }); +// middle_lookup_constraints.push_back({range_check_table_index, { state.stack_size + 65535 - max_stack_size } }); + + // TODO: proper end constraints zkevm_padding_operation +// last_constraints.push_back(state.last_row_indicator.prev() - 1); + end_selector = circuit.add_gate(last_constraints); auto generic_state_transition_constraints = generate_generic_transition_constraints( start_selector, end_selector); @@ -553,11 +581,18 @@ namespace nil { constraint_type opcode_first_line_constraint; constraint_type stack_size_transitions; + constraint_type memory_size_transitions; constraint_type gas_transitions; for (auto opcode_it : opcodes) { + if( opcode_it.second->stack_input != opcodes_info_instance.get_opcode_stack_input(opcode_it.first)) + std::cout << "WRONG stack_input for " << opcode_to_string(opcode_it.first) << ": " << opcode_it.second->stack_input << " != " << opcodes_info_instance.get_opcode_stack_input(opcode_it.first) << std::endl; + if( opcode_it.second->stack_output != opcodes_info_instance.get_opcode_stack_output(opcode_it.first)) + std::cout << "WRONG stack_output for " << opcode_to_string(opcode_it.first) << ": " << opcode_it.second->stack_output << " != " << opcodes_info_instance.get_opcode_stack_output(opcode_it.first) << std::endl; + if( opcode_it.second->gas_cost != opcodes_info_instance.get_opcode_cost(opcode_it.first)) + std::cout << "WRONG gas_cost for " << opcode_to_string(opcode_it.first) << ": " << opcode_it.second->gas_cost << " != " << opcodes_info_instance.get_opcode_cost(opcode_it.first) << std::endl; std::size_t opcode_height = opcode_it.second->rows_amount(); - std::cout << "Gates for " << opcode_to_string(opcode_it.first) << std::endl; + //std::cout << "Gates for " << opcode_to_string(opcode_it.first) << std::endl; if (opcode_height > max_opcode_height) { BOOST_ASSERT("Opcode height exceeds maximum, please update max_opcode_height constant."); @@ -566,25 +601,14 @@ namespace nil { std::size_t adj_opcode_height = opcode_height + (opcode_height % 2); std::size_t opcode_num = opcodes_info_instance.get_opcode_number(opcode_it.first); - auto curr_opt_constraint = state_selector->option_constraint(opcode_num), - curr_opt_constraint_even = state_selector->option_constraint_even(opcode_num), // Why do we need it? - curr_opt_constraint_odd = state_selector->option_constraint_odd(opcode_num); // Why do we need it? // save constraints to ensure later that internal row number has proper value at the start of the opcode + // We can use opcode_row_selector_constraint, but it has similar degree. opcode_first_line_constraint += - curr_opt_constraint * (opcode_row_selector->option_variable() - (adj_opcode_height - 1)); + opcode_selector_constraint(opcode_num) * ( state.row_counter() - adj_opcode_height + 1); // ^^^ curr_opt_constraint is in _odd_ version because it's applied // at row with internal number adj_opcode_height-1, that always odd - // save constraints to ensure correct updates of stack size - // TODO: Done for each opcode individually - // Static case will be hardcoded in zkevm_operatoin - /* stack_size_transitions += curr_opt_constraint_even * (state.stack_size - - opcodes_info_instance.get_opcode_stack_input(opcode_it.first) - + opcodes_info_instance.get_opcode_stack_output(opcode_it.first) - - state.stack_size.next());*/ - // curr_opt_constraint is in _even_ version because it's applied at row with internal number 0 - // save constraints to ensure correct updates of remaining gas NB: only static costs now! TODO: include dynamic costs // TODO: Done for each opcode individually // Static case will be hardcoded in zkevm_operatoin @@ -599,25 +623,21 @@ namespace nil { for (auto gate_it : opcode_gates) { switch (gate_it.first) { case zkevm_opcode_gate_class::FIRST_OP: - for (auto constraint_pair : gate_it.second.first) { - std::size_t local_row = constraint_pair.first; - constraint_type curr_opt_constraint = - (local_row % 2 == 0) ? curr_opt_constraint_even : curr_opt_constraint_odd; - constraint_type constraint = get_opcode_row_constraint(local_row, adj_opcode_height) + std::cout << "Not implemented" << std::endl; +/* for (auto constraint_pair : gate_it.second.first) { + constraint_type constraint = opcode_row_selector_constraint(opcode_num, adj_opcode_height - 1) * constraint_pair.second; constraint_list[gate_id_type(constraint_pair.second)] = constraint_pair.second; virtual_selector[gate_id_type(constraint_pair.second)] += - get_opcode_row_constraint(local_row, adj_opcode_height) * curr_opt_constraint * start_selector; + constraint * start_selector; } for (auto lookup_constraint_pair : gate_it.second.second) { std::size_t local_row = lookup_constraint_pair.first; - constraint_type curr_opt_constraint = - (local_row % 2 == 0) ? curr_opt_constraint_even : curr_opt_constraint_odd; lookup_constraint_type lookup_constraint = lookup_constraint_pair.second; auto lookup_table = lookup_constraint.table_id; auto lookup_expressions = lookup_constraint.lookup_input; - constraint_type row_selector = get_opcode_row_constraint(local_row, adj_opcode_height); + constraint_type row_selector = get_opcode_row_constraint(local_row, adj_opcode_height - 1); std::vector new_lookup_expressions; for(auto lookup_expr : lookup_expressions) { @@ -625,35 +645,29 @@ namespace nil { } middle_lookup_constraints.push_back({lookup_table, new_lookup_expressions}); } - break; + break;*/ case zkevm_opcode_gate_class::MIDDLE_OP: - std::cout << "Middle constraints from " << opcode_to_string(opcode_it.first) << std::endl; + //std::cout << "Middle constraints from " << opcode_to_string(opcode_it.first) << std::endl; for (auto constraint_pair : gate_it.second.first) { - std::cout << "\t" << constraint_pair.first << ": " << constraint_pair.second << std::endl; + //std::cout << "\t" << constraint_pair.first << ": " << constraint_pair.second << std::endl; std::size_t local_row = constraint_pair.first; - if(local_row > 1) continue; if( opcode_it.second->rows_amount() % 2 ) local_row++; - //constraint_type curr_opt_constraint = - // (local_row % 2 == 0) ? curr_opt_constraint_odd : curr_opt_constraint_even; - constraint_type row_constraint = get_opcode_row_constraint(local_row, adj_opcode_height) - * constraint_pair.second; constraint_list[gate_id_type(constraint_pair.second)] = constraint_pair.second; virtual_selector[gate_id_type(constraint_pair.second)] += - get_opcode_row_constraint(local_row, adj_opcode_height) * curr_opt_constraint; + opcode_row_selector_constraint(opcode_num, local_row); } for (auto lookup_constraint_pair : gate_it.second.second) { + // TODO:: do same trick with polynomial constraints for lookup constraints with similar lookup tables. std::size_t local_row = lookup_constraint_pair.first; - //constraint_type curr_opt_constraint = - // (local_row % 2 == 0) ? curr_opt_constraint_even : curr_opt_constraint_odd; + if( opcode_it.second->rows_amount() % 2 ) local_row++; lookup_constraint_type lookup_constraint = lookup_constraint_pair.second; auto lookup_table = lookup_constraint.table_id; auto lookup_expressions = lookup_constraint.lookup_input; - constraint_type row_selector = get_opcode_row_constraint(local_row, adj_opcode_height); std::vector new_lookup_expressions; for(auto lookup_expr : lookup_expressions) { - new_lookup_expressions.push_back(curr_opt_constraint * lookup_expr * row_selector); + new_lookup_expressions.push_back( opcode_row_selector_constraint(opcode_num, local_row) * lookup_expr ); } middle_lookup_constraints.push_back({lookup_table, new_lookup_expressions}); } @@ -668,21 +682,17 @@ namespace nil { BOOST_ASSERT("Unknown gate class"); } } + gas_transitions += opcode_row_selector_constraint(opcode_num, 0) * opcode_it.second->gas_transition(*this); + pc_transitions += opcode_row_selector_constraint(opcode_num, 0) * opcode_it.second->pc_transition(*this); + stack_size_transitions += opcode_row_selector_constraint(opcode_num, 0) * opcode_it.second->stack_size_transition(*this); } // ensure first line of each opcode has correct internal row number - middle_constraints.push_back(opcode_first_line_constraint * state.step_selection()); + middle_constraints.push_back(opcode_first_line_constraint * state.step_start()); - // ensure the last line of each opcode updates stack_size and gas correctly -// middle_constraints.push_back(stack_size_transitions * state.step_selection.next()); -// middle_constraints.push_back(gas_transitions * state.step_selection.next()); - // increase program counter, unless the opcode is JUMP or JUMPI -// middle_constraints.push_back((state.pc() + 1 - state.pc.next()) -// * (1 - state_selector->option_constraint_even(opcodes_info_instance.get_opcode_number(zkevm_opcode::JUMP)) -// - state_selector->option_constraint_even(opcodes_info_instance.get_opcode_number(zkevm_opcode::JUMPI))) -// * state.step_selection.next()); - // TODO: JUMP and JUMPI need special constraints for program counter - // we also need to check that they are followed by either JUMPDEST or an error opcode + middle_constraints.push_back(stack_size_transitions); + middle_constraints.push_back(pc_transitions); + //middle_constraints.push_back(gas_transitions); for(const auto c : virtual_selector) { constraint_type constraint = constraint_list[c.first]; @@ -693,6 +703,11 @@ namespace nil { circuit.add_lookup_gate(middle_selector, middle_lookup_constraints); } + constraint_type gas_transitions; + constraint_type pc_transitions; + constraint_type stack_size_transitions; + constraint_type opcode_transitions; + zkevm_state_type state; // static selectors used to mark the places where the circuit starts/ends and when the circuit is acitve std::size_t start_selector; @@ -703,9 +718,9 @@ namespace nil { // witness columns for opcodes std::vector opcode_cols; // dynamic selectors for the state selector circuit - std::vector state_selector_cols; + std::vector opcode_selector_cols; // columns for selecting specific rows from the opcode - std::vector opcode_row_selection_cols; + std::vector row_selector_cols; // --------------------------------------------------------------------------------------------- // |Variables below this point are internal to the object and do not go into the actual circuit| // --------------------------------------------------------------------------------------------- @@ -714,8 +729,8 @@ namespace nil { assignment_type &assignment; // information about opcode metadata (mapping, etc.) const opcodes_info &opcodes_info_instance; - std::shared_ptr state_selector; - std::shared_ptr opcode_row_selector; + std::shared_ptr opcode_selector; // Selects opcode_id/4 + std::shared_ptr row_selector; // Selects row_selector/2 // opcode objects std::map>> opcodes; // start and end rows for the circuit; both have to be fixed diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp index ee30e3b31..7958db580 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_operation.hpp @@ -68,8 +68,6 @@ namespace nil { using assignment_type = assignment>; using var = nil::crypto3::zk::snark::plonk_variable; - zkevm_operation() {} - virtual ~zkevm_operation() = default; // note that some parts of the map may be empty // we expect that most of the operations would only use MIDDLE_OP @@ -79,14 +77,35 @@ namespace nil { >> generate_gates(zkevm_circuit_type &zkevm_circuit) = 0; - virtual void generate_assignments(zkevm_table_type &zkevm_circuit, zkevm_machine_interface &machine) = 0; + virtual void generate_assignments(zkevm_table_type &zkevm_table, const zkevm_machine_interface &machine) = 0; // should return the same rows amount for everyс operation right now // here in case we would make it dynamic in the future virtual std::size_t rows_amount() = 0; + + virtual constraint_type pc_transition(const zkevm_circuit_type &zkevm_circuit) { + const auto &state = zkevm_circuit.get_state(); + return state.pc.next() - state.pc() - pc_gap; + } + + virtual constraint_type gas_transition(const zkevm_circuit_type &zkevm_circuit) { + const auto &state = zkevm_circuit.get_state(); + return state.gas.next() - state.gas() + gas_cost; + } + + virtual constraint_type stack_size_transition(const zkevm_circuit_type &zkevm_circuit) { + const auto &state = zkevm_circuit.get_state(); + return state.stack_size.next() - state.stack_size() + stack_input - stack_output; + } + // utility funciton static var var_gen(const std::vector &witness_cols, std::size_t i, int32_t offset = 0) { return var(witness_cols[i], offset, true, var::column_type::witness); }; + public: + std::size_t pc_gap = 1; + std::size_t stack_input = 0; + std::size_t stack_output = 0; + std::size_t gas_cost = 3; }; } // namespace blueprint } // namespace nil diff --git a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_table.hpp b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_table.hpp index e06a16c4a..07398628f 100644 --- a/libs/blueprint/include/nil/blueprint/zkevm/zkevm_table.hpp +++ b/libs/blueprint/include/nil/blueprint/zkevm/zkevm_table.hpp @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include #include @@ -92,7 +92,7 @@ namespace nil { using columns_manager_type = columns_manager; using zkevm_operation_type = zkevm_operation; using zkevm_opcode_gate_class = typename zkevm_operation::gate_class; - using state_selector_type = components::state_selector; + using index_selector_type = components::index_selector; using constraint_type = crypto3::zk::snark::plonk_constraint; using lookup_constraint_type = crypto3::zk::snark::plonk_lookup_constraint; using value_type = typename BlueprintFieldType::value_type; @@ -102,22 +102,8 @@ namespace nil { circuit(circuit_), assignment(assignment_), curr_row(circuit.get_start_row_index()){ } -/* void assign_state(const zkevm_machine_interface &machine) { - assignment.witness(circuit.get_state().pc.index, curr_row) = machine.pc; - assignment.witness(circuit.get_state().gas.index, curr_row) = machine.gas; - assignment.witness(circuit.get_state().stack_size.index, curr_row) = machine.stack_size; - assignment.witness(circuit.get_state().memory_size.index, curr_row) = machine.memory_size; - assignment.witness(circuit.get_state().step_selection.index, curr_row); - assignment.witness(circuit.get_state().rows_counter_inv.index, curr_row); - assignment.witness(circuit.get_state().last_row_indicator.index, curr_row); - //circuit.state.assign_state(assignment, curr_row); Yes. We have to do it using machine. - }*/ - void finalize_test() { finalize(); - // this is done in order for the vizualiser export to work correctly before padding the circuit. - // otherwise constraints try to access non-existent rows - assignment.witness(circuit.get_state_selector()->W(0), curr_row) = value_type(0xC0FFEE); // Don't understand it std::cout << "Assignment rows amount = " << assignment.rows_amount() << std::endl; } @@ -130,8 +116,6 @@ namespace nil { assign_opcode(empty_machine); } - assignment.witness(circuit.get_state().last_row_indicator.index, curr_row - 1) = 1; - // Assign dynamic lookup tables typename zkevm_circuit::bytecode_table_component bytecode_table({ circuit.get_bytecode_witnesses()[0], circuit.get_bytecode_witnesses()[1], circuit.get_bytecode_witnesses()[2], @@ -143,7 +127,12 @@ namespace nil { void assign_opcode(zkevm_machine_interface &machine) { auto opcode = machine.opcode; - std::cout << "Assign opcode " << opcode_to_string(machine.opcode) << " on row " << curr_row << std::endl; + std::cout << "Assign opcode " << opcode_to_string(machine.opcode) + << " on row " << curr_row + << " pc = " << machine.pc + << " stack_size = " << machine.stack.size() + << " gas = " << machine.gas + << std::endl; const auto &opcodes = circuit.get_opcodes(); auto opcode_it = opcodes.find(opcode); if (opcode_it == opcodes.end()) { @@ -162,7 +151,7 @@ namespace nil { zkevm_opcode::err0 }; if (opcodes_with_args.find(opcode) == opcodes_with_args.end()) { - opcode_it->second->generate_assignments(*this, machine); + opcode_it->second->generate_assignments(*this, machine); } else { // for push opcodes we use the additional argument using pushx_op_type = zkevm_pushx_operation; @@ -176,19 +165,11 @@ namespace nil { } } curr_row += opcode_it->second->rows_amount() + opcode_it->second->rows_amount() % 2; - // post-opcode state management - // circuit.state.pc.value++; -- it's machine's business - // NB: we don't need to control stack size values here, because in a valid circuit they should alway be within the range - // machine.gas -= circuit.opcodes_info_instance.get_opcode_cost(opcode); -- machine can do it itself - BOOST_ASSERT(curr_row - circuit.get_start_row_index() < circuit.get_max_rows()); } void advance_rows( const zkevm_machine_interface &machine -// std::size_t opcode_height -// std::size_t internal_start_row, -// std::size_t shift = 0 ) { const auto &opcodes = circuit.get_opcodes(); auto opcode = machine.opcode; @@ -197,51 +178,47 @@ namespace nil { BOOST_ASSERT_MSG(false, (std::string("Unimplemented opcode: ") + opcode_to_string(opcode)) != ""); } std::size_t opcode_height = opcode_it->second->rows_amount(); - std::size_t local_row = curr_row; const auto &state = circuit.get_state(); // state management - value_type step_selection = 1; // internal variables - value_type last_row_indicator = 0; // internal variables - value_type rows_counter_inv; + value_type step_start = 1; // internal variables + value_type row_counter_inv; // for opcodes with odd height append one row if (opcode_it->second->rows_amount() % 2 ) { opcode_height++; } - rows_counter_inv = value_type(opcode_height - 1).inversed(); + row_counter_inv = value_type(opcode_height - 1).inversed(); std::size_t current_internal_row = opcode_height - 1; auto &opcodes_info_instance = circuit.get_opcodes_info(); // TODO: figure out what is going to happen on state change - value_type opcode_num = opcodes_info_instance.get_opcode_number(opcode); + std::size_t local_row = curr_row; + std::size_t opcode_num = opcodes_info_instance.get_opcode_number(opcode); + std::size_t opcode_half = ((opcode_num % 4 == 3) || (opcode_num % 4 == 2)); for (std::size_t i = 0; i < opcode_height; i++) { - if (i % circuit.get_state_selector()->rows_amount == 0) { - // TODO: switch to real bytecode - assignment.witness(circuit.get_state_selector()->W(0), local_row) = opcode_num; - components::generate_assignments( - *circuit.get_state_selector(), assignment, - {var(circuit.get_state_selector()->W(0), local_row, false, var::column_type::witness)}, local_row); + assignment.witness(state.opcode.index, local_row) = opcode_num; + if (i % 2 == opcode_half) { + components::generate_assignments(*circuit.get_opcode_selector(), assignment, {opcode_num/4}, local_row); } - assignment.witness(circuit.get_opcode_row_selector()->W(0), local_row) = current_internal_row; - components::generate_assignments( - *(circuit.get_opcode_row_selector()), assignment, - {var(circuit.get_opcode_row_selector()->W(0), local_row, false, var::column_type::witness)}, local_row); + assignment.witness(state.opcode_parity.index, local_row) = opcode_num%2; + + assignment.witness(state.row_counter.index, local_row) = current_internal_row; + components::generate_assignments(*(circuit.get_row_selector()), assignment, {current_internal_row/2}, local_row); assignment.witness(state.pc.index, local_row) = machine.pc; assignment.witness(state.gas.index, local_row) = machine.gas; assignment.witness(state.stack_size.index, local_row) = machine.stack.size(); assignment.witness(state.memory_size.index, local_row) = machine.memory.size(); - assignment.witness(state.step_selection.index, local_row) = step_selection; - assignment.witness(state.rows_counter_inv.index, local_row) = rows_counter_inv; - assignment.witness(state.last_row_indicator.index, local_row) = last_row_indicator; - if (i == 0) step_selection = 0; - assignment.witness(circuit.get_state_selector()->W(0), local_row) = opcode_num; + assignment.witness(state.step_start.index, local_row) = step_start; + assignment.witness(state.row_counter_inv.index, local_row) = row_counter_inv; + + if (i == 0) step_start = 0; current_internal_row--; - rows_counter_inv = current_internal_row == 0 ? 0 : value_type(current_internal_row).inversed(); + row_counter_inv = current_internal_row == 0 ? 0 : value_type(current_internal_row).inversed(); local_row++; } } @@ -254,6 +231,10 @@ namespace nil { return circuit.get_opcode_cols(); } + const std::size_t get_opcode_range_checked_cols_amount() const { + return circuit.get_opcode_range_checked_cols_amount(); + } + assignment_type &get_assignment(){ return assignment; }