From 4e51fc07493a54cb91d36d0f24b2b07bf434af26 Mon Sep 17 00:00:00 2001 From: akokoshn Date: Wed, 19 Jun 2024 11:37:26 +0300 Subject: [PATCH 1/3] Example init bytecode circuit --- lib/assigner/evmone/baseline.hpp | 2 + lib/assigner/include/assigner.hpp | 6 ++ lib/assigner/include/bytecode.hpp | 108 ++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 lib/assigner/include/bytecode.hpp diff --git a/lib/assigner/evmone/baseline.hpp b/lib/assigner/evmone/baseline.hpp index 011f7d7..4179ef0 100644 --- a/lib/assigner/evmone/baseline.hpp +++ b/lib/assigner/evmone/baseline.hpp @@ -235,6 +235,8 @@ int64_t dispatch(const CostTable& cost_table, ExecutionState // Code iterator and stack top pointer for interpreter loop. Position position{code, stack_bottom}; + // fill assignments for bytecode circuit + state.assigner->handle_bytcode(&state, code); while (true) // Guaranteed to terminate because padded code ends with STOP. { const auto op = *position.code_it; diff --git a/lib/assigner/include/assigner.hpp b/lib/assigner/include/assigner.hpp index 3766079..9b364d9 100644 --- a/lib/assigner/include/assigner.hpp +++ b/lib/assigner/include/assigner.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -26,6 +27,11 @@ namespace nil { assigner(std::vector> &assignments): m_assignments(assignments) {} + // TODO error handling + void handle_bytcode(const typename evmone::ExecutionState* state, const uint8_t* code) { + return process_bytecode_input(state->original_code.size(), code, m_assignments); + } + std::vector> &m_assignments; }; diff --git a/lib/assigner/include/bytecode.hpp b/lib/assigner/include/bytecode.hpp new file mode 100644 index 0000000..7bd585b --- /dev/null +++ b/lib/assigner/include/bytecode.hpp @@ -0,0 +1,108 @@ +//---------------------------------------------------------------------------// +// Copyright (c) Nil Foundation and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. +//---------------------------------------------------------------------------// + +#ifndef EVM1_ASSIGNER_INCLUDE_BYTECODE_HPP_ +#define EVM1_ASSIGNER_INCLUDE_BYTECODE_HPP_ + +#include + +#include +#include +#include + +namespace nil { + namespace blueprint { + + template + void process_bytecode_input(size_t original_code_size, const uint8_t* code, + std::vector>> &assignments) { + using value_type = typename BlueprintFieldType::value_type; + using hash_type = nil::crypto3::hashes::keccak_1600<256>; + + std::cout << "MYINFO: " << std::endl; + std::cout << "MYINFO: bytecode size: " << original_code_size << std::endl; + std::cout << "MYINFO: bytecode: " << std::endl; + + std::vector bytecode; + for (size_t i = 0; i < original_code_size; i++) { + const auto op = code[i]; + bytecode.push_back(op); + std::cout << (uint)op << " "; + } + std::cout << std::endl; + + std::cout << "MYINFO: :" << std::endl; + std::string hash = nil::crypto3::hash>(bytecode.begin(), bytecode.end()); + std::string str_hi = hash.substr(0, hash.size()-32); + std::string str_lo = hash.substr(hash.size()-32, 32); + value_type hash_hi; + value_type hash_lo; + for( std::size_t j = 0; j < str_hi.size(); j++ ){hash_hi *=16; hash_hi += str_hi[j] >= '0' && str_hi[j] <= '9'? str_hi[j] - '0' : str_hi[j] - 'a' + 10;} + for( std::size_t j = 0; j < str_lo.size(); j++ ){hash_lo *=16; hash_lo += str_lo[j] >= '0' && str_lo[j] <= '9'? str_lo[j] - '0' : str_lo[j] - 'a' + 10;} + std::cout << std::hex << "Contract hash = " << hash << " h:" << hash_hi << " l:" << hash_lo << std::dec << std::endl; + + static constexpr uint32_t TAG = 0; + static constexpr uint32_t INDEX = 1; + static constexpr uint32_t VALUE = 2; + static constexpr uint32_t IS_OPCODE = 3; + static constexpr uint32_t PUSH_SIZE = 4; + static constexpr uint32_t LENGTH_LEFT = 5; + static constexpr uint32_t HASH_HI = 6; + static constexpr uint32_t HASH_LO = 7; + static constexpr uint32_t VALUE_RLC = 8; + static constexpr uint32_t RLC_CHALLENGE = 9; + + value_type rlc_challenge = 15; + uint32_t cur = 0; + // no one another circuit uses witness column VALUE from 1th table + uint32_t start_row_index = assignments[0].witness_column_size(VALUE); + std::size_t prev_length = 0; + value_type prev_vrlc = 0; + value_type push_size = 0; + for(size_t j = 0; j < original_code_size; j++, cur++){ + std::uint8_t byte = bytecode[j]; + assignments[0].witness(VALUE, start_row_index + cur) = bytecode[j]; + assignments[0].witness(HASH_HI, start_row_index + cur) = hash_hi; + assignments[0].witness(HASH_LO, start_row_index + cur) = hash_lo; + assignments[0].witness(RLC_CHALLENGE, start_row_index + cur) = rlc_challenge; + if( j == 0) { + // HEADER + assignments[0].witness(TAG, start_row_index + cur) = 0; + assignments[0].witness(INDEX, start_row_index + cur) = 0; + assignments[0].witness(IS_OPCODE, start_row_index + cur) = 0; + assignments[0].witness(PUSH_SIZE, start_row_index + cur) = 0; + prev_length = bytecode[j]; + assignments[0].witness(LENGTH_LEFT, start_row_index + cur) = bytecode[j]; + prev_vrlc = 0; + assignments[0].witness(VALUE_RLC, start_row_index + cur) = 0; + push_size = 0; + } else { + // BYTE + assignments[0].witness(TAG, start_row_index + cur) = 1; + assignments[0].witness(INDEX, start_row_index + cur) = j-1; + assignments[0].witness(LENGTH_LEFT, start_row_index + cur) = prev_length - 1; + prev_length = prev_length - 1; + if (push_size == 0) { + assignments[0].witness(IS_OPCODE, start_row_index + cur) = 1; + if(byte > 0x5f && byte < 0x80) { + push_size = byte - 0x5f; + } + } else { + assignments[0].witness(IS_OPCODE, start_row_index + cur) = 0; + push_size--; + } + assignments[0].witness(PUSH_SIZE, start_row_index + cur) = push_size; + assignments[0].witness(VALUE_RLC, start_row_index + cur) = prev_vrlc * rlc_challenge + byte; + prev_vrlc = prev_vrlc * rlc_challenge + byte; + } + } + } + + } // namespace blueprint +} // namespace nil + +#endif // EVM1_ASSIGNER_INCLUDE_BYTECODE_HPP_ From 3021b99745f55426f4a59842559b7cd06db9b0ba Mon Sep 17 00:00:00 2001 From: akokoshn Date: Fri, 28 Jun 2024 11:45:32 +0300 Subject: [PATCH 2/3] Add read/write circuit --- flake.lock | 74 ++++- lib/assigner/CMakeLists.txt | 5 +- lib/assigner/evmone/baseline.hpp | 2 - lib/assigner/evmone/execution_state.hpp | 7 +- lib/assigner/evmone/instructions.hpp | 405 +++++++++++++++++++----- lib/assigner/include/assigner.hpp | 33 +- lib/assigner/include/bytecode.hpp | 3 +- lib/assigner/include/rw.hpp | 284 +++++++++++++++++ lib/assigner/include/vm_host.hpp | 2 +- lib/assigner/include/zkevm_word.hpp | 45 ++- lib/assigner/test/assigner_test.cpp | 68 +++- 11 files changed, 815 insertions(+), 113 deletions(-) create mode 100644 lib/assigner/include/rw.hpp diff --git a/flake.lock b/flake.lock index fd0af26..9592af6 100644 --- a/flake.lock +++ b/flake.lock @@ -18,18 +18,37 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nil_crypto3": { "inputs": { + "nix-3rdparty": "nix-3rdparty", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1717659149, - "narHash": "sha256-aWvONWN0Q7Kx6NDvwpeydO1GpdlinI5RwPlaQly9HsA=", + "lastModified": 1718821034, + "narHash": "sha256-j3fsPiltYzctIgAWakAHdPLAxq+UiXXnKNmAu+oN7Us=", "ref": "refs/heads/master", - "rev": "73ded556ea77147ce7afd3dbeed8e21f5f4d81db", - "revCount": 9117, + "rev": "caf78b83692b39f504c4ee74a3834a4c38662a77", + "revCount": 9146, "submodules": true, "type": "git", "url": "https://github.com/NilFoundation/crypto3" @@ -53,11 +72,11 @@ ] }, "locked": { - "lastModified": 1717666696, - "narHash": "sha256-/hDnXfn4mrmhfP89ZRBf7TRqISkTqxbZhdZc2oqBDko=", + "lastModified": 1719590043, + "narHash": "sha256-C2O0EAmWOR+Fq+lNqumwd7YS9+TBDkWGNpS1CjCAwvY=", "ref": "refs/heads/master", - "rev": "8a3c4059616b0dd2183506e7ec8740e584ea443a", - "revCount": 1230, + "rev": "84ebc6dbdd94256424904c8270cb16d764236a31", + "revCount": 1243, "submodules": true, "type": "git", "url": "https://github.com/NilFoundation/zkllvm-blueprint" @@ -69,6 +88,28 @@ } }, "nix-3rdparty": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nil_crypto3", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1717519917, + "narHash": "sha256-GqzEqEW4Uz9Z7uDZwers0t9seWRNbRWPNE3OJnjE1Uw=", + "owner": "NilFoundation", + "repo": "nix-3rdparty", + "rev": "a2e45429aa25a4a6e8e362ef17df6f197312f224", + "type": "github" + }, + "original": { + "owner": "NilFoundation", + "repo": "nix-3rdparty", + "type": "github" + } + }, + "nix-3rdparty_2": { "inputs": { "flake-utils": [ "flake-utils" @@ -112,7 +153,7 @@ "flake-utils": "flake-utils", "nil_crypto3": "nil_crypto3", "nil_zkllvm_blueprint": "nil_zkllvm_blueprint", - "nix-3rdparty": "nix-3rdparty", + "nix-3rdparty": "nix-3rdparty_2", "nixpkgs": "nixpkgs" } }, @@ -130,6 +171,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/lib/assigner/CMakeLists.txt b/lib/assigner/CMakeLists.txt index d26ae99..dd684bb 100644 --- a/lib/assigner/CMakeLists.txt +++ b/lib/assigner/CMakeLists.txt @@ -15,8 +15,9 @@ add_library(${PROJECT_NAME} STATIC ${evmone_sources}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) +find_package(Boost REQUIRED COMPONENTS container random filesystem log log_setup program_options thread system) find_package(crypto3 REQUIRED) -find_package(blueprint_crypto3 REQUIRED) +find_package(crypto3_blueprint REQUIRED) target_include_directories(${PROJECT_NAME} PUBLIC $ @@ -24,7 +25,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC $) target_link_libraries(${PROJECT_NAME} - PUBLIC evmc::evmc intx::intx crypto3::all blueprint ethash::keccak) + PUBLIC evmc::evmc intx::intx crypto3::all crypto3::blueprint ethash::keccak) set_target_properties( ${PROJECT_NAME} diff --git a/lib/assigner/evmone/baseline.hpp b/lib/assigner/evmone/baseline.hpp index 4179ef0..011f7d7 100644 --- a/lib/assigner/evmone/baseline.hpp +++ b/lib/assigner/evmone/baseline.hpp @@ -235,8 +235,6 @@ int64_t dispatch(const CostTable& cost_table, ExecutionState // Code iterator and stack top pointer for interpreter loop. Position position{code, stack_bottom}; - // fill assignments for bytecode circuit - state.assigner->handle_bytcode(&state, code); while (true) // Guaranteed to terminate because padded code ends with STOP. { const auto op = *position.code_it; diff --git a/lib/assigner/evmone/execution_state.hpp b/lib/assigner/evmone/execution_state.hpp index 661a5c5..d9ef707 100644 --- a/lib/assigner/evmone/execution_state.hpp +++ b/lib/assigner/evmone/execution_state.hpp @@ -8,6 +8,7 @@ #include #include +#include namespace nil { namespace blueprint { @@ -143,6 +144,9 @@ class ExecutionState evmc_status_code status = EVMC_SUCCESS; size_t output_offset = 0; size_t output_size = 0; + + std::size_t call_id; + std::vector> rw_trace; std::shared_ptr> assigner; private: @@ -167,12 +171,13 @@ class ExecutionState ExecutionState(const evmc_message& message, evmc_revision revision, const evmc_host_interface& host_interface, evmc_host_context* host_ctx, bytes_view _code, - bytes_view _data, std::shared_ptr> _assigner) noexcept + bytes_view _data, size_t _call_id, std::shared_ptr> _assigner) noexcept : msg{&message}, host{host_interface, host_ctx}, rev{revision}, original_code{_code}, data{_data}, + call_id{_call_id}, assigner{_assigner} {} diff --git a/lib/assigner/evmone/instructions.hpp b/lib/assigner/evmone/instructions.hpp index 3f8c9f5..666edfa 100644 --- a/lib/assigner/evmone/instructions.hpp +++ b/lib/assigner/evmone/instructions.hpp @@ -38,6 +38,9 @@ class StackTop /// Assigns the value to the stack top and moves the stack top pointer up. void push(const nil::blueprint::zkevm_word& value) noexcept { *++m_top = value; } + + /// size of stack + uint16_t size(nil::blueprint::zkevm_word* bottom) noexcept { return (m_top > bottom) ? (uint16_t)(m_top - bottom) : 0; } }; @@ -249,65 +252,99 @@ struct instructions { return stop_impl(stack, gas_left, state, EVMC_INVALID_INSTRUCTION); } - static void add(StackTop stack) noexcept + static void add(StackTop stack, ExecutionState& state) noexcept { - stack.top() = stack.top() + stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + stack.top() = stack.top() + stack.pop();// calculate stack next + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void mul(StackTop stack, ExecutionState& state) noexcept { - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(0, 0) = stack[0].to_uint64(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(0, 1) = stack[1].to_uint64(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() * stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void sub(StackTop stack) noexcept + static void sub(StackTop stack, ExecutionState& state) noexcept { - stack[1] = stack[0] - stack[1]; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + const auto& x = stack.pop(); + stack[0] = x - stack[0]; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void div(StackTop stack) noexcept + static void div(StackTop stack, ExecutionState& state) noexcept { - auto& v = stack[1]; - v = v != 0 ? stack[0] / v : 0; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + const auto& x = stack.pop(); + auto& v = stack[0]; + v = v != 0 ? x / v : 0; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void sdiv(StackTop stack) noexcept + static void sdiv(StackTop stack, ExecutionState& state) noexcept { - auto& v = stack[1]; - v = stack[0].sdiv(v); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + const auto& x = stack.pop(); + auto& v = stack[0]; + v = x.sdiv(v); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void mod(StackTop stack) noexcept + static void mod(StackTop stack, ExecutionState& state) noexcept { - auto& v = stack[1]; - v = v != 0 ? stack[0] % v : 0; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + const auto& x = stack.pop(); + auto& v = stack[0]; + v = v != 0 ? x % v : 0; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void smod(StackTop stack) noexcept + static void smod(StackTop stack, ExecutionState& state) noexcept { - auto& v = stack[1]; - v = stack[0].smod(v); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + const auto& x = stack.pop(); + auto& v = stack[0]; + v = x.smod(v); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void addmod(StackTop stack) noexcept + static void addmod(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& x = stack.pop(); const auto& y = stack.pop(); auto& m = stack.top(); m = x.addmod(y, m); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void mulmod(StackTop stack) noexcept + static void mulmod(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& x = stack[0]; const auto& y = stack[1]; auto& m = stack[2]; m = x.mulmod(y, m); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result exp(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& base = stack.pop(); auto& exponent = stack.top(); @@ -318,11 +355,14 @@ struct instructions { return {EVMC_OUT_OF_GAS, gas_left}; exponent = base.exp(exponent); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } - static void signextend(StackTop stack) noexcept + static void signextend(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& ext = stack.pop(); auto& x = stack.top(); @@ -352,64 +392,96 @@ struct instructions { for (size_t i = 3; i > sign_word_index; --i) x.set_val(sign_ex, i); // Clear extended words. } + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void lt(StackTop stack) noexcept + static void lt(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& x = stack.pop(); stack[0] = x < stack[0]; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void gt(StackTop stack) noexcept + static void gt(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& x = stack.pop(); stack[0] = stack[0] < x; // Arguments are swapped and < is used. + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void slt(StackTop stack) noexcept + static void slt(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& x = stack.pop(); stack[0] = x.slt(stack[0]); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void sgt(StackTop stack) noexcept + static void sgt(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& x = stack.pop(); stack[0] = stack[0].slt(x); // Arguments are swapped and SLT is used. + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void eq(StackTop stack) noexcept + static void eq(StackTop stack, ExecutionState& state) noexcept { - stack[1] = stack[0] == stack[1]; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); + const auto& x = stack.pop(); + stack[0] = stack[0] == x; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void iszero(StackTop stack) noexcept + static void iszero(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() == 0; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void and_(StackTop stack) noexcept + static void and_(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() & stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void or_(StackTop stack) noexcept + static void or_(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() | stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void xor_(StackTop stack) noexcept + static void xor_(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() ^stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void not_(StackTop stack) noexcept + static void not_(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = ~stack.top(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void byte(StackTop stack) noexcept + static void byte(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& n = stack.pop(); auto& x = stack.top(); @@ -421,20 +493,29 @@ struct instructions { const auto byte_index = index % 8; const auto byte = (word >> (byte_index * 8)) & byte_mask; x = nil::blueprint::zkevm_word(byte); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void shl(StackTop stack) noexcept + static void shl(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() << stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void shr(StackTop stack) noexcept + static void shr(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); stack.top() = stack.top() >> stack.pop(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static void sar(StackTop stack) noexcept + static void sar(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& y = stack.pop(); auto& x = stack.top(); @@ -443,10 +524,13 @@ struct instructions { const auto mask_shift = (y < 256) ? (256 - y.to_uint64(0)) : 0; x = (x >> y) | (sign_mask << mask_shift); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result keccak256(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& index = stack.pop(); auto& size = stack.top(); @@ -460,8 +544,15 @@ struct instructions { if ((gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; - auto data = s != 0 ? &state.memory[i] : nullptr; + uint8_t* data = nullptr; + if (s != 0 ) { + data = &state.memory[i]; + for(uint64_t j = 0; j < 32; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, index + j, state.rw_trace.size(), false, data[j])); + } + } size = nil::blueprint::zkevm_word(ethash::keccak256(data, s)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } @@ -469,10 +560,12 @@ struct instructions { static void address(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.msg->recipient)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result balance(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& x = stack.top(); const auto addr = x.to_address(); @@ -483,30 +576,33 @@ struct instructions { } x = nil::blueprint::zkevm_word(state.host.get_balance(addr)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } static void origin(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().tx_origin)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void caller(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.msg->sender)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void callvalue(StackTop stack, ExecutionState& state) noexcept { auto val = nil::blueprint::zkevm_word(state.msg->value); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 0) = val.to_uint64(); stack.push(val); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void calldataload(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& index = stack.top(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 1) = index.to_uint64(); const auto index_uint64 = index.to_uint64(); if (state.msg->input_size < index_uint64) @@ -522,15 +618,20 @@ struct instructions { index = nil::blueprint::zkevm_word(data, 32); } + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void calldatasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.msg->input_size); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result calldatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); const auto& size = stack.pop(); @@ -553,17 +654,25 @@ struct instructions { if (s - copy_size > 0) std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + for(uint64_t j = 0; j < copy_size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, mem_index + j, state.rw_trace.size(), true, state.memory[dst + j])); + } + return {EVMC_SUCCESS, gas_left}; } static void codesize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.original_code.size()); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result codecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { // TODO: Similar to calldatacopy(). + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); @@ -589,6 +698,10 @@ struct instructions { if (s - copy_size > 0) std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + for(uint64_t j = 0; j < copy_size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, mem_index + j, state.rw_trace.size(), true, state.memory[dst + j])); + } + return {EVMC_SUCCESS, gas_left}; } @@ -596,15 +709,18 @@ struct instructions { static void gasprice(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().tx_gas_price)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void basefee(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_base_fee)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void blobhash(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& index = stack.top(); const auto& tx = state.get_tx_context(); const auto index_uin64 = index.to_uint64(); @@ -612,15 +728,18 @@ struct instructions { index = (index_uin64 < tx.blob_hashes_count) ? nil::blueprint::zkevm_word(tx.blob_hashes[index_uin64]) : 0; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void blobbasefee(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().blob_base_fee)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result extcodesize(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& x = stack.top(); const auto addr = x.to_address(); @@ -631,11 +750,16 @@ struct instructions { } x = state.host.get_code_size(addr); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } static Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-4, state.rw_trace.size(), false, stack[3])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto addr = stack.pop().to_address(); const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); @@ -663,6 +787,7 @@ struct instructions { const auto num_bytes_copied = state.host.copy_code(addr, src, &state.memory[dst], s); if (const auto num_bytes_to_clear = s - num_bytes_copied; num_bytes_to_clear > 0) std::memset(&state.memory[dst + num_bytes_copied], 0, num_bytes_to_clear); + // TODO: add length write operations to memory } return {EVMC_SUCCESS, gas_left}; @@ -671,10 +796,14 @@ struct instructions { static void returndatasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.return_data.size()); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& mem_index = stack.pop(); const auto& input_index = stack.pop(); const auto& size = stack.pop(); @@ -695,14 +824,19 @@ struct instructions { if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; - if (s > 0) + if (s > 0) { std::memcpy(&state.memory[dst], &state.return_data[src], s); + for(uint64_t j = 0; j < s; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, dst + j, state.rw_trace.size(), true, state.memory[dst + j])); + } + } return {EVMC_SUCCESS, gas_left}; } static Result extcodehash(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& x = stack.top(); const auto addr = x.to_address(); @@ -713,12 +847,14 @@ struct instructions { } x = nil::blueprint::zkevm_word(state.host.get_code_hash(addr)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } static void blockhash(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& number = stack.top(); const auto upper_bound = state.get_tx_context().block_number; @@ -728,84 +864,106 @@ struct instructions { (decltype(upper_bound)(n) < upper_bound && decltype(upper_bound)(n) >= lower_bound) ? state.host.get_block_hash(decltype(upper_bound)(n)) : evmc::bytes32{}; number = nil::blueprint::zkevm_word(header); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void coinbase(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_coinbase)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void timestamp(StackTop stack, ExecutionState& state) noexcept { // TODO: Add tests for negative timestamp? stack.push(static_cast(state.get_tx_context().block_timestamp)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void number(StackTop stack, ExecutionState& state) noexcept { // TODO: Add tests for negative block number? stack.push(static_cast(state.get_tx_context().block_number)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void prevrandao(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().block_prev_randao)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void gaslimit(StackTop stack, ExecutionState& state) noexcept { stack.push(static_cast(state.get_tx_context().block_gas_limit)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void chainid(StackTop stack, ExecutionState& state) noexcept { stack.push(nil::blueprint::zkevm_word(state.get_tx_context().chain_id)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static void selfbalance(StackTop stack, ExecutionState& state) noexcept { // TODO: introduce selfbalance in EVMC? stack.push(nil::blueprint::zkevm_word(state.host.get_balance(state.msg->recipient))); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } template static Result mload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& index = stack.top(); - if (!check_memory(gas_left, state.memory, index, (BlueprintFieldType::modulus_bits / 8))) + if (!check_memory(gas_left, state.memory, index, nil::blueprint::zkevm_word::size)) return {EVMC_OUT_OF_GAS, gas_left}; - index = nil::blueprint::zkevm_word(&state.memory[index.to_uint64()], (BlueprintFieldType::modulus_bits / 8)); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(2, 2) = index.to_uint64(); + const auto addr = index.to_uint64(); + index = nil::blueprint::zkevm_word(&state.memory[addr], nil::blueprint::zkevm_word::size); + for(uint64_t j = 0; j < nil::blueprint::zkevm_word::size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, addr + j, state.rw_trace.size(), false, state.memory[addr + j])); + } + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } template static Result mstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& index = stack.pop(); auto& value = stack.pop(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(2, 0) = value.to_uint64(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(2, 1) = index.to_uint64(); - - if (!check_memory(gas_left, state.memory, index, (BlueprintFieldType::modulus_bits / 8))) + if (!check_memory(gas_left, state.memory, index, nil::blueprint::zkevm_word::size)) return {EVMC_OUT_OF_GAS, gas_left}; - value.template store(&state.memory[index.to_uint64()]); + const auto addr = index.to_uint64(); + value.template store(&state.memory[addr]); + for(uint64_t j = 0; j < nil::blueprint::zkevm_word::size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, addr + j, state.rw_trace.size(), true, state.memory[addr + j])); + } return {EVMC_SUCCESS, gas_left}; } static Result mstore8(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& index = stack.pop(); const auto& value = stack.pop(); if (!check_memory(gas_left, state.memory, index, 1)) return {EVMC_OUT_OF_GAS, gas_left}; - state.memory[(int)index.to_uint64()] = value.to_uint64(); + const auto addr = (int)index.to_uint64(); + state.memory[addr] = value.to_uint64(); + for(uint64_t j = 0; j < 8; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, addr + j, state.rw_trace.size(), true, state.memory[addr + j])); + } return {EVMC_SUCCESS, gas_left}; } @@ -826,12 +984,15 @@ struct instructions { /// JUMP instruction implementation using baseline::CodeAnalysis. static code_iterator jump(StackTop stack, ExecutionState& state, code_iterator /*pos*/) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); return jump_impl(state, stack.pop()); } /// JUMPI instruction implementation using baseline::CodeAnalysis. static code_iterator jumpi(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto& dst = stack.pop(); const auto& cond = stack.pop(); return cond.to_uint64() > 0 ? jump_impl(state, dst) : pos + 1; @@ -846,12 +1007,14 @@ struct instructions { static code_iterator rjumpi(StackTop stack, ExecutionState& state, code_iterator pc) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto cond = stack.pop(); return cond.to_uint64() > 0 ? rjump(stack, state, pc) : pc + 3; } - static code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iterator pc) noexcept + static code_iterator rjumpv(StackTop stack, ExecutionState& state, code_iterator pc) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); const auto case_ = stack.pop(); @@ -874,27 +1037,32 @@ struct instructions { static code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { stack.push(static_cast(pos - state.analysis.baseline->executable_code.data())); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return pos + 1; } static void msize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.memory.size()); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } - static Result gas(StackTop stack, int64_t gas_left, ExecutionState& /*state*/) noexcept + static Result gas(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { stack.push(gas_left); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } static void tload(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& x = stack.top(); evmc::bytes32 key = x.to_uint256be(); const auto value = state.host.get_transient_storage(state.msg->recipient, key); + // TODO: add trasient storage operations x = nil::blueprint::zkevm_word(value); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 2) = x.to_uint64(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } static Result tstore(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept @@ -902,17 +1070,19 @@ struct instructions { if (state.in_static_mode()) return {EVMC_STATIC_MODE_VIOLATION, 0}; - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 0) = stack[1].to_uint64(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(4, 1) = stack[0].to_uint64(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); evmc::bytes32 key = stack.pop().to_uint256be(); evmc::bytes32 value = stack.pop().to_uint256be(); state.host.set_transient_storage(state.msg->recipient, key, value); + // TODO: add trasient storage operations return {EVMC_SUCCESS, gas_left}; } - static void push0(StackTop stack) noexcept + static void push0(StackTop stack, ExecutionState& state) noexcept { stack.push({}); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), true, stack[0])); } /// PUSH instruction implementation. @@ -920,7 +1090,7 @@ struct instructions { /// /// It assumes that at lest 32 bytes of data are available so code padding is required. template - static code_iterator push(StackTop stack, ExecutionState& /*state*/, code_iterator pos) noexcept + static code_iterator push(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { // TODO size of field in bytes constexpr size_t word_size = 8; @@ -945,31 +1115,42 @@ struct instructions { data += word_size; } + int num_words = (int)(Len / nil::blueprint::zkevm_word::size) + (int)(Len % nil::blueprint::zkevm_word::size); + for (int i = 0; i < num_words; ++i) { + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom())- 1 - i), state.rw_trace.size(), true, stack[i])); + } + return pos + (Len + 1); } /// DUP instruction implementation. /// @tparam N The number as in the instruction definition, e.g. DUP3 is dup<3>. template - static void dup(StackTop stack) noexcept + static void dup(StackTop stack, ExecutionState& state) noexcept { static_assert(N >= 0 && N <= 16); if constexpr (N == 0) { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); const auto index = stack.pop(); - assert(index.to_uint64() < std::numeric_limits::max()); - stack.push(stack[(int)index.to_uint64() - 1]); + const auto addr = (int)index.to_uint64(); + assert(addr < std::numeric_limits::max()); + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - addr), state.rw_trace.size(), false, stack[addr - 1])); + stack.push(stack[addr - 1]); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())- 1, state.rw_trace.size(), true, stack[0])); } else { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - N, state.rw_trace.size(), false, stack[N - 1])); stack.push(stack[N - 1]); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())- 1, state.rw_trace.size(), true, stack[0])); } } /// SWAP instruction implementation. /// @tparam N The number as in the instruction definition, e.g. SWAP3 is swap<3>. template - static void swap(StackTop stack) noexcept + static void swap(StackTop stack, ExecutionState& state) noexcept { static_assert(N >= 0 && N <= 16); @@ -977,16 +1158,21 @@ struct instructions { // clang missed optimization: https://github.com/llvm/llvm-project/issues/59116 // TODO(clang): Check if #59116 bug fix has been released. nil::blueprint::zkevm_word* a; + uint16_t addr = N; if constexpr (N == 0) { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& index = stack.pop(); assert(index < std::numeric_limits::max()); - a = &stack[(int)index.to_uint64()]; + addr = (uint16_t)index.to_uint64(); + a = &stack[addr]; } else { a = &stack[N]; } + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - addr - 1), state.rw_trace.size(), false, stack[addr])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())-1, state.rw_trace.size(), false, stack[0])); auto& t = stack.top(); auto t0 = t.to_uint64(0); auto t1 = t.to_uint64(1); @@ -997,13 +1183,15 @@ struct instructions { a->set_val(t1, 1); a->set_val(t2, 2); a->set_val(t3, 3); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())- 1, state.rw_trace.size(), true, stack[0])); + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - addr), state.rw_trace.size(), true, stack[addr - 1])); } static code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { - const auto n = pos[1] + 1; + const uint16_t n = pos[1] + 1; - const auto stack_size = &stack.top() - state.stack_space.bottom(); + const uint16_t stack_size = (uint16_t)(&stack.top() - state.stack_space.bottom()); if (stack_size < n) { @@ -1011,7 +1199,9 @@ struct instructions { return nullptr; } + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - n), state.rw_trace.size(), false, stack[n - 1])); stack.push(stack[n - 1]); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())- 1, state.rw_trace.size(), true, stack[0])); return pos + 2; } @@ -1028,14 +1218,21 @@ struct instructions { return nullptr; } + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - n - 1), state.rw_trace.size(), false, stack[n])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); // TODO: This may not be optimal, see instr::core::swap(). std::swap(stack.top(), stack[n]); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom())- 1, state.rw_trace.size(), true, stack[0])); + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - n - 1), state.rw_trace.size(), true, stack[n])); return pos + 2; } static Result mcopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); const auto& dst_u256 = stack.pop(); const auto& src_u256 = stack.pop(); const auto& size_u256 = stack.pop(); @@ -1050,16 +1247,23 @@ struct instructions { if (const auto cost = copy_cost(size); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; - if (size > 0) + if (size > 0) { std::memmove(&state.memory[dst], &state.memory[src], size); + // TODO: add length read operations to memory + // TODO: add length write operations to memory + /*for(uint64_t j = 0; j < size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, src + j, state.rw_trace.size(), false, state.memory[src + j])); + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, dst + j, state.rw_trace.size(), true, state.memory[dst + j])); + }*/ + } return {EVMC_SUCCESS, gas_left}; } static void dataload(StackTop stack, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); auto& index = stack.top(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(1, 2) = index.to_uint64(); if (state.data.size() < index.to_uint64()) index = 0; @@ -1073,24 +1277,30 @@ struct instructions { data[i] = state.data[begin + i]; index = nil::blueprint::zkevm_word(data, (end - begin)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), true, stack[0])); } } static void datasize(StackTop stack, ExecutionState& state) noexcept { stack.push(state.data.size()); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), true, stack[0])); } static code_iterator dataloadn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { const auto index = read_uint16_be(&pos[1]); - stack.push(nil::blueprint::zkevm_word(&state.data[index], (BlueprintFieldType::modulus_bits / 8))); + stack.push(nil::blueprint::zkevm_word(&state.data[index], nil::blueprint::zkevm_word::size)); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), true, stack[0])); return pos + 3; } static Result datacopy(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 3, state.rw_trace.size(), false, stack[2])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); const auto& mem_index = stack.pop(); const auto& data_index = stack.pop(); const auto& size = stack.pop(); @@ -1109,11 +1319,19 @@ struct instructions { if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) return {EVMC_OUT_OF_GAS, gas_left}; - if (copy_size > 0) + if (copy_size > 0) { std::memcpy(&state.memory[dst], &state.data[src], copy_size); + for(uint64_t j = 0; j < copy_size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, dst + j, state.rw_trace.size(), true, state.memory[dst + j])); + } + } - if (s - copy_size > 0) + if (s - copy_size > 0) { std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + for(uint64_t j = 0; j < s - copy_size; j++){ + state.rw_trace.push_back(nil::blueprint::memory_operation(state.call_id, dst + j, state.rw_trace.size(), true, 0)); + } + } return {EVMC_SUCCESS, gas_left}; } @@ -1122,6 +1340,10 @@ struct instructions { { assert(Op == OP_CALL || Op == OP_CALLCODE || Op == OP_DELEGATECALL || Op == OP_STATICCALL); + uint16_t num_stack_read = (Op == OP_STATICCALL || Op == OP_DELEGATECALL) ? 6 : 7; + for (uint16_t i = 0; i < num_stack_read; i++) { + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - (i + 1)), state.rw_trace.size(), false, stack[i])); + } const auto gas = stack.pop(); const auto dst = stack.pop().to_address(); const auto value = (Op == OP_STATICCALL || Op == OP_DELEGATECALL) ? 0 : stack.pop(); @@ -1224,6 +1446,7 @@ struct instructions { const auto result = state.host.call(msg); state.return_data.assign(result.output_data, result.output_size); stack.top() = result.status_code == EVMC_SUCCESS; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), true, stack[0])); if (const auto copy_size = std::min(output_size, result.output_size); copy_size > 0) std::memcpy(&state.memory[output_offset], result.output_data, copy_size); @@ -1256,6 +1479,10 @@ struct instructions { if (state.in_static_mode()) return {EVMC_STATIC_MODE_VIOLATION, gas_left}; + uint16_t num_stack_read = (Op == OP_CREATE2) ? 4 : 3; + for (uint16_t i = 0; i < num_stack_read; i++) { + state.rw_trace.push_back(stack_operation(state.call_id, (uint16_t)(stack.size(state.stack_space.bottom()) - (i + 1)), state.rw_trace.size(), false, stack[i])); + } const auto endowment = stack.pop(); const auto init_code_offset_u256 = stack.pop(); const auto init_code_size_u256 = stack.pop(); @@ -1309,6 +1536,7 @@ struct instructions { state.return_data.assign(result.output_data, result.output_size); if (result.status_code == EVMC_SUCCESS) stack.top() = nil::blueprint::zkevm_word(result.create_address); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } @@ -1376,6 +1604,8 @@ struct instructions { static TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state, evmc_status_code StatusCode) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); const auto& offset = stack[0]; const auto& size = stack[1]; @@ -1400,6 +1630,7 @@ struct instructions { if (state.in_static_mode()) return {EVMC_STATIC_MODE_VIOLATION, gas_left}; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); const auto beneficiary = stack[0].to_address(); if (state.rev >= EVMC_BERLIN && state.host.access_account(beneficiary) == EVMC_ACCESS_COLD) @@ -1432,6 +1663,7 @@ struct instructions { static Result sload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept { + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); auto& x = stack.top(); const auto key = x.to_uint256be(); @@ -1446,8 +1678,18 @@ struct instructions { return {EVMC_OUT_OF_GAS, gas_left}; } - x = nil::blueprint::zkevm_word(state.host.get_storage(state.msg->recipient, key)); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(3, 2) = x.to_uint64(); + const auto value = nil::blueprint::zkevm_word(state.host.get_storage(state.msg->recipient, key)); + state.rw_trace.push_back(storage_operation( + state.call_id, + nil::blueprint::zkevm_word(state.msg->recipient),// should be transaction_id), WHY??? + x, + state.rw_trace.size(), + false, + value, + value + )); + x = value; + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), true, stack[0])); return {EVMC_SUCCESS, gas_left}; } @@ -1460,17 +1702,28 @@ struct instructions { if (state.rev >= EVMC_ISTANBUL && gas_left <= 2300) return {EVMC_OUT_OF_GAS, gas_left}; - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(3, 0) = stack[1].to_uint64(); - state.assigner->m_assignments[static_cast(state.msg->depth)].witness(3, 1) = stack[0].to_uint64(); - const auto key = stack.pop().to_uint256be(); - const auto value = stack.pop().to_uint256be(); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 2, state.rw_trace.size(), false, stack[1])); + state.rw_trace.push_back(stack_operation(state.call_id, stack.size(state.stack_space.bottom()) - 1, state.rw_trace.size(), false, stack[0])); + const auto key = stack.pop(); + const auto value = stack.pop(); + const auto key_uint64 = key.to_uint256be(); + const auto value_uint64 = value.to_uint256be(); const auto gas_cost_cold = (state.rev >= EVMC_BERLIN && - state.host.access_storage(state.msg->recipient, key) == EVMC_ACCESS_COLD) ? + state.host.access_storage(state.msg->recipient, key_uint64) == EVMC_ACCESS_COLD) ? instr::cold_sload_cost : 0; - const auto status = state.host.set_storage(state.msg->recipient, key, value); + state.rw_trace.push_back(storage_operation( + state.call_id,//TODO should be transaction_id) + nil::blueprint::zkevm_word(state.msg->recipient), + key, + state.rw_trace.size(), + true, + value, + state.host.get_storage(state.msg->recipient, key_uint64) + )); + const auto status = state.host.set_storage(state.msg->recipient, key_uint64, value_uint64); const auto [gas_cost_warm, gas_refund] = sstore_costs[state.rev][status]; const auto gas_cost = gas_cost_warm + gas_cost_cold; diff --git a/lib/assigner/include/assigner.hpp b/lib/assigner/include/assigner.hpp index 9b364d9..6a79013 100644 --- a/lib/assigner/include/assigner.hpp +++ b/lib/assigner/include/assigner.hpp @@ -28,8 +28,13 @@ namespace nil { assigner(std::vector> &assignments): m_assignments(assignments) {} // TODO error handling - void handle_bytcode(const typename evmone::ExecutionState* state, const uint8_t* code) { - return process_bytecode_input(state->original_code.size(), code, m_assignments); + void handle_bytcode(size_t original_code_size, const uint8_t* code) { + return process_bytecode_input(original_code_size, code, m_assignments); + } + + // TODO error handling + void handle_rw(std::vector>& rw_trace) { + return process_rw_operations(rw_trace, m_assignments); } std::vector> &m_assignments; @@ -42,23 +47,29 @@ namespace nil { const evmone::bytes_view container{code_ptr, code_size}; const auto code_analysis = evmone::baseline::analyze(rev, container); const auto data = code_analysis.eof_header.get_data(container); - auto state = std::make_unique>(*msg, rev, *host, ctx, container, data, assigner); + evmone::ExecutionState state(*msg, rev, *host, ctx, container, data, 0, assigner); - state->analysis.baseline = &code_analysis; // Assign code analysis for instruction implementations. + state.analysis.baseline = &code_analysis; // Assign code analysis for instruction implementations. const auto code = code_analysis.executable_code; + // fill assignments for bytecode circuit + assigner->handle_bytcode(state.original_code.size(), code.data()); + int64_t gas = msg->gas; - const auto& cost_table = evmone::baseline::get_baseline_cost_table(state->rev, code_analysis.eof_header.version); + const auto& cost_table = evmone::baseline::get_baseline_cost_table(state.rev, code_analysis.eof_header.version); + + gas = evmone::baseline::dispatch(cost_table, state, msg->gas, code.data()); - gas = evmone::baseline::dispatch(cost_table, *state, msg->gas, code.data()); + // fill assignments for read/write circuit + assigner->handle_rw(state.rw_trace); - const auto gas_left = (state->status == EVMC_SUCCESS || state->status == EVMC_REVERT) ? gas : 0; - const auto gas_refund = (state->status == EVMC_SUCCESS) ? state->gas_refund : 0; + const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; + const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0; - assert(state->output_size != 0 || state->output_offset == 0); - const auto evmc_result = evmc::make_result(state->status, gas_left, gas_refund, - state->output_size != 0 ? &state->memory[state->output_offset] : nullptr, state->output_size); + assert(state.output_size != 0 || state.output_offset == 0); + const auto evmc_result = evmc::make_result(state.status, gas_left, gas_refund, + state.output_size != 0 ? &state.memory[state.output_offset] : nullptr, state.output_size); return evmc::Result{evmc_result}; } diff --git a/lib/assigner/include/bytecode.hpp b/lib/assigner/include/bytecode.hpp index 7bd585b..d072740 100644 --- a/lib/assigner/include/bytecode.hpp +++ b/lib/assigner/include/bytecode.hpp @@ -8,7 +8,8 @@ #ifndef EVM1_ASSIGNER_INCLUDE_BYTECODE_HPP_ #define EVM1_ASSIGNER_INCLUDE_BYTECODE_HPP_ -#include +#include +#include #include #include diff --git a/lib/assigner/include/rw.hpp b/lib/assigner/include/rw.hpp new file mode 100644 index 0000000..1dc7b0e --- /dev/null +++ b/lib/assigner/include/rw.hpp @@ -0,0 +1,284 @@ +//---------------------------------------------------------------------------// +// Copyright (c) Nil Foundation and its affiliates. +// +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. +//---------------------------------------------------------------------------// + +#ifndef EVM1_ASSIGNER_INCLUDE_RW_HPP_ +#define EVM1_ASSIGNER_INCLUDE_RW_HPP_ + +#include +#include + +#include + +namespace nil { + namespace blueprint { + + template + struct rw_operation{ + std::uint8_t op; // described above + std::size_t id; // call_id for stack, memory, tx_id for + zkevm_word address; // 10 bit for stack, 160 bit for + std::uint8_t field; // Not used for stack, memory, storage + zkevm_word storage_key; // 256-bit, not used for stack, memory + std::size_t rw_id; // 32-bit + bool is_write; // 1 if it's write operation + zkevm_word value; // It's full 256 words for storage and stack, but it's only byte for memory. + zkevm_word value_prev; + + bool operator< (const rw_operation &other) const { + if( op != other.op ) return op < other.op; + if( address != other.address ) return address < other.address; + if( field != other.field ) return field < other.field; + if( storage_key != other.storage_key ) return storage_key < other.storage_key; + if( rw_id != other.rw_id) return rw_id < other.rw_id; + return false; + } + }; + + constexpr std::uint8_t START_OP = 0; + constexpr std::uint8_t STACK_OP = 1; + constexpr std::uint8_t MEMORY_OP = 2; + constexpr std::uint8_t STORAGE_OP = 3; + constexpr std::uint8_t TRANSIENT_STORAGE_OP = 4; + constexpr std::uint8_t CALL_CONTEXT_OP = 5; + constexpr std::uint8_t ACCOUNT_OP = 6; + constexpr std::uint8_t TX_REFUND_OP = 7; + constexpr std::uint8_t TX_ACCESS_LIST_ACCOUNT_OP = 8; + constexpr std::uint8_t TX_ACCESS_LIST_ACCOUNT_STORAGE_OP = 9; + constexpr std::uint8_t TX_LOG_OP = 10; + constexpr std::uint8_t TX_RECEIPT_OP = 11; + constexpr std::uint8_t PADDING_OP = 12; + constexpr std::uint8_t rw_options_amount = 13; + + // For testing purposes + template + std::ostream& operator<<(std::ostream& os, const rw_operation& obj){ + if(obj.op == START_OP ) os << "START: "; + if(obj.op == STACK_OP ) os << "STACK: "; + if(obj.op == MEMORY_OP ) os << "MEMORY: "; + if(obj.op == STORAGE_OP ) os << "STORAGE: "; + if(obj.op == TRANSIENT_STORAGE_OP ) os << "TRANSIENT_STORAGE: "; + if(obj.op == CALL_CONTEXT_OP ) os << "CALL_CONTEXT_OP: "; + if(obj.op == ACCOUNT_OP ) os << "ACCOUNT_OP: "; + if(obj.op == TX_REFUND_OP ) os << "TX_REFUND_OP: "; + if(obj.op == TX_ACCESS_LIST_ACCOUNT_OP ) os << "TX_ACCESS_LIST_ACCOUNT_OP: "; + if(obj.op == TX_ACCESS_LIST_ACCOUNT_STORAGE_OP ) os << "TX_ACCESS_LIST_ACCOUNT_STORAGE_OP: "; + if(obj.op == TX_LOG_OP ) os << "TX_LOG_OP: "; + if(obj.op == TX_RECEIPT_OP ) os << "TX_RECEIPT_OP: "; + if(obj.op == PADDING_OP ) os << "PADDING_OP: "; + os << obj.rw_id << ", addr =" << std::hex << obj.address << std::dec; + if(obj.op == STORAGE_OP || obj.op == TRANSIENT_STORAGE_OP) + os << " storage_key = " << obj.storage_key; + if(obj.is_write) os << " W "; else os << " R "; + os << "[" << std::hex << obj.value_prev << std::dec <<"] => "; + os << "[" << std::hex << obj.value << std::dec <<"]"; + return os; + } + + template + rw_operation start_operation(){ + return rw_operation({START_OP, 0, 0, 0, 0, 0, 0, 0}); + } + + template + rw_operation stack_operation(std::size_t id, uint16_t address, std::size_t rw_id, bool is_write, zkevm_word value){ + assert(id < ( 1 << 28)); // Maximum calls amount(?) + assert(address < 1024); + return rw_operation({STACK_OP, id, address, 0, 0, rw_id, is_write, value, 0}); + } + + template + rw_operation memory_operation(std::size_t id, zkevm_word address, std::size_t rw_id, bool is_write, zkevm_word value){ + assert(id < ( 1 << 28)); // Maximum calls amount(?) + return rw_operation({MEMORY_OP, id, address, 0, 0, rw_id, is_write, value, 0}); + } + + template + rw_operation storage_operation( + std::size_t id, + zkevm_word address, + zkevm_word storage_key, + std::size_t rw_id, + bool is_write, + zkevm_word value, + zkevm_word value_prev + ){ + return rw_operation({STORAGE_OP, id, address, 0, storage_key, rw_id, is_write, value, value_prev}); + } + + template + rw_operation padding_operation(){ + return rw_operation({PADDING_OP, 0, 0, 0, 0, 0, 0, 0}); + } + + template + void process_rw_operations(std::vector>& rw_trace, + std::vector>> &assignments) { + constexpr std::size_t OP = 0; + constexpr std::size_t ID = 1; + constexpr std::size_t ADDRESS = 2; + constexpr std::size_t STORAGE_KEY_HI = 3; + constexpr std::size_t STORAGE_KEY_LO = 4; + constexpr std::size_t FIELD_TYPE = 5; // NOT USED FOR STACK, MEMORY and ACCOUNT STORAGE, but used by txComponent. + constexpr std::size_t RW_ID = 6; + constexpr std::size_t IS_WRITE = 7; + constexpr std::size_t VALUE_HI = 8; + constexpr std::size_t VALUE_LO = 9; + + constexpr std::size_t OP_SELECTORS_AMOUNT = 4; // index \in {0..31} + constexpr std::array OP_SELECTORS = {10, 11, 12, 13}; + + constexpr std::size_t INDICES_AMOUNT = 5; // index \in {0..31} + constexpr std::array INDICES = {14, 15, 16, 17, 18}; + + constexpr std::size_t IS_FIRST = 19; + + constexpr std::size_t CHUNKS_AMOUNT = 30; + constexpr std::array CHUNKS = { + 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49 + }; + + constexpr std::size_t DIFFERENCE = 50; + constexpr std::size_t INV_DIFFERENCE = 51; + constexpr std::size_t VALUE_BEFORE_HI = 52; // Check, where do we need it. + constexpr std::size_t VALUE_BEFORE_LO = 53; // Check, where do we need it. + constexpr std::size_t STATE_ROOT_HI = 54; // Check, where do we need it. + constexpr std::size_t STATE_ROOT_LO = 55; // Check, where do we need it. + constexpr std::size_t STATE_ROOT_BEFORE_HI = 56; // Check, where do we need it. + constexpr std::size_t STATE_ROOT_BEFORE_LO = 57; // Check, where do we need it. + constexpr std::size_t IS_LAST = 58; + + constexpr std::size_t SORTED_COLUMNS_AMOUNT = 32; + + constexpr std::size_t total_witness_amount = 60; + + uint32_t start_row_index = assignments[0].witness_column_size(OP); + + std::cout << "process_rw_operations" << std::endl; + std::cout << "Start row index: " << start_row_index << std::endl; + + std::vector sorting = { + OP, + // ID + CHUNKS[0], CHUNKS[1], + // address + CHUNKS[2], CHUNKS[3], CHUNKS[4], CHUNKS[5], CHUNKS[6], + CHUNKS[7], CHUNKS[8], CHUNKS[9], CHUNKS[10], CHUNKS[11], + // field + FIELD_TYPE, + // storage_key + CHUNKS[12], CHUNKS[13], CHUNKS[14], CHUNKS[15], CHUNKS[16], + CHUNKS[17], CHUNKS[18], CHUNKS[19], CHUNKS[20], CHUNKS[21], + CHUNKS[22], CHUNKS[23], CHUNKS[24], CHUNKS[25], CHUNKS[26], + CHUNKS[27], + // rw_id + CHUNKS[28], CHUNKS[29] + }; + //sort operations + std::sort(rw_trace.begin(), rw_trace.end()); + + for(uint32_t i = 0; i < rw_trace.size(); i++){ + // Lookup columns + assignments[0].witness(OP, start_row_index + i) = rw_trace[i].op; + assignments[0].witness(ID, start_row_index + i) = rw_trace[i].id; + assignments[0].witness(ADDRESS, start_row_index + i) = rw_trace[i].address.to_field_as_address(); + assignments[0].witness(STORAGE_KEY_HI, start_row_index + i) = rw_trace[i].storage_key.w_hi(); + assignments[0].witness(STORAGE_KEY_LO, start_row_index + i) = rw_trace[i].storage_key.w_lo(); + assignments[0].witness(RW_ID, start_row_index + i) = rw_trace[i].rw_id; + assignments[0].witness(IS_WRITE, start_row_index + i) = rw_trace[i].is_write; + assignments[0].witness(VALUE_HI, start_row_index + i) = rw_trace[i].value.w_hi(); + assignments[0].witness(VALUE_LO, start_row_index + i) = rw_trace[i].value.w_lo(); + + // Op selectors + typename BlueprintFieldType::integral_type mask = (1 << OP_SELECTORS_AMOUNT); + for( std::size_t j = 0; j < OP_SELECTORS_AMOUNT; j++){ + mask >>= 1; + assignments[0].witness(OP_SELECTORS[j], start_row_index + i) = (((rw_trace[i].op & mask) == 0) ? 0 : 1); + } + + // Fill chunks. + // id + mask = 0xffff; + mask <<= 16; + assignments[0].witness(CHUNKS[0], start_row_index + i) = (mask & rw_trace[i].id) >> 16; + mask >>= 16; + assignments[0].witness(CHUNKS[1], start_row_index + i) = (mask & rw_trace[i].id); + + // address + mask = 0xffff; + mask <<= (16 * 9); + for( std::size_t j = 0; j < 10; j++){ + assignments[0].witness(CHUNKS[2+j], start_row_index + i) = (((rw_trace[i].address & mask) >> (16 * (9-j)))); + mask >>= 16; + } + + // storage key + mask = 0xffff; + mask <<= (16 * 15); + for( std::size_t j = 0; j < 16; j++){ + assignments[0].witness(CHUNKS[12+j], start_row_index + i) = (((rw_trace[i].storage_key & mask) >> (16 * (15-j)))); + mask >>= 16; + } + + // rw_key + mask = 0xffff; + mask <<= 16; + assignments[0].witness(CHUNKS[28], start_row_index + i) = (mask & rw_trace[i].rw_id) >> 16; + mask >>= 16; + assignments[0].witness(CHUNKS[29], start_row_index + i) = (mask & rw_trace[i].rw_id); + + // fill sorting indices and advices + if( i == 0 ) continue; + bool neq = true; + std::size_t diff_ind = 0; + for(; diff_ind < sorting.size(); diff_ind++){ + if( + assignments[0].witness(sorting[diff_ind], start_row_index+i) != + assignments[0].witness(sorting[diff_ind], start_row_index+i - 1) + ) break; + } + if( diff_ind < 30 ){ + assignments[0].witness(VALUE_BEFORE_HI, start_row_index + i) = rw_trace[i].value_prev.w_hi(); + assignments[0].witness(VALUE_BEFORE_LO, start_row_index + i) = rw_trace[i].value_prev.w_lo(); + } else { + assignments[0].witness(VALUE_BEFORE_HI, start_row_index + i) = assignments[0].witness(VALUE_BEFORE_HI, start_row_index + i - 1); + assignments[0].witness(VALUE_BEFORE_LO, start_row_index + i) = assignments[0].witness(VALUE_BEFORE_LO, start_row_index + i - 1); + } + + mask = (1 << INDICES_AMOUNT); + for(std::size_t j = 0; j < INDICES_AMOUNT; j++){ + mask >>= 1; + assignments[0].witness(INDICES[j], start_row_index + i) = ((mask & diff_ind) == 0? 0: 1); + } + if( rw_trace[i].op != START_OP && diff_ind < 30){ + assignments[0].witness(IS_LAST, start_row_index + i - 1) = 1; + } + if( rw_trace[i].op != START_OP && rw_trace[i].op != PADDING_OP && diff_ind < 30){ + assignments[0].witness(IS_FIRST, start_row_index + i) = 1; + } + + assignments[0].witness(DIFFERENCE, start_row_index + i) = + assignments[0].witness(sorting[diff_ind], start_row_index+i) - + assignments[0].witness(sorting[diff_ind], start_row_index+i - 1); + std::cout << "Diff index = " << diff_ind << + " is_first = " << assignments[0].witness(IS_FIRST, start_row_index + i) << + " is_last = " << assignments[0].witness(IS_LAST, start_row_index + i) << + std::endl; + + if( assignments[0].witness(DIFFERENCE, start_row_index + i) == 0) + assignments[0].witness(INV_DIFFERENCE, start_row_index + i) = 0; + else + assignments[0].witness(INV_DIFFERENCE, start_row_index + i) = BlueprintFieldType::value_type::one() / assignments[0].witness(DIFFERENCE, start_row_index+i); + } + } + + } // namespace blueprint +} // namespace nil + +#endif // EVM1_ASSIGNER_INCLUDE_RW_HPP_ diff --git a/lib/assigner/include/vm_host.hpp b/lib/assigner/include/vm_host.hpp index 45808b7..df977d8 100644 --- a/lib/assigner/include/vm_host.hpp +++ b/lib/assigner/include/vm_host.hpp @@ -284,7 +284,7 @@ class VMHost : public evmc::Host auto hash = nil::blueprint::zkevm_word(ethash::keccak256(msg.input_data, msg.input_size)); auto sender = nil::blueprint::zkevm_word(msg.sender); auto sum = nil::blueprint::zkevm_word(0xff) + seed + hash + sender; - auto rehash = ethash::keccak256(sum.raw_data(), sum.size()); + auto rehash = ethash::keccak256(sum.raw_data(), nil::blueprint::zkevm_word::size); // Result address is the last 20 bytes of the hash evmc::address res; std::memcpy(res.bytes, rehash.bytes + 12, 20); diff --git a/lib/assigner/include/zkevm_word.hpp b/lib/assigner/include/zkevm_word.hpp index 1704dee..9c13bb1 100644 --- a/lib/assigner/include/zkevm_word.hpp +++ b/lib/assigner/include/zkevm_word.hpp @@ -28,6 +28,7 @@ namespace nil { struct zkevm_word { using column_type_t = typename crypto3::zk::snark::plonk_column; using value_type = intx::uint256; + static constexpr uint64_t size = sizeof(value_type); // constructors zkevm_word() { value = 0; @@ -138,6 +139,17 @@ namespace nil { return value & other.value; } + typename BlueprintFieldType::integral_type operator&(const typename BlueprintFieldType::integral_type& other) const { + typename BlueprintFieldType::integral_type val = value[0]; + typename BlueprintFieldType::integral_type lo = value[1]; + typename BlueprintFieldType::integral_type hi = value[2]; + typename BlueprintFieldType::integral_type h = value[3]; + val += lo << 64; + val += hi << 126; + val += h << 192; + return val & other; + } + zkevm_word operator|(const zkevm_word& other) const { return value | other.value; } @@ -179,6 +191,15 @@ namespace nil { return intx::be::trunc(value); } + typename BlueprintFieldType::value_type to_field_as_address() const { + typename BlueprintFieldType::integral_type res = value[0]; + typename BlueprintFieldType::integral_type lo = value[1]; + typename BlueprintFieldType::integral_type hi = value[2]; + res += lo << 64; + res += hi << 128; + return res; + } + evmc::uint256be to_uint256be() const { return intx::be::store(value); } @@ -221,12 +242,32 @@ namespace nil { return reinterpret_cast(&value); } - uint64_t size() const { - return sizeof(value); + typename BlueprintFieldType::value_type w_hi() const { + typename BlueprintFieldType::integral_type res = value[3]; + res = res << 64; + res += value[2]; + return res; + } + + typename BlueprintFieldType::value_type w_lo() const { + typename BlueprintFieldType::integral_type res = value[1]; + res = res << 64; + res += value[0]; + return res; } + private: intx::uint256 value; }; + + template + std::ostream& operator<<(std::ostream& os, const zkevm_word& obj){ + const auto bytes = obj.to_uint256be().bytes; + for (uint8_t i = 0; i < 32; i++) { + os << (int)bytes[i] << " "; + } + return os; + } } // namespace blueprint } // namespace nil #endif // EVM1_ASSIGNER_INCLUDE_NIL_BLUEPRINT_ZKEVM_WORD_HPP_ diff --git a/lib/assigner/test/assigner_test.cpp b/lib/assigner/test/assigner_test.cpp index c0b5d5d..f4d88d3 100644 --- a/lib/assigner/test/assigner_test.cpp +++ b/lib/assigner/test/assigner_test.cpp @@ -17,7 +17,7 @@ class AssignerTest : public testing::Test using ArithmetizationType = nil::crypto3::zk::snark::plonk_constraint_system; static void SetUpTestSuite() { - const std::size_t WitnessColumns = 15; + const std::size_t WitnessColumns = 65; const std::size_t PublicInputColumns = 1; const std::size_t ConstantColumns = 5; @@ -92,6 +92,37 @@ inline void check_eq(const uint8_t* l, const uint8_t* r, size_t len) { } } +inline void rw_circuit_check(const std::vector> assignments, + uint32_t start_row_index, + uint8_t operation_type, + uint32_t call_id, + const typename AssignerTest::BlueprintFieldType::value_type& address, + const typename AssignerTest::BlueprintFieldType::value_type& stoage_key_hi, + const typename AssignerTest::BlueprintFieldType::value_type& stoage_key_lo, + uint32_t rw_id, + bool is_write, + const typename AssignerTest::BlueprintFieldType::value_type& value_hi, + const typename AssignerTest::BlueprintFieldType::value_type& value_lo) { + // OP_TYPE + EXPECT_EQ(assignments[0].witness(0, start_row_index), operation_type); + // CALL ID + EXPECT_EQ(assignments[0].witness(1, start_row_index), call_id); + // address (stack size) + EXPECT_EQ(assignments[0].witness(2, start_row_index), address); + // storage key hi + EXPECT_EQ(assignments[0].witness(3, start_row_index), stoage_key_hi); + // storage key lo + EXPECT_EQ(assignments[0].witness(4, start_row_index), stoage_key_lo); + // RW ID + EXPECT_EQ(assignments[0].witness(6, start_row_index), rw_id); + // is write + EXPECT_EQ(assignments[0].witness(7, start_row_index), (is_write ? 1 : 0)); + // value hi + EXPECT_EQ(assignments[0].witness(8, start_row_index), value_hi); + // value lo + EXPECT_EQ(assignments[0].witness(9, start_row_index), value_lo); +} + TEST_F(AssignerTest, conversions_uint256be_to_zkevm_word) { evmc::uint256be uint256be_number; @@ -164,11 +195,28 @@ TEST_F(AssignerTest, mul) { }; nil::blueprint::evaluate(host_interface, ctx, rev, &msg, code.data(), code.size(), assigner_ptr); - EXPECT_EQ(assignments[0].witness(0, 0), 8); - EXPECT_EQ(assignments[0].witness(0, 1), 4); + + uint32_t start_row_index = 5; + uint32_t call_id = 0; + //PUSH + rw_circuit_check(assignments, start_row_index, 1/*STACK_OP*/, call_id, 0/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, + 0/*trace size*/, true/*is_write*/, 0/*value_hi*/, 4/*value_lo*/); + //MUL + rw_circuit_check(assignments, start_row_index + 1, 1/*STACK_OP*/, call_id, 0/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, + 2/*trace size*/, false/*is_write*/, 0/*value_hi*/, 4/*value_lo*/); + //MUL + rw_circuit_check(assignments, start_row_index + 2, 1/*STACK_OP*/, call_id, 0/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, + 4/*trace size*/, true/*is_write*/, 0/*value_hi*/, 32/*value_lo*/); + //PUSH + rw_circuit_check(assignments, start_row_index + 3, 1/*STACK_OP*/, call_id, 1/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, + 1/*trace size*/, true/*is_write*/, 0/*value_hi*/, 8/*value_lo*/); + //MUL + rw_circuit_check(assignments, start_row_index + 4, 1/*STACK_OP*/, call_id, 1/*address in stack*/, 0/*storage key hi*/, 0/*storage key lo*/, + 3/*trace size*/, false/*is_write*/, 0/*value_hi*/, 8/*value_lo*/); } -TEST_F(AssignerTest, callvalue_calldataload) +// TODO add check assignment tables +TEST_F(AssignerTest, DISABLED_callvalue_calldataload) { const uint8_t index = 10; std::vector code = { @@ -194,7 +242,8 @@ TEST_F(AssignerTest, DISABLED_dataload) { EXPECT_EQ(assignments[0].witness(1, 2), index); } -TEST_F(AssignerTest, mstore_load) +// TODO add check assignment tables +TEST_F(AssignerTest, DISABLED_mstore_load) { const uint8_t value = 12; const uint8_t index = 1; @@ -214,7 +263,8 @@ TEST_F(AssignerTest, mstore_load) EXPECT_EQ(assignments[0].witness(2, 2), value); } -TEST_F(AssignerTest, sstore_load) +// TODO add check assignment tables +TEST_F(AssignerTest, DISABLED_sstore_load) { const uint8_t value = 12; const uint8_t key = 4; @@ -254,7 +304,8 @@ TEST_F(AssignerTest, DISABLED_tstore_load) { EXPECT_EQ(assignments[0].witness(4, 2), value); } -TEST_F(AssignerTest, create) { +// TODO add check assignment tables +TEST_F(AssignerTest, DISABLED_create) { std::vector code = { // Create an account with 0 wei and no code @@ -311,7 +362,8 @@ TEST_F(AssignerTest, create) { EXPECT_EQ(assignments[1].witness(2, 0), 0xFFFFFFFF); } -TEST_F(AssignerTest, call) { +// TODO add check assignment tables +TEST_F(AssignerTest, DISABLED_call) { std::vector code = { // Create a contract that creates an exception if first word of calldata is 0 From d85d07b2bb50f7720c5bc5fe8a0e0af92f77ee68 Mon Sep 17 00:00:00 2001 From: akokoshn Date: Thu, 4 Jul 2024 12:16:35 +0300 Subject: [PATCH 3/3] Add boost log and target circuit --- cmake/Config.cmake.in | 2 +- flake.lock | 30 +++++++++++++------------- flake.nix | 16 +++++++------- lib/assigner/CMakeLists.txt | 1 - lib/assigner/include/assigner.hpp | 36 ++++++++++++++++++++++++++++--- lib/assigner/include/bytecode.hpp | 17 +++++++++------ lib/assigner/include/rw.hpp | 14 ++++++++---- lib/assigner/include/vm_host.hpp | 13 +++++------ 8 files changed, 84 insertions(+), 45 deletions(-) diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index 955b28a..305b342 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -2,7 +2,7 @@ include(CMakeFindDependencyMacro) find_dependency(crypto3 REQUIRED) -find_dependency(blueprint_crypto3 REQUIRED) +find_dependency(crypto3_blueprint REQUIRED) find_dependency(evmc REQUIRED) find_dependency(intx REQUIRED) find_dependency(ethash REQUIRED) diff --git a/flake.lock b/flake.lock index 9592af6..04b5029 100644 --- a/flake.lock +++ b/flake.lock @@ -36,7 +36,7 @@ "type": "github" } }, - "nil_crypto3": { + "nil-crypto3": { "inputs": { "nix-3rdparty": "nix-3rdparty", "nixpkgs": [ @@ -44,11 +44,11 @@ ] }, "locked": { - "lastModified": 1718821034, - "narHash": "sha256-j3fsPiltYzctIgAWakAHdPLAxq+UiXXnKNmAu+oN7Us=", + "lastModified": 1720036529, + "narHash": "sha256-Ub38Lwsy0PzQtJ+3cfqbVVAXrDuu0A4i1i3vPT9Nxa0=", "ref": "refs/heads/master", - "rev": "caf78b83692b39f504c4ee74a3834a4c38662a77", - "revCount": 9146, + "rev": "c34662a28869af63afc67789435390a2a8f3c708", + "revCount": 9148, "submodules": true, "type": "git", "url": "https://github.com/NilFoundation/crypto3" @@ -59,24 +59,24 @@ "url": "https://github.com/NilFoundation/crypto3" } }, - "nil_zkllvm_blueprint": { + "nil-zkllvm-blueprint": { "inputs": { "flake-utils": [ "flake-utils" ], - "nil_crypto3": [ - "nil_crypto3" + "nil-crypto3": [ + "nil-crypto3" ], "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1719590043, - "narHash": "sha256-C2O0EAmWOR+Fq+lNqumwd7YS9+TBDkWGNpS1CjCAwvY=", + "lastModified": 1720092182, + "narHash": "sha256-WTjhgYwkHjDCSDmDX1SwCH+cuz6OWl9vQaOYCUKOw7c=", "ref": "refs/heads/master", - "rev": "84ebc6dbdd94256424904c8270cb16d764236a31", - "revCount": 1243, + "rev": "73d6a40e39b6b6fc7b1c84441e62337206dc0815", + "revCount": 1246, "submodules": true, "type": "git", "url": "https://github.com/NilFoundation/zkllvm-blueprint" @@ -91,7 +91,7 @@ "inputs": { "flake-utils": "flake-utils_2", "nixpkgs": [ - "nil_crypto3", + "nil-crypto3", "nixpkgs" ] }, @@ -151,8 +151,8 @@ "root": { "inputs": { "flake-utils": "flake-utils", - "nil_crypto3": "nil_crypto3", - "nil_zkllvm_blueprint": "nil_zkllvm_blueprint", + "nil-crypto3": "nil-crypto3", + "nil-zkllvm-blueprint": "nil-zkllvm-blueprint", "nix-3rdparty": "nix-3rdparty_2", "nixpkgs": "nixpkgs" } diff --git a/flake.nix b/flake.nix index a2c91f0..1656346 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ flake-utils.follows = "flake-utils"; }; }; - nil_crypto3 = { + nil-crypto3 = { url = "https://github.com/NilFoundation/crypto3"; type = "git"; submodules = true; @@ -19,14 +19,14 @@ nixpkgs.follows = "nixpkgs"; }; }; - nil_zkllvm_blueprint = { + nil-zkllvm-blueprint = { url = "https://github.com/NilFoundation/zkllvm-blueprint"; type = "git"; submodules = true; inputs = { nixpkgs.follows = "nixpkgs"; flake-utils.follows = "flake-utils"; - nil_crypto3.follows = "nil_crypto3"; + nil-crypto3.follows = "nil-crypto3"; }; }; }; @@ -50,12 +50,12 @@ # Add more complicated logic here if you need to have debug packages resolveInput = input: input.packages.${system}.default; - nil_inputs = with inputs; [ - nil_crypto3 - nil_zkllvm_blueprint + nil-inputs = with inputs; [ + nil-crypto3 + nil-zkllvm-blueprint ]; - nil_packages = map resolveInput nil_inputs; + nil-packages = map resolveInput nil-inputs; defaultNativeBuildInputs = with pkgs; [ cmake @@ -77,7 +77,7 @@ ethash ]; in - deps ++ nil_packages; + deps ++ nil-packages; devInputs = [ pkgs.clang_17 ]; diff --git a/lib/assigner/CMakeLists.txt b/lib/assigner/CMakeLists.txt index dd684bb..37a598c 100644 --- a/lib/assigner/CMakeLists.txt +++ b/lib/assigner/CMakeLists.txt @@ -15,7 +15,6 @@ add_library(${PROJECT_NAME} STATIC ${evmone_sources}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) -find_package(Boost REQUIRED COMPONENTS container random filesystem log log_setup program_options thread system) find_package(crypto3 REQUIRED) find_package(crypto3_blueprint REQUIRED) diff --git a/lib/assigner/include/assigner.hpp b/lib/assigner/include/assigner.hpp index 6a79013..a9b1b58 100644 --- a/lib/assigner/include/assigner.hpp +++ b/lib/assigner/include/assigner.hpp @@ -10,6 +10,10 @@ #include +#include +#include +#include + #include #include @@ -20,6 +24,18 @@ namespace nil { namespace blueprint { + enum zkevm_circuit : uint8_t { + ALL = 0xFF, + BYTECODE = 0x1, + RW = 0x2 + }; + + static const std::map zkevm_circuits_map = { + {"", zkevm_circuit::ALL}, + {"bytecode", zkevm_circuit::BYTECODE}, + {"rw", zkevm_circuit::RW} + }; + template struct assigner { @@ -43,7 +59,13 @@ namespace nil { template static evmc::Result evaluate(const evmc_host_interface* host, evmc_host_context* ctx, evmc_revision rev, const evmc_message* msg, const uint8_t* code_ptr, size_t code_size, - std::shared_ptr> assigner) { + std::shared_ptr> assigner, const std::string& target_circuit = "") { + if(zkevm_circuits_map.find(target_circuit) == zkevm_circuits_map.end()) { + std::cerr << "Unknown target circuit " << target_circuit << "\n"; + return evmc::Result{EVMC_FAILURE, msg->gas}; + } + const auto zkevm_target_circuit = zkevm_circuits_map.find(target_circuit)->second; + const evmone::bytes_view container{code_ptr, code_size}; const auto code_analysis = evmone::baseline::analyze(rev, container); const auto data = code_analysis.eof_header.get_data(container); @@ -53,16 +75,24 @@ namespace nil { const auto code = code_analysis.executable_code; // fill assignments for bytecode circuit - assigner->handle_bytcode(state.original_code.size(), code.data()); + if (zkevm_target_circuit & zkevm_circuit::BYTECODE) { + assigner->handle_bytcode(state.original_code.size(), code.data()); + } int64_t gas = msg->gas; const auto& cost_table = evmone::baseline::get_baseline_cost_table(state.rev, code_analysis.eof_header.version); + BOOST_LOG_TRIVIAL(debug) << "Run evaluate\n"; + gas = evmone::baseline::dispatch(cost_table, state, msg->gas, code.data()); + BOOST_LOG_TRIVIAL(debug) << "Evaluate result = " << state.status << "\n"; + // fill assignments for read/write circuit - assigner->handle_rw(state.rw_trace); + if (zkevm_target_circuit & zkevm_circuit::RW) { + assigner->handle_rw(state.rw_trace); + } const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0; const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0; diff --git a/lib/assigner/include/bytecode.hpp b/lib/assigner/include/bytecode.hpp index d072740..fc1efde 100644 --- a/lib/assigner/include/bytecode.hpp +++ b/lib/assigner/include/bytecode.hpp @@ -11,6 +11,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -24,19 +28,18 @@ namespace nil { using value_type = typename BlueprintFieldType::value_type; using hash_type = nil::crypto3::hashes::keccak_1600<256>; - std::cout << "MYINFO: " << std::endl; - std::cout << "MYINFO: bytecode size: " << original_code_size << std::endl; - std::cout << "MYINFO: bytecode: " << std::endl; + BOOST_LOG_TRIVIAL(debug) << "Process bytecode circuit\n"; + BOOST_LOG_TRIVIAL(debug) << "Bytecode size: " << original_code_size << "\n"; + BOOST_LOG_TRIVIAL(debug) << "Bytecode: " << std::endl; std::vector bytecode; for (size_t i = 0; i < original_code_size; i++) { const auto op = code[i]; bytecode.push_back(op); - std::cout << (uint)op << " "; + BOOST_LOG_TRIVIAL(debug) << (uint)op << " "; } - std::cout << std::endl; + BOOST_LOG_TRIVIAL(debug) << "\n"; - std::cout << "MYINFO: :" << std::endl; std::string hash = nil::crypto3::hash>(bytecode.begin(), bytecode.end()); std::string str_hi = hash.substr(0, hash.size()-32); std::string str_lo = hash.substr(hash.size()-32, 32); @@ -44,7 +47,7 @@ namespace nil { value_type hash_lo; for( std::size_t j = 0; j < str_hi.size(); j++ ){hash_hi *=16; hash_hi += str_hi[j] >= '0' && str_hi[j] <= '9'? str_hi[j] - '0' : str_hi[j] - 'a' + 10;} for( std::size_t j = 0; j < str_lo.size(); j++ ){hash_lo *=16; hash_lo += str_lo[j] >= '0' && str_lo[j] <= '9'? str_lo[j] - '0' : str_lo[j] - 'a' + 10;} - std::cout << std::hex << "Contract hash = " << hash << " h:" << hash_hi << " l:" << hash_lo << std::dec << std::endl; + BOOST_LOG_TRIVIAL(debug) << std::hex << "Contract hash = " << hash << " h:" << hash_hi << " l:" << hash_lo << std::dec << "\n"; static constexpr uint32_t TAG = 0; static constexpr uint32_t INDEX = 1; diff --git a/lib/assigner/include/rw.hpp b/lib/assigner/include/rw.hpp index 1dc7b0e..7aa46f0 100644 --- a/lib/assigner/include/rw.hpp +++ b/lib/assigner/include/rw.hpp @@ -11,6 +11,10 @@ #include #include +#include +#include +#include + #include namespace nil { @@ -160,8 +164,8 @@ namespace nil { uint32_t start_row_index = assignments[0].witness_column_size(OP); - std::cout << "process_rw_operations" << std::endl; - std::cout << "Start row index: " << start_row_index << std::endl; + BOOST_LOG_TRIVIAL(debug) << "Process RW circuit\n"; + BOOST_LOG_TRIVIAL(debug) << "Start row index: " << start_row_index << "\n"; std::vector sorting = { OP, @@ -183,7 +187,9 @@ namespace nil { //sort operations std::sort(rw_trace.begin(), rw_trace.end()); + BOOST_LOG_TRIVIAL(debug) << "Num operations = " << rw_trace.size() << "\n"; for(uint32_t i = 0; i < rw_trace.size(); i++){ + BOOST_LOG_TRIVIAL(debug) << rw_trace[i] << "\n"; // Lookup columns assignments[0].witness(OP, start_row_index + i) = rw_trace[i].op; assignments[0].witness(ID, start_row_index + i) = rw_trace[i].id; @@ -266,10 +272,10 @@ namespace nil { assignments[0].witness(DIFFERENCE, start_row_index + i) = assignments[0].witness(sorting[diff_ind], start_row_index+i) - assignments[0].witness(sorting[diff_ind], start_row_index+i - 1); - std::cout << "Diff index = " << diff_ind << + BOOST_LOG_TRIVIAL(debug) << "Diff index = " << diff_ind << " is_first = " << assignments[0].witness(IS_FIRST, start_row_index + i) << " is_last = " << assignments[0].witness(IS_LAST, start_row_index + i) << - std::endl; + "\n"; if( assignments[0].witness(DIFFERENCE, start_row_index + i) == 0) assignments[0].witness(INV_DIFFERENCE, start_row_index + i) = 0; diff --git a/lib/assigner/include/vm_host.hpp b/lib/assigner/include/vm_host.hpp index df977d8..e8c0db9 100644 --- a/lib/assigner/include/vm_host.hpp +++ b/lib/assigner/include/vm_host.hpp @@ -49,12 +49,12 @@ class VMHost : public evmc::Host public: VMHost() = default; - explicit VMHost(evmc_tx_context& _tx_context, std::shared_ptr> _assigner) noexcept - : tx_context{_tx_context}, assigner{_assigner} + explicit VMHost(evmc_tx_context& _tx_context, std::shared_ptr> _assigner, const std::string& _target_circuit = "") noexcept + : tx_context{_tx_context}, assigner{_assigner}, target_circuit{_target_circuit} {} - VMHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts, std::shared_ptr> _assigner) noexcept - : accounts{_accounts}, tx_context{_tx_context}, assigner{_assigner} + VMHost(evmc_tx_context& _tx_context, evmc::accounts& _accounts, std::shared_ptr> _assigner, const std::string& _target_circuit = "") noexcept + : accounts{_accounts}, tx_context{_tx_context}, assigner{_assigner}, target_circuit{_target_circuit} {} bool account_exists(const evmc::address& addr) const noexcept final @@ -216,6 +216,7 @@ class VMHost : public evmc::Host private: std::shared_ptr> assigner; + std::string target_circuit; evmc::Result handle_call(const evmc_message& msg) { auto sender_iter = accounts.find(msg.sender); @@ -245,7 +246,7 @@ class VMHost : public evmc::Host } // TODO: handle precompiled contracts evmc::Result res = nil::blueprint::evaluate(&get_interface(), to_context(), - EVMC_LATEST_STABLE_REVISION, &msg, acc.code.data(), acc.code.size(), assigner); + EVMC_LATEST_STABLE_REVISION, &msg, acc.code.data(), acc.code.size(), assigner, target_circuit); return res; } @@ -267,7 +268,7 @@ class VMHost : public evmc::Host init_msg.sender = msg.sender; init_msg.input_size = 0; evmc::Result res = nil::blueprint::evaluate(&get_interface(), to_context(), - EVMC_LATEST_STABLE_REVISION, &init_msg, msg.input_data, msg.input_size, assigner); + EVMC_LATEST_STABLE_REVISION, &init_msg, msg.input_data, msg.input_size, assigner, target_circuit); if (res.status_code == EVMC_SUCCESS) { accounts[new_contract_address].code =