From dc3a97688112893542fca371a268073aca41a8c5 Mon Sep 17 00:00:00 2001 From: Antoine Cyr Date: Tue, 4 Feb 2025 16:25:44 -0500 Subject: [PATCH 1/5] memory expansion util --- .../include/nil/blueprint/zkevm_bbf/util.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp index 6e467648b..a2f55ae55 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp @@ -89,6 +89,18 @@ namespace nil { while (d > 0) d /= 256u, ++count; return count; } + + std::size_t memory_size_word(std::size_t memory_byte_size){ + return (memory_byte_size + 31) / 32; + } + + std::size_t memory_cost(std::size_t memory_byte_size){ + return (memory_size_word(memory_byte_size) ** 2) / 512 + (3 * memory_size_word(memory_byte_size)); + } + + std::size_t memory_expansion_cost(std::size_t new_memory_byte_size, std::size_t last_memory_byte_size) { + return memory_cost(new_memory_byte_size) - memory_cost(last_memory_byte_size); + } } } } From c43d1f20562eaf5178fcb7d7c3eb79b7b0db6035 Mon Sep 17 00:00:00 2001 From: Antoine Cyr Date: Thu, 6 Feb 2025 14:25:35 -0500 Subject: [PATCH 2/5] memory expansion subcomponent --- .../opcode_tester_input_generator.hpp | 23 +++ .../zkevm_bbf/opcodes/calldatacopy.hpp | 185 +++++++++++------- .../zkevm_bbf/subcomponents/memory_cost.hpp | 91 +++++++++ .../zkevm_bbf/subcomponents/word_size.hpp | 83 ++++++++ .../include/nil/blueprint/zkevm_bbf/util.hpp | 8 +- crypto3/libs/blueprint/test/CMakeLists.txt | 1 + .../test/zkevm_bbf/opcodes/calldata.cpp | 95 +++++++++ 7 files changed, 407 insertions(+), 79 deletions(-) create mode 100644 crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp create mode 100644 crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp create mode 100644 crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp index 8a8c78416..cf7e9e625 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp @@ -40,6 +40,8 @@ #include #include +#include + namespace nil { namespace blueprint { namespace bbf { @@ -484,6 +486,27 @@ namespace nil { stack.push_back(result); pc++; gas -= 3; + } else if(opcode == zkevm_opcode::CALLDATACOPY){ + // 0x37 + + auto destOffset = std::size_t(stack.back()); + stack.pop_back(); + _rw_operations.push_back(stack_rw_operation(call_id, stack.size(), rw_counter++, false, destOffset)); + + auto offset = std::size_t(stack.back()); + stack.pop_back(); + _rw_operations.push_back(stack_rw_operation(call_id, stack.size(), rw_counter++, false, offset)); + + auto length = std::size_t(stack.back()); + stack.pop_back(); + _rw_operations.push_back(stack_rw_operation(call_id, stack.size(), rw_counter++, false, length)); + + std::size_t minimum_word_size = (length + 31) / 32; + std::size_t memory_expansion = memory_expansion_cost(state.memory_size + length, state.memory_size); + + gas-=3; //static gas + gas -= 3 * minimum_word_size + memory_expansion; //dynamic gas + pc++; } else if(opcode == zkevm_opcode::JUMP){ // 0x56 auto addr = std::size_t(stack.back()); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp index 4f0068022..9297ae49c 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp @@ -24,15 +24,17 @@ #pragma once -#include #include +#include #include +#include +#include #include namespace nil { namespace blueprint { - namespace bbf{ + namespace bbf { template class opcode_abstract; @@ -44,69 +46,100 @@ namespace nil { using generic_component::constrain; using generic_component::lookup; using generic_component::lookup_table; - public: - using typename generic_component::TYPE; - zkevm_calldatacopy_bbf(context_type &context_object, const opcode_input_type ¤t_state): - generic_component(context_object, false) - { - TYPE destOffset; - TYPE offset; - TYPE length; - if constexpr( stage == GenerationStage::ASSIGNMENT ){ + public: + using typename generic_component::TYPE; + + zkevm_calldatacopy_bbf( + context_type &context_object, + const opcode_input_type ¤t_state) + : generic_component(context_object, false) { + using Word_Size = typename bbf::word_size; + using Memory_Cost = typename bbf::memory_cost; + + TYPE destOffset, offset, length, current_mem, next_mem, + memory_expansion; + + if constexpr (stage == GenerationStage::ASSIGNMENT) { destOffset = w_lo(current_state.stack_top()); offset = w_lo(current_state.stack_top(1)); length = w_lo(current_state.stack_top(2)); + current_mem = current_state.memory_size; + next_mem = current_mem + length; } - allocate(destOffset,32,0); - allocate(offset,33,0); - allocate(length,34,0); - if constexpr( stage == GenerationStage::CONSTRAINTS ){ - // constrain(current_state.pc_next() - current_state.pc(0) - 1); // PC transition - // constrain(current_state.gas(0) - current_state.gas_next() - 3); // GAS transition - // constrain(current_state.stack_size_next() - current_state.stack_size(0) - 3); // stack_size transition - // constrain(current_state.memory_size(0) - current_state.memory_size_next()); // memory_size transition - // constrain(current_state.rw_counter_next() - current_state.rw_counter(0) - 2); // rw_counter transition - // std::vector tmp; - // tmp = { - // TYPE(rw_op_to_num(rw_operation_type::stack)), - // current_state.call_id(0), - // current_state.stack_size(0) - 1, - // TYPE(0),// storage_key_hi - // TYPE(0),// storage_key_lo - // TYPE(0),// field - // current_state.rw_counter(0), - // TYPE(0),// is_write - // TYPE(0), - // destOffset - // }; - // lookup(tmp, "zkevm_rw"); - // tmp = { - // TYPE(rw_op_to_num(rw_operation_type::stack)), - // current_state.call_id(0), - // current_state.stack_size(0) - 2, - // TYPE(0),// storage_key_hi - // TYPE(0),// storage_key_lo - // TYPE(0),// field - // current_state.rw_counter(0) + 1, - // TYPE(0),// is_write - // TYPE(0), - // offset - // }; - // lookup(tmp, "zkevm_rw"); - // tmp = { - // TYPE(rw_op_to_num(rw_operation_type::stack)), - // current_state.call_id(0), - // current_state.stack_size(0) - 3, - // TYPE(0),// storage_key_hi - // TYPE(0),// storage_key_lo - // TYPE(0),// field - // current_state.rw_counter(0) + 2, - // TYPE(0),// is_write - // TYPE(0), - // length - // }; - // lookup(tmp, "zkevm_rw"); + allocate(destOffset, 32, 0); + allocate(offset, 33, 0); + allocate(length, 34, 0); + allocate(current_mem, 35, 0); + allocate(next_mem, 36, 0); + constrain(next_mem - current_mem - length); + + Word_Size word = + Word_Size(context_object, length, {32, 33, 34, 35}, {1, 1, 1, 1}); + + Memory_Cost current_memory = Memory_Cost( + context_object, current_mem, {37, 38, 39, 40, 41, 42, 43, 44}, + {0, 0, 0, 0, 0, 0, 0, 0}); + + Memory_Cost next_memory = Memory_Cost( + context_object, next_mem, {37, 38, 39, 40, 41, 42, 43, 44}, + {1, 1, 1, 1, 1, 1, 1, 1}); + + allocate(memory_expansion, 45, 0); + memory_expansion = next_memory.cost - current_memory.cost; + + if constexpr (stage == GenerationStage::CONSTRAINTS) { + constrain(current_state.pc_next() - current_state.pc(0) - + 1); // PC transition + constrain(current_state.gas(0) - current_state.gas_next() - 3 - + 3 * word.size - memory_expansion); // GAS transition + constrain(current_state.stack_size(0) - current_state.stack_size_next() - 3); // stack_size transition + constrain( + current_state.memory_size(0) - + current_state.memory_size_next() ); // memory_size transition + constrain(current_state.rw_counter_next() - + current_state.rw_counter(0) - + 3); // rw_counter transition + std::vector tmp; + tmp = { + TYPE(rw_op_to_num(rw_operation_type::stack)), + current_state.call_id(0), + current_state.stack_size(0) - 1, + TYPE(0),// storage_key_hi + TYPE(0),// storage_key_lo + TYPE(0),// field + current_state.rw_counter(0), + TYPE(0),// is_write + TYPE(0), + destOffset + }; + lookup(tmp, "zkevm_rw"); + tmp = { + TYPE(rw_op_to_num(rw_operation_type::stack)), + current_state.call_id(0), + current_state.stack_size(0) - 2, + TYPE(0),// storage_key_hi + TYPE(0),// storage_key_lo + TYPE(0),// field + current_state.rw_counter(0) + 1, + TYPE(0),// is_write + TYPE(0), + offset + }; + lookup(tmp, "zkevm_rw"); + tmp = { + TYPE(rw_op_to_num(rw_operation_type::stack)), + current_state.call_id(0), + current_state.stack_size(0) - 3, + TYPE(0),// storage_key_hi + TYPE(0),// storage_key_lo + TYPE(0),// field + current_state.rw_counter(0) + 2, + TYPE(0),// is_write + TYPE(0), + length + }; + lookup(tmp, "zkevm_rw"); } else { std::cout << "\tSTATE transition implemented" << std::endl; } @@ -115,23 +148,25 @@ namespace nil { template class zkevm_calldatacopy_operation : public opcode_abstract { - public: - virtual std::size_t rows_amount() override { - return 1; - } + public: + virtual std::size_t rows_amount() override { return 2; } virtual void fill_context( - typename generic_component::context_type &context, - const opcode_input_type ¤t_state - ) override { - zkevm_calldatacopy_bbf bbf_obj(context, current_state); + typename generic_component< + FieldType, GenerationStage::ASSIGNMENT>::context_type &context, + const opcode_input_type + ¤t_state) override { + zkevm_calldatacopy_bbf + bbf_obj(context, current_state); } virtual void fill_context( - typename generic_component::context_type &context, - const opcode_input_type ¤t_state - ) override { - zkevm_calldatacopy_bbf bbf_obj(context, current_state); + typename generic_component< + FieldType, GenerationStage::CONSTRAINTS>::context_type &context, + const opcode_input_type + ¤t_state) override { + zkevm_calldatacopy_bbf + bbf_obj(context, current_state); } }; - } // namespace bbf - } // namespace blueprint -} // namespace nil + } // namespace bbf + } // namespace blueprint +} // namespace nil diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp new file mode 100644 index 000000000..1b6801aa5 --- /dev/null +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp @@ -0,0 +1,91 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2025 Antoine Cyr +// +// 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. +//---------------------------------------------------------------------------// + +// #include + +#ifndef CRYPTO3_BBF_MEMORY_COST_HPP +#define CRYPTO3_BBF_MEMORY_COST_HPP + +#include +#include + +namespace nil { + namespace blueprint { + namespace bbf { + + template + class memory_cost : public generic_component { + using generic_component::allocate; + using generic_component::copy_constrain; + using generic_component::constrain; + + public: + using typename generic_component::TYPE; + using typename generic_component::context_type; + + public: + TYPE cost; + + memory_cost(context_type &context_object, TYPE memory_input, + std::vector columns, std::vector rows) + : generic_component(context_object, false) { + assert(columns.size() == 8); + assert(rows.size() == 8); + + using integral_type = typename FieldType::integral_type; + TYPE mem_words, mem_cost, C, R; + using Word_Size = typename bbf::word_size; + + Word_Size word = + Word_Size(context_object, memory_input, + std::vector(columns.begin(), columns.begin() + 4), + std::vector(rows.begin(), rows.begin() + 4)); + mem_words = word.size; + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + integral_type memory2 = + integral_type(mem_words.data) * integral_type(mem_words.data); + mem_cost = memory2 / 512 + 3 * integral_type(mem_words.data); + R = memory2 % 512; + R.is_zero() ? C = 0 : C = 1; + } + + allocate(mem_words, columns[4], rows[4]); + allocate(mem_cost, columns[5], rows[5]); + allocate(R, columns[6], rows[6]); + allocate(C, columns[7], rows[7]); + + constrain(C * (1 - C)); + constrain((1 - C) * R); + constrain((mem_cost - 3 * mem_words) * 512 - mem_words * mem_words + + C * R); + + cost = mem_cost; + }; + }; + } // namespace bbf + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BBF_MEMORY_COST_HPP diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp new file mode 100644 index 000000000..2972d7f3f --- /dev/null +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp @@ -0,0 +1,83 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2025 Antoine Cyr +// +// 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. +//---------------------------------------------------------------------------// + +#ifndef CRYPTO3_BBF_WORD_SIZE_HPP +#define CRYPTO3_BBF_WORD_SIZE_HPP + +#include + +namespace nil { + namespace blueprint { + namespace bbf { + + template + class word_size : public generic_component { + using generic_component::allocate; + using generic_component::copy_constrain; + using generic_component::constrain; + + public: + using typename generic_component::TYPE; + using typename generic_component::context_type; + + public: + TYPE size; + + word_size(context_type &context_object, TYPE bite_size_input, + std::vector columns, std::vector rows, + bool make_links = true) + : generic_component(context_object, false) { + assert(columns.size() == 4); + assert(rows.size() == 4); + + using integral_type = typename FieldType::integral_type; + TYPE bites, words, R, C; + + if constexpr (stage == GenerationStage::ASSIGNMENT) { + bites = bite_size_input; + words = (integral_type(bites.data) + 31) / 32; + R = (integral_type(bites.data) + 31) % 32; + R.is_zero() ? C = 0 : C = 1; + } + + allocate(bites, columns[0], rows[0]); + allocate(words, columns[1], rows[1]); + allocate(R, columns[2], rows[2]); + allocate(C, columns[3], rows[3]); + + constrain(C * (1 - C)); + constrain((1 - C) * R); + constrain(words * 32 - bites - 31 + C * R); + + if (make_links) { + copy_constrain(bites, bite_size_input); + } + size = words; + }; + }; + } // namespace bbf + } // namespace blueprint +} // namespace nil + +#endif // CRYPTO3_BBF_WORD_SIZE_HPP diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp index a2f55ae55..10d7e6ab7 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/util.hpp @@ -90,16 +90,16 @@ namespace nil { return count; } - std::size_t memory_size_word(std::size_t memory_byte_size){ + std::size_t memory_size_word_util(std::size_t memory_byte_size){ return (memory_byte_size + 31) / 32; } - std::size_t memory_cost(std::size_t memory_byte_size){ - return (memory_size_word(memory_byte_size) ** 2) / 512 + (3 * memory_size_word(memory_byte_size)); + std::size_t memory_cost_util(std::size_t memory_byte_size){ + return (memory_size_word_util(memory_byte_size) * memory_size_word_util(memory_byte_size)) / 512 + (3 * memory_size_word_util(memory_byte_size)); } std::size_t memory_expansion_cost(std::size_t new_memory_byte_size, std::size_t last_memory_byte_size) { - return memory_cost(new_memory_byte_size) - memory_cost(last_memory_byte_size); + return memory_cost_util(new_memory_byte_size) - memory_cost_util(last_memory_byte_size); } } } diff --git a/crypto3/libs/blueprint/test/CMakeLists.txt b/crypto3/libs/blueprint/test/CMakeLists.txt index 8ef649a41..b791f0c92 100644 --- a/crypto3/libs/blueprint/test/CMakeLists.txt +++ b/crypto3/libs/blueprint/test/CMakeLists.txt @@ -228,6 +228,7 @@ set(ZKEVM_BBF_TESTS_FILES "zkevm_bbf/opcodes/jumps" "zkevm_bbf/opcodes/not" "zkevm_bbf/opcodes/exp" + "zkevm_bbf/opcodes/calldata" ) diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp new file mode 100644 index 000000000..d54dfc586 --- /dev/null +++ b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp @@ -0,0 +1,95 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Elena Tatuzova +// Copyright (c) 2025 Antoine Cur +// +// MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE blueprint_plonk_opcodes_test + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./opcode_test_fixture.hpp" + +using namespace nil::crypto3; +using namespace nil::blueprint::bbf; + +// Remember that in production sizes should be preset. +// Here they are different for different tests just for fast and easy testing +BOOST_FIXTURE_TEST_SUITE(zkevm_opcode_test_suite, zkEVMOpcodeTestFixture) +BOOST_AUTO_TEST_CASE(calldata) { + using field_type = typename algebra::curves::pallas::base_field_type; + zkevm_opcode_tester opcode_tester; + l1_size_restrictions max_sizes; + + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 32); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 8); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 31); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 32); + opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 64); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); + opcode_tester.push_opcode(zkevm_opcode::STOP); + + max_sizes.max_keccak_blocks = 10; + max_sizes.max_bytecode = 3000; + max_sizes.max_mpt = 0; + max_sizes.max_rw = 500; + max_sizes.max_copy = 500; + max_sizes.max_zkevm_rows = 100; + max_sizes.max_exp_rows = 500; + max_sizes.max_exponentiations = 50; + + complex_opcode_test(opcode_tester, max_sizes); +} +BOOST_AUTO_TEST_SUITE_END() From 200be3d8ff28cabfb49f2a1804f126eda87823cf Mon Sep 17 00:00:00 2001 From: Antoine Cyr Date: Fri, 7 Feb 2025 10:41:04 -0500 Subject: [PATCH 3/5] subcontext for memory_expansion subcomponent --- .../zkevm_bbf/opcodes/calldatacopy.hpp | 96 +++++++++---------- .../zkevm_bbf/subcomponents/memory_cost.hpp | 26 +++-- .../zkevm_bbf/subcomponents/word_size.hpp | 27 ++---- .../test/zkevm_bbf/opcodes/calldata.cpp | 4 + 4 files changed, 73 insertions(+), 80 deletions(-) diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp index 9297ae49c..98a12b947 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp @@ -72,20 +72,24 @@ namespace nil { allocate(length, 34, 0); allocate(current_mem, 35, 0); allocate(next_mem, 36, 0); - constrain(next_mem - current_mem - length); - Word_Size word = - Word_Size(context_object, length, {32, 33, 34, 35}, {1, 1, 1, 1}); + constrain(next_mem - current_mem - length); - Memory_Cost current_memory = Memory_Cost( - context_object, current_mem, {37, 38, 39, 40, 41, 42, 43, 44}, - {0, 0, 0, 0, 0, 0, 0, 0}); + std::vector word_size_lookup_area = {32, 33, 34, 35}; + std::vector memory_cost_lookup_area = {37, 38, 39,40,41,43,44,45}; + + context_type word_size_ct = + context_object.subcontext(word_size_lookup_area, 1, 1); + context_type current_memory_ct = + context_object.subcontext(memory_cost_lookup_area, 0, 1); + context_type next_memory_ct = + context_object.subcontext(memory_cost_lookup_area, 1, 1); - Memory_Cost next_memory = Memory_Cost( - context_object, next_mem, {37, 38, 39, 40, 41, 42, 43, 44}, - {1, 1, 1, 1, 1, 1, 1, 1}); + Word_Size word = Word_Size(word_size_ct, length); + Memory_Cost current_memory = Memory_Cost(current_memory_ct, current_mem); + Memory_Cost next_memory = Memory_Cost(next_memory_ct, next_mem); - allocate(memory_expansion, 45, 0); + allocate(memory_expansion, 36, 1); memory_expansion = next_memory.cost - current_memory.cost; if constexpr (stage == GenerationStage::CONSTRAINTS) { @@ -93,52 +97,48 @@ namespace nil { 1); // PC transition constrain(current_state.gas(0) - current_state.gas_next() - 3 - 3 * word.size - memory_expansion); // GAS transition - constrain(current_state.stack_size(0) - current_state.stack_size_next() - 3); // stack_size transition + constrain(current_state.stack_size(0) - + current_state.stack_size_next() - + 3); // stack_size transition constrain( current_state.memory_size(0) - - current_state.memory_size_next() ); // memory_size transition + current_state.memory_size_next()); // memory_size transition constrain(current_state.rw_counter_next() - current_state.rw_counter(0) - 3); // rw_counter transition std::vector tmp; - tmp = { - TYPE(rw_op_to_num(rw_operation_type::stack)), - current_state.call_id(0), - current_state.stack_size(0) - 1, - TYPE(0),// storage_key_hi - TYPE(0),// storage_key_lo - TYPE(0),// field - current_state.rw_counter(0), - TYPE(0),// is_write - TYPE(0), - destOffset - }; + tmp = {TYPE(rw_op_to_num(rw_operation_type::stack)), + current_state.call_id(0), + current_state.stack_size(0) - 1, + TYPE(0), // storage_key_hi + TYPE(0), // storage_key_lo + TYPE(0), // field + current_state.rw_counter(0), + TYPE(0), // is_write + TYPE(0), + destOffset}; lookup(tmp, "zkevm_rw"); - tmp = { - TYPE(rw_op_to_num(rw_operation_type::stack)), - current_state.call_id(0), - current_state.stack_size(0) - 2, - TYPE(0),// storage_key_hi - TYPE(0),// storage_key_lo - TYPE(0),// field - current_state.rw_counter(0) + 1, - TYPE(0),// is_write - TYPE(0), - offset - }; + tmp = {TYPE(rw_op_to_num(rw_operation_type::stack)), + current_state.call_id(0), + current_state.stack_size(0) - 2, + TYPE(0), // storage_key_hi + TYPE(0), // storage_key_lo + TYPE(0), // field + current_state.rw_counter(0) + 1, + TYPE(0), // is_write + TYPE(0), + offset}; lookup(tmp, "zkevm_rw"); - tmp = { - TYPE(rw_op_to_num(rw_operation_type::stack)), - current_state.call_id(0), - current_state.stack_size(0) - 3, - TYPE(0),// storage_key_hi - TYPE(0),// storage_key_lo - TYPE(0),// field - current_state.rw_counter(0) + 2, - TYPE(0),// is_write - TYPE(0), - length - }; + tmp = {TYPE(rw_op_to_num(rw_operation_type::stack)), + current_state.call_id(0), + current_state.stack_size(0) - 3, + TYPE(0), // storage_key_hi + TYPE(0), // storage_key_lo + TYPE(0), // field + current_state.rw_counter(0) + 2, + TYPE(0), // is_write + TYPE(0), + length}; lookup(tmp, "zkevm_rw"); } else { std::cout << "\tSTATE transition implemented" << std::endl; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp index 1b6801aa5..25f821e52 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp @@ -47,20 +47,18 @@ namespace nil { public: TYPE cost; - memory_cost(context_type &context_object, TYPE memory_input, - std::vector columns, std::vector rows) + memory_cost(context_type &context_object, TYPE memory_input) : generic_component(context_object, false) { - assert(columns.size() == 8); - assert(rows.size() == 8); - using integral_type = typename FieldType::integral_type; - TYPE mem_words, mem_cost, C, R; using Word_Size = typename bbf::word_size; - Word_Size word = - Word_Size(context_object, memory_input, - std::vector(columns.begin(), columns.begin() + 4), - std::vector(rows.begin(), rows.begin() + 4)); + TYPE mem_words, mem_cost, C, R; + std::vector word_size_lookup_area = {0, 1, 2, 3}; + + context_type word_size_ct = + context_object.subcontext(word_size_lookup_area, 0, 1); + + Word_Size word = Word_Size(word_size_ct, memory_input); mem_words = word.size; if constexpr (stage == GenerationStage::ASSIGNMENT) { @@ -71,10 +69,10 @@ namespace nil { R.is_zero() ? C = 0 : C = 1; } - allocate(mem_words, columns[4], rows[4]); - allocate(mem_cost, columns[5], rows[5]); - allocate(R, columns[6], rows[6]); - allocate(C, columns[7], rows[7]); + allocate(mem_words, 4, 0); + allocate(mem_cost, 5, 0); + allocate(R, 6, 0); + allocate(C, 7, 0); constrain(C * (1 - C)); constrain((1 - C) * R); diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp index 2972d7f3f..5e388f906 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp @@ -44,35 +44,26 @@ namespace nil { public: TYPE size; - word_size(context_type &context_object, TYPE bite_size_input, - std::vector columns, std::vector rows, - bool make_links = true) + word_size(context_type &context_object, TYPE bite_size_input) : generic_component(context_object, false) { - assert(columns.size() == 4); - assert(rows.size() == 4); - using integral_type = typename FieldType::integral_type; - TYPE bites, words, R, C; + TYPE words, R, C; if constexpr (stage == GenerationStage::ASSIGNMENT) { - bites = bite_size_input; - words = (integral_type(bites.data) + 31) / 32; - R = (integral_type(bites.data) + 31) % 32; + words = (integral_type(bite_size_input.data) + 31) / 32; + R = (integral_type(bite_size_input.data) + 31) % 32; R.is_zero() ? C = 0 : C = 1; } - allocate(bites, columns[0], rows[0]); - allocate(words, columns[1], rows[1]); - allocate(R, columns[2], rows[2]); - allocate(C, columns[3], rows[3]); + allocate(bite_size_input, 0, 0); + allocate(words, 1, 0); + allocate(R, 2, 0); + allocate(C, 3, 0); constrain(C * (1 - C)); constrain((1 - C) * R); - constrain(words * 32 - bites - 31 + C * R); + constrain(words * 32 - bite_size_input - 31 + C * R); - if (make_links) { - copy_constrain(bites, bite_size_input); - } size = words; }; }; diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp index d54dfc586..5ab6ab26a 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp @@ -79,6 +79,10 @@ BOOST_AUTO_TEST_CASE(calldata) { opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); + opcode_tester.push_opcode(zkevm_opcode::PUSH2, 800); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); opcode_tester.push_opcode(zkevm_opcode::STOP); max_sizes.max_keccak_blocks = 10; From a2faa0f8ca9f46d5f1b103f18670ee64c8701fe9 Mon Sep 17 00:00:00 2001 From: Antoine Cyr Date: Fri, 7 Feb 2025 16:57:13 -0500 Subject: [PATCH 4/5] correct calldatacopy logic --- .../opcode_tester_input_generator.hpp | 3 ++- .../zkevm_bbf/opcodes/calldatacopy.hpp | 25 ++++++++++++------- .../test/zkevm_bbf/opcodes/calldata.cpp | 4 +++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp index cf7e9e625..ab40e7ca2 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/input_generators/opcode_tester_input_generator.hpp @@ -502,7 +502,8 @@ namespace nil { _rw_operations.push_back(stack_rw_operation(call_id, stack.size(), rw_counter++, false, length)); std::size_t minimum_word_size = (length + 31) / 32; - std::size_t memory_expansion = memory_expansion_cost(state.memory_size + length, state.memory_size); + std::size_t next_mem = std::max(destOffset + length, state.memory_size); + std::size_t memory_expansion = memory_expansion_cost(next_mem, state.memory_size); gas-=3; //static gas gas -= 3 * minimum_word_size + memory_expansion; //dynamic gas diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp index 98a12b947..7dd8053a6 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp @@ -58,40 +58,47 @@ namespace nil { using Memory_Cost = typename bbf::memory_cost; TYPE destOffset, offset, length, current_mem, next_mem, - memory_expansion; + memory_expansion, S; if constexpr (stage == GenerationStage::ASSIGNMENT) { destOffset = w_lo(current_state.stack_top()); offset = w_lo(current_state.stack_top(1)); length = w_lo(current_state.stack_top(2)); current_mem = current_state.memory_size; - next_mem = current_mem + length; + next_mem = std::max(destOffset + length, current_mem); + S = next_mem > current_mem; } allocate(destOffset, 32, 0); allocate(offset, 33, 0); allocate(length, 34, 0); allocate(current_mem, 35, 0); allocate(next_mem, 36, 0); + allocate(S, 37, 0); - constrain(next_mem - current_mem - length); + constrain(S * (S - 1)); + constrain(S * (next_mem - destOffset - length) + + (1 - S) * (next_mem - current_mem)); std::vector word_size_lookup_area = {32, 33, 34, 35}; - std::vector memory_cost_lookup_area = {37, 38, 39,40,41,43,44,45}; - + std::vector memory_cost_lookup_area = {38, 39, 40, 41, + 43, 44, 45, 46}; + context_type word_size_ct = context_object.subcontext(word_size_lookup_area, 1, 1); + context_type current_memory_ct = context_object.subcontext(memory_cost_lookup_area, 0, 1); context_type next_memory_ct = context_object.subcontext(memory_cost_lookup_area, 1, 1); - Word_Size word = Word_Size(word_size_ct, length); - Memory_Cost current_memory = Memory_Cost(current_memory_ct, current_mem); + Memory_Cost current_memory = + Memory_Cost(current_memory_ct, current_mem); Memory_Cost next_memory = Memory_Cost(next_memory_ct, next_mem); - - allocate(memory_expansion, 36, 1); memory_expansion = next_memory.cost - current_memory.cost; + Word_Size word = Word_Size(word_size_ct, length); + allocate(memory_expansion, 36, 1); + if constexpr (stage == GenerationStage::CONSTRAINTS) { constrain(current_state.pc_next() - current_state.pc(0) - 1); // PC transition diff --git a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp index 5ab6ab26a..db681b290 100644 --- a/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp +++ b/crypto3/libs/blueprint/test/zkevm_bbf/opcodes/calldata.cpp @@ -83,6 +83,10 @@ BOOST_AUTO_TEST_CASE(calldata) { opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::PUSH1, 0); + opcode_tester.push_opcode(zkevm_opcode::CALLDATACOPY); opcode_tester.push_opcode(zkevm_opcode::STOP); max_sizes.max_keccak_blocks = 10; From 6103a46beadd1abc0bcee57c4ce494b5730dc901 Mon Sep 17 00:00:00 2001 From: Antoine Cyr Date: Mon, 10 Feb 2025 16:31:20 -0500 Subject: [PATCH 5/5] improvment word_size --- .../zkevm_bbf/opcodes/calldatacopy.hpp | 8 ++++---- .../zkevm_bbf/subcomponents/memory_cost.hpp | 17 ++++++----------- .../zkevm_bbf/subcomponents/word_size.hpp | 18 +++++++----------- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp index 7dd8053a6..eb4ecb462 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/opcodes/calldatacopy.hpp @@ -79,9 +79,10 @@ namespace nil { constrain(S * (next_mem - destOffset - length) + (1 - S) * (next_mem - current_mem)); - std::vector word_size_lookup_area = {32, 33, 34, 35}; - std::vector memory_cost_lookup_area = {38, 39, 40, 41, - 43, 44, 45, 46}; + std::vector word_size_lookup_area = {32, 33, 34}; + allocate(memory_expansion, 35, 1); + std::vector memory_cost_lookup_area = {42, 43, 44, + 45, 46,47}; context_type word_size_ct = context_object.subcontext(word_size_lookup_area, 1, 1); @@ -97,7 +98,6 @@ namespace nil { memory_expansion = next_memory.cost - current_memory.cost; Word_Size word = Word_Size(word_size_ct, length); - allocate(memory_expansion, 36, 1); if constexpr (stage == GenerationStage::CONSTRAINTS) { constrain(current_state.pc_next() - current_state.pc(0) - diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp index 25f821e52..ea02d15ce 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/memory_cost.hpp @@ -52,8 +52,8 @@ namespace nil { using integral_type = typename FieldType::integral_type; using Word_Size = typename bbf::word_size; - TYPE mem_words, mem_cost, C, R; - std::vector word_size_lookup_area = {0, 1, 2, 3}; + TYPE mem_words, mem_cost, R; + std::vector word_size_lookup_area = {0, 1, 2}; context_type word_size_ct = context_object.subcontext(word_size_lookup_area, 0, 1); @@ -66,18 +66,13 @@ namespace nil { integral_type(mem_words.data) * integral_type(mem_words.data); mem_cost = memory2 / 512 + 3 * integral_type(mem_words.data); R = memory2 % 512; - R.is_zero() ? C = 0 : C = 1; } - allocate(mem_words, 4, 0); - allocate(mem_cost, 5, 0); - allocate(R, 6, 0); - allocate(C, 7, 0); - - constrain(C * (1 - C)); - constrain((1 - C) * R); + allocate(mem_words, 3, 0); + allocate(mem_cost, 4, 0); + allocate(R, 5, 0); constrain((mem_cost - 3 * mem_words) * 512 - mem_words * mem_words + - C * R); + R); cost = mem_cost; }; diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp index 5e388f906..cd17bc80e 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm_bbf/subcomponents/word_size.hpp @@ -44,25 +44,21 @@ namespace nil { public: TYPE size; - word_size(context_type &context_object, TYPE bite_size_input) + // Computes the number of 32-byte words required to store a given number of bytes. + word_size(context_type &context_object, TYPE byte_size_input) : generic_component(context_object, false) { using integral_type = typename FieldType::integral_type; - TYPE words, R, C; + TYPE words, R; if constexpr (stage == GenerationStage::ASSIGNMENT) { - words = (integral_type(bite_size_input.data) + 31) / 32; - R = (integral_type(bite_size_input.data) + 31) % 32; - R.is_zero() ? C = 0 : C = 1; + words = (integral_type(byte_size_input.data) + 31) / 32; + R = (integral_type(byte_size_input.data) + 31) % 32; } - allocate(bite_size_input, 0, 0); + allocate(byte_size_input, 0, 0); allocate(words, 1, 0); allocate(R, 2, 0); - allocate(C, 3, 0); - - constrain(C * (1 - C)); - constrain((1 - C) * R); - constrain(words * 32 - bite_size_input - 31 + C * R); + constrain(words * 32 - byte_size_input - 31 + R); size = words; };