From 1fe5864deac485f7a1d677927b1d3b37de0d52bb Mon Sep 17 00:00:00 2001 From: x-mass <36629999+x-mass@users.noreply.github.com> Date: Thu, 24 Oct 2024 05:41:27 +0000 Subject: [PATCH] feat: add traces reader to proof producer --- .gitignore | 4 + CMakeLists.txt | 2 +- .../nil/blueprint/zkevm/zkevm_word.hpp | 4 +- flake.lock | 6 +- proof-producer.nix | 5 +- proof-producer/CMakeLists.txt | 4 +- .../bin/proof-producer/CMakeLists.txt | 32 +++- .../nil/proof-generator/file_operations.hpp | 5 + .../include/nil/proof-generator/prover.hpp | 41 +++-- .../nil/proof-generator/traces_reader.hpp | 172 ++++++++++++++++++ .../bin/proof-producer/proto/traces.proto | 75 ++++++++ .../bin/proof-producer/src/arg_parser.hpp | 1 + .../bin/proof-producer/src/main.cpp | 12 +- 13 files changed, 336 insertions(+), 27 deletions(-) create mode 100644 proof-producer/bin/proof-producer/include/nil/proof-generator/traces_reader.hpp create mode 100644 proof-producer/bin/proof-producer/proto/traces.proto diff --git a/.gitignore b/.gitignore index 6655d0051b..3fb7163c31 100644 --- a/.gitignore +++ b/.gitignore @@ -637,3 +637,7 @@ callgrind.* .ycm_extra_config.py .color_coded + +# Ignore generated protobufs +*.pb.h +*.pb.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 5afeac3c96..187f23906a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_compile_options (-fcolor-diagnostics) endif () -if(DEFINED CMAKE_BUILD_TYPE AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") +if(DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -ggdb") set(BOOST_FORCEINLINE "OFF") # improves debugging traces endif() diff --git a/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp b/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp index 9224795e02..e0f5a240e1 100644 --- a/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp +++ b/crypto3/libs/blueprint/include/nil/blueprint/zkevm/zkevm_word.hpp @@ -32,12 +32,12 @@ namespace nil { namespace blueprint { - constexpr static const + inline constexpr const boost::multiprecision::number< boost::multiprecision::backends::cpp_int_modular_backend<257>> zkevm_modulus = 0x10000000000000000000000000000000000000000000000000000000000000000_cppui_modular257; - constexpr static const boost::multiprecision::backends::modular_params< + inline constexpr const boost::multiprecision::backends::modular_params< boost::multiprecision::backends::cpp_int_modular_backend<257>> zkevm_modular_params = zkevm_modulus.backend(); diff --git a/flake.lock b/flake.lock index 55de22fc35..82c760dee0 100644 --- a/flake.lock +++ b/flake.lock @@ -43,11 +43,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1728462432, - "narHash": "sha256-TE4jYs94xWlanv5I6evd9Epq3+spic++zwR6E+chnV4=", + "lastModified": 1728888510, + "narHash": "sha256-nsNdSldaAyu6PE3YUA+YQLqUDJh+gRbBooMMekZJwvI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b755388e12a4b7c84153c3351f14349587ae36a3", + "rev": "a3c0b3b21515f74fd2665903d4ce6bc4dc81c77c", "type": "github" }, "original": { diff --git a/proof-producer.nix b/proof-producer.nix index bef5113cd4..f6be8446b4 100644 --- a/proof-producer.nix +++ b/proof-producer.nix @@ -6,6 +6,7 @@ boost, gdb, lldb, + protobuf, cmake_modules, enableDebugging, enableDebug ? false, @@ -19,14 +20,14 @@ in stdenv.mkDerivation { src = lib.sourceByRegex ./. ["^proof-producer(/.*)?$" "^crypto3(/.*)?$" "^parallel-crypto3(/.*)?$" "CMakeLists.txt"]; hardeningDisable = [ "fortify" ]; - nativeBuildInputs = [ cmake ninja pkg-config ] ++ + nativeBuildInputs = [ cmake ninja pkg-config protobuf ] ++ (lib.optional (!stdenv.isDarwin) gdb) ++ (lib.optional (stdenv.isDarwin) lldb); # enableDebugging will keep debug symbols in boost propagatedBuildInputs = [ (if enableDebug then (enableDebugging boost) else boost) ]; - buildInputs = [cmake_modules ]; + buildInputs = [ cmake_modules ]; cmakeFlags = [ diff --git a/proof-producer/CMakeLists.txt b/proof-producer/CMakeLists.txt index dcff5766ab..d95d7903ee 100644 --- a/proof-producer/CMakeLists.txt +++ b/proof-producer/CMakeLists.txt @@ -10,7 +10,7 @@ cmake_minimum_required(VERSION 3.22 FATAL_ERROR) -if(DEFINED CMAKE_BUILD_TYPE AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") +if(DEFINED CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL "Debug") set(ZK_PLACEHOLDER_DEBUG_ENABLED TRUE) endif() @@ -26,7 +26,7 @@ cm_project(proof-producer WORKSPACE_NAME ${CMAKE_WORKSPACE_NAME} LANGUAGES CXX) # If Nix is used, LSP could not guess the locations of implicit include # directories, so we need to include them explicitly. if(CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) endif() diff --git a/proof-producer/bin/proof-producer/CMakeLists.txt b/proof-producer/bin/proof-producer/CMakeLists.txt index 32c7e696f1..0bf4f89da6 100644 --- a/proof-producer/bin/proof-producer/CMakeLists.txt +++ b/proof-producer/bin/proof-producer/CMakeLists.txt @@ -12,6 +12,9 @@ include(CMDeploy) include(CMSetupVersion) +find_package(Protobuf REQUIRED) +find_package(absl REQUIRED) + if (CPACK_PACKAGE_VERSION) add_compile_definitions(PROOF_GENERATOR_VERSION=${CPACK_PACKAGE_VERSION}) endif() @@ -38,6 +41,7 @@ function(setup_proof_generator_target) add_executable(${ARG_TARGET_NAME} src/arg_parser.cpp src/main.cpp + ${PROTO_SRC} ) add_library(${ARG_TARGET_NAME}-lib INTERFACE) @@ -58,10 +62,12 @@ function(setup_proof_generator_target) target_include_directories(${ARG_TARGET_NAME}-lib INTERFACE $ $ + ${PROTOBUF_INCLUDE_DIR} ) target_link_libraries(${ARG_TARGET_NAME}-lib INTERFACE ${INTERFACE_LIBS}) - target_link_libraries(${ARG_TARGET_NAME} PRIVATE ${ARG_TARGET_NAME}-lib Boost::program_options) + # absl is required for protobuf + target_link_libraries(${ARG_TARGET_NAME} PRIVATE ${ARG_TARGET_NAME}-lib Boost::program_options absl::log_internal_check_op crypto3::blueprint ${PROTOBUF_LIBRARY}) target_include_directories(${ARG_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src @@ -81,6 +87,30 @@ function(setup_proof_generator_target) endfunction() +file(GLOB PROTO_FILES "proto/*.proto") + +foreach(PROTO_FILE ${PROTO_FILES}) + get_filename_component(PROTO_NAME_WE ${PROTO_FILE} NAME_WE) + get_filename_component(PROTO_DIR ${PROTO_FILE} DIRECTORY) + + set(OUTPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/nil/proof-generator") + set(PROTO_HDR "${OUTPUT_DIR}/${PROTO_NAME_WE}.pb.h") + set(PROTO_SRC "${OUTPUT_DIR}/${PROTO_NAME_WE}.pb.cc") + + add_custom_command( + OUTPUT ${PROTO_HDR} ${PROTO_SRC} + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS --cpp_out=${OUTPUT_DIR} --proto_path ${PROTO_DIR} ${PROTO_FILE} + DEPENDS ${PROTO_FILE} + COMMENT "Generating ${PROTO_SRC} and ${PROTO_HDR} from ${PROTO_FILE}" + VERBATIM + + ) + + list(APPEND GENERATED_SOURCES ${PROTO_SRC}) + list(APPEND GENERATED_HEADERS ${PROTO_HDR}) +endforeach() + set(SINGLE_THREADED_TARGET "${CURRENT_PROJECT_NAME}-single-threaded") setup_proof_generator_target(TARGET_NAME ${SINGLE_THREADED_TARGET} ADDITIONAL_DEPENDENCIES crypto3::all) set(MULTI_THREADED_TARGET "${CURRENT_PROJECT_NAME}-multi-threaded") diff --git a/proof-producer/bin/proof-producer/include/nil/proof-generator/file_operations.hpp b/proof-producer/bin/proof-producer/include/nil/proof-generator/file_operations.hpp index 43e496c0cd..761b1b1efc 100644 --- a/proof-producer/bin/proof-producer/include/nil/proof-generator/file_operations.hpp +++ b/proof-producer/bin/proof-producer/include/nil/proof-generator/file_operations.hpp @@ -29,6 +29,11 @@ namespace nil { namespace proof_generator { inline bool is_valid_path(const std::string& path) { + if (path.empty()) { + BOOST_LOG_TRIVIAL(error) << "Provided file path is empty."; + return false; + } + if (path.length() >= PATH_MAX) { BOOST_LOG_TRIVIAL(error) << path << ": file path is too long. Maximum allowed length is " << PATH_MAX << " characters."; diff --git a/proof-producer/bin/proof-producer/include/nil/proof-generator/prover.hpp b/proof-producer/bin/proof-producer/include/nil/proof-generator/prover.hpp index cf510c6c7c..c4520a1d96 100644 --- a/proof-producer/bin/proof-producer/include/nil/proof-generator/prover.hpp +++ b/proof-producer/bin/proof-producer/include/nil/proof-generator/prover.hpp @@ -55,10 +55,11 @@ #include - #include #include +#include + namespace nil { namespace proof_generator { namespace detail { @@ -102,18 +103,20 @@ namespace nil { } enum class ProverStage { - ALL = 0, - PREPROCESS = 1, - PROVE = 2, - VERIFY = 3, - GENERATE_AGGREGATED_CHALLENGE = 4, - GENERATE_PARTIAL_PROOF = 5, - COMPUTE_COMBINED_Q = 6, - GENERATE_AGGREGATED_FRI_PROOF = 7, - GENERATE_CONSISTENCY_CHECKS_PROOF = 8, - MERGE_PROOFS = 9 + ALL, + PREPROCESS, + PROVE, + VERIFY, + GENERATE_AGGREGATED_CHALLENGE, + GENERATE_PARTIAL_PROOF, + COMPUTE_COMBINED_Q, + GENERATE_AGGREGATED_FRI_PROOF, + GENERATE_CONSISTENCY_CHECKS_PROOF, + MERGE_PROOFS, + READ_TRACES }; + // TODO: make it another macro in parser for two-way conversion ProverStage prover_stage_from_string(const std::string& stage) { static std::unordered_map stage_map = { {"all", ProverStage::ALL}, @@ -125,7 +128,8 @@ namespace nil { {"compute-combined-Q", ProverStage::COMPUTE_COMBINED_Q}, {"merge-proofs", ProverStage::MERGE_PROOFS}, {"aggregated-FRI", ProverStage::GENERATE_AGGREGATED_FRI_PROOF}, - {"consistency-checks", ProverStage::GENERATE_CONSISTENCY_CHECKS_PROOF} + {"consistency-checks", ProverStage::GENERATE_CONSISTENCY_CHECKS_PROOF}, + {"read-traces", ProverStage::READ_TRACES} }; auto it = stage_map.find(stage); if (it == stage_map.end()) { @@ -462,6 +466,18 @@ namespace nil { return true; } + bool read_execution_traces_from_file(boost::filesystem::path execution_traces_file_path) { + BOOST_LOG_TRIVIAL(info) << "Read execution traces from " << execution_traces_file_path; + + auto traces = deserialize_traces_from_file(execution_traces_file_path); + if (!traces) { + return false; + } + execution_traces_.emplace(*traces); + + return true; + } + bool verify(const Proof& proof) const { BOOST_LOG_TRIVIAL(info) << "Verifying proof..."; bool verification_result = @@ -972,6 +988,7 @@ namespace nil { std::optional table_description_; std::optional constraint_system_; std::optional assignment_table_; + std::optional execution_traces_; std::optional lpc_scheme_; }; diff --git a/proof-producer/bin/proof-producer/include/nil/proof-generator/traces_reader.hpp b/proof-producer/bin/proof-producer/include/nil/proof-generator/traces_reader.hpp new file mode 100644 index 0000000000..6330de8dca --- /dev/null +++ b/proof-producer/bin/proof-producer/include/nil/proof-generator/traces_reader.hpp @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include + + +namespace nil { + namespace proof_generator { + + struct StackOp { + bool is_read; + int idx; + blueprint::zkevm_word_type value; + uint64_t pc; + uint32_t msg_id; + uint64_t rw_idx; + }; + + struct MemoryOp { + bool is_read; + int idx; + std::byte value; + uint64_t pc; + uint32_t msg_id; + uint64_t rw_idx; + }; + + struct StorageOp { + bool is_read; + std::string key; // hex string of hash + blueprint::zkevm_word_type value; + uint64_t pc; + uint32_t msg_id; + uint64_t rw_idx; + }; + + struct SlotChangeTrace { + std::string key; // hex string of hash + std::string root_before; // hex string of hash + std::string root_after; // hex string of hash + blueprint::zkevm_word_type value_before; + blueprint::zkevm_word_type value_after; + std::vector proof; + }; + + struct ExecutionTraces { + std::vector stack_ops; + std::vector memory_ops; + std::vector storage_ops; + std::vector>> slots_changes; // per message + std::unordered_map> contracts_bytecode; + }; + + [[nodiscard]] blueprint::zkevm_word_type proto_uint256_to_zkevm_word(const pb::Uint256& pb_uint) { + blueprint::zkevm_word_type result = 0; + for (size_t i = 0; i < pb_uint.word_parts_size() && i < 4; i++) { + result |= (static_cast(pb_uint.word_parts(i)) << (i * 64)); + } + return result; + } + + [[nodiscard]] std::optional read_pb_traces_from_file(const boost::filesystem::path& filename) { + if (!is_valid_path(filename.c_str()) || !can_read_from_file(filename.c_str())) { + return std::nullopt; + } + + auto file = open_file(filename.c_str(), std::ios::in | std::ios::binary); + if (!file) { + return std::nullopt; + } + + pb::ExecutionTraces pb_traces; + if (!pb_traces.ParseFromIstream(&*file)) { + return std::nullopt; + } + + return pb_traces; + } + + [[nodiscard]] std::optional deserialize_traces_from_file(const boost::filesystem::path& filename) { + const auto pb_traces = read_pb_traces_from_file(filename); + if (!pb_traces) { + return std::nullopt; + } + + ExecutionTraces traces; + + // Convert stack operations + traces.stack_ops.reserve(pb_traces->stack_ops_size()); + for (const auto& pb_sop : pb_traces->stack_ops()) { + traces.stack_ops.push_back(StackOp{ + /*is_read=*/ pb_sop.is_read(), + /*idx=*/ static_cast(pb_sop.index()), + /*value=*/ proto_uint256_to_zkevm_word(pb_sop.value()), + /*pc=*/ pb_sop.pc(), + /*msg_id=*/ static_cast(pb_sop.msg_id()), + /*rw_idx=*/ pb_sop.rw_idx() + }); + } + + // Convert memory operations + traces.memory_ops.reserve(pb_traces->memory_ops_size()); + for (const auto& pb_mop : pb_traces->memory_ops()) { + traces.memory_ops.push_back(MemoryOp{ + /*is_read=*/ pb_mop.is_read(), + /*idx=*/ static_cast(pb_mop.index()), + /*value=*/ static_cast(pb_mop.value()[0]), + /*pc=*/ pb_mop.pc(), + /*msg_id=*/ static_cast(pb_mop.msg_id()), + /*rw_idx=*/ pb_mop.rw_idx() + }); + } + + // Convert storage operations + traces.storage_ops.reserve(pb_traces->storage_ops_size()); + for (const auto& pb_sop : pb_traces->storage_ops()) { + traces.storage_ops.push_back(StorageOp{ + /*is_read=*/ pb_sop.is_read(), + /*key=*/ pb_sop.key(), + /*value=*/ proto_uint256_to_zkevm_word(pb_sop.value()), + /*pc=*/ pb_sop.pc(), + /*msg_id=*/ static_cast(pb_sop.msg_id()), + /*rw_idx=*/ pb_sop.rw_idx() + }); + } + + // Convert message traces (slots changes) + traces.slots_changes.reserve(pb_traces->message_traces_size()); + for (const auto& pb_msg_trace : pb_traces->message_traces()) { + std::unordered_map> addr_changes; + + for (const auto& [addr, pb_storage_traces] : pb_msg_trace.storage_traces_by_address()) { + std::vector slot_changes; + slot_changes.reserve(pb_storage_traces.slots_changes_size()); + + for (const auto& pb_slot : pb_storage_traces.slots_changes()) { + slot_changes.push_back(SlotChangeTrace{ + /*key=*/ pb_slot.key(), + /*root_before=*/ pb_slot.root_before(), + /*root_after=*/ pb_slot.root_after(), + /*value_before=*/ proto_uint256_to_zkevm_word(pb_slot.value_before()), + /*value_after=*/ proto_uint256_to_zkevm_word(pb_slot.value_after()), + /*proof=*/ std::vector( + reinterpret_cast(pb_slot.ssz_proof().data()), + reinterpret_cast(pb_slot.ssz_proof().data() + pb_slot.ssz_proof().size()) + ) + }); + } + addr_changes[addr] = std::move(slot_changes); + } + traces.slots_changes.push_back(std::move(addr_changes)); + } + + // Convert contract bytecodes + for (const auto& [addr, bytecode] : pb_traces->contract_bytecodes()) { + traces.contracts_bytecode[addr] = std::vector( + reinterpret_cast(bytecode.data()), + reinterpret_cast(bytecode.data() + bytecode.size()) + ); + } + + return traces; + } + + } // namespace proof_generator +} // namespace nil diff --git a/proof-producer/bin/proof-producer/proto/traces.proto b/proof-producer/bin/proof-producer/proto/traces.proto new file mode 100644 index 0000000000..0f249fa92b --- /dev/null +++ b/proof-producer/bin/proof-producer/proto/traces.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +package nil.proof_generator.pb; +option go_package = "/proto"; + +// Uint256 represents a 256-bit unsigned integer as a sequence of uint64 parts +message Uint256 { + repeated uint64 word_parts = 1; // 4 uint64 parts composing the 256-bit number +} + +// Address represents an Ethereum address +message Address { + bytes address_bytes = 1; // 20-byte address +} + +// StackOp represents a single stack operation () +message StackOp { + bool is_read = 1; + int32 index = 2; // Index in the stack + Uint256 value = 3; + uint64 pc = 4; + uint64 msg_id = 5; // Number of message within a block + uint64 rw_idx = 6; // shared between all ops counter +} + +// MemoryOp represents a single memory operation (byte) +message MemoryOp { + bool is_read = 1; + int32 index = 2; // Index in memory + bytes value = 3; // Single byte value + uint64 pc = 4; + uint64 msg_id = 5; // Number of message within a block + uint64 rw_idx = 6; // shared between all ops counter +} + +// StorageOp represents a single storage operation +message StorageOp { + bool is_read = 1; + // HEX of hash, bytes would be more space-efficient, but less readable + string key = 2; + Uint256 value = 3; // Single byte value + uint64 pc = 4; + uint64 msg_id = 5; // Number of message within a block + uint64 rw_idx = 6; // shared between all ops counter +} + +// SlotChangeTrace represents a trace of storage slot change. Only initial and final value for each message +message SlotChangeTrace { + // HEX of hashes, bytes would be more space-efficient, but less readable + string key = 1; + string root_before = 2; + string root_after = 3; + Uint256 value_before = 4; + Uint256 value_after = 5; + bytes ssz_proof = 6; +} + +message AdderssSlotsChanges { + repeated SlotChangeTrace slots_changes = 1; +} + +// MessageTraces contains traces related to a single executed message +message MessageTraces { + // HEX address to slots changes in address's storage + map storage_traces_by_address = 1; +} + +// ExecutionTraces represents all proofs related to contract execution +message ExecutionTraces { + repeated StackOp stack_ops = 1; + repeated MemoryOp memory_ops = 2; + repeated StorageOp storage_ops = 3; + repeated MessageTraces message_traces = 4; + map contract_bytecodes = 5; +} diff --git a/proof-producer/bin/proof-producer/src/arg_parser.hpp b/proof-producer/bin/proof-producer/src/arg_parser.hpp index e36dca7862..1cd6d839ac 100644 --- a/proof-producer/bin/proof-producer/src/arg_parser.hpp +++ b/proof-producer/bin/proof-producer/src/arg_parser.hpp @@ -61,6 +61,7 @@ namespace nil { boost::log::trivial::severity_level log_level = boost::log::trivial::severity_level::info; CurvesVariant elliptic_curve_type = type_identity{}; HashesVariant hash_type = type_identity>{}; + boost::filesystem::path execution_traces_path; std::size_t lambda = 9; std::size_t grind = 0; diff --git a/proof-producer/bin/proof-producer/src/main.cpp b/proof-producer/bin/proof-producer/src/main.cpp index 1d6c8a20a2..19752007dc 100644 --- a/proof-producer/bin/proof-producer/src/main.cpp +++ b/proof-producer/bin/proof-producer/src/main.cpp @@ -50,7 +50,7 @@ int run_prover(const nil::proof_generator::ProverOptions& prover_options) { prover.generate_to_file( prover_options.proof_file_path, prover_options.json_file_path, - false/*don't skip verification*/) && + false/*don't skip verification*/) && prover.save_preprocessed_common_data_to_file(prover_options.preprocessed_common_data_path) && prover.save_public_preprocessed_data_to_file(prover_options.preprocessed_public_data_path) && prover.save_commitment_state_to_file(prover_options.commitment_scheme_state_path); @@ -116,14 +116,14 @@ int run_prover(const nil::proof_generator::ProverOptions& prover_options) { prover_options.proof_file_path); break; case nil::proof_generator::detail::ProverStage::COMPUTE_COMBINED_Q: - prover_result = + prover_result = prover.read_commitment_scheme_from_file(prover_options.commitment_scheme_state_path) && prover.generate_combined_Q_to_file( prover_options.aggregated_challenge_file, prover_options.combined_Q_starting_power, prover_options.combined_Q_polynomial_file); break; case nil::proof_generator::detail::ProverStage::GENERATE_AGGREGATED_FRI_PROOF: - prover_result = + prover_result = prover.read_assignment_description(prover_options.assignment_description_file_path) && prover.generate_aggregated_FRI_proof_to_file( prover_options.aggregated_challenge_file, @@ -133,7 +133,7 @@ int run_prover(const nil::proof_generator::ProverOptions& prover_options) { prover_options.consistency_checks_challenges_file); break; case nil::proof_generator::detail::ProverStage::GENERATE_CONSISTENCY_CHECKS_PROOF: - prover_result = + prover_result = prover.read_commitment_scheme_from_file(prover_options.commitment_scheme_state_path) && prover.generate_consistency_checks_to_file( prover_options.combined_Q_polynomial_file, @@ -141,6 +141,10 @@ int run_prover(const nil::proof_generator::ProverOptions& prover_options) { prover_options.proof_file_path ); break; + case nil::proof_generator::detail::ProverStage::READ_TRACES: + prover_result = + prover.read_execution_traces_from_file(prover_options.execution_traces_path); + break; } } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(error) << e.what();