diff --git a/.gitmodules b/.gitmodules index ffb4b09dc5..e14b6bafaf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,6 +18,9 @@ [submodule "libraries/eos-vm"] path = libraries/eos-vm url = https://github.com/eosnetworkfoundation/mandel-eos-vm +[submodule "libraries/rapidjson"] + path = libraries/rapidjson + url = https://github.com/Tencent/rapidjson/ [submodule "eosio-wasm-spec-tests"] path = eosio-wasm-spec-tests url = https://github.com/eosnetworkfoundation/mandel-wasm-spec-tests diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 4a05f74d1e..c59b97c28e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -131,6 +131,7 @@ target_link_libraries( eosio_chain PUBLIC fc chainbase Logging IR WAST WASM Runt target_include_directories( eosio_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../wasm-jit/Include" + "${CMAKE_CURRENT_SOURCE_DIR}/../rapidjson/include" ) add_library(eosio_chain_wrap INTERFACE ) diff --git a/libraries/chain/include/eosio/chain/snapshot.hpp b/libraries/chain/include/eosio/chain/snapshot.hpp index e5bfd26243..b4e8fb1a61 100644 --- a/libraries/chain/include/eosio/chain/snapshot.hpp +++ b/libraries/chain/include/eosio/chain/snapshot.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace eosio { namespace chain { /** @@ -272,11 +273,6 @@ namespace eosio { namespace chain { read_section(detail::snapshot_section_traits::section_name(), f); } - template - bool has_section(const std::string& suffix = std::string()) { - return has_section(suffix + detail::snapshot_section_traits::section_name()); - } - virtual void validate() const = 0; virtual void return_to_header() = 0; @@ -284,7 +280,6 @@ namespace eosio { namespace chain { virtual ~snapshot_reader(){}; protected: - virtual bool has_section( const std::string& section_name ) = 0; virtual void set_section( const std::string& section_name ) = 0; virtual bool read_row( detail::abstract_snapshot_row_reader& row_reader ) = 0; virtual bool empty( ) = 0; @@ -313,7 +308,6 @@ namespace eosio { namespace chain { explicit variant_snapshot_reader(const fc::variant& snapshot); void validate() const override; - bool has_section( const string& section_name ) override; void set_section( const string& section_name ) override; bool read_row( detail::abstract_snapshot_row_reader& row_reader ) override; bool empty ( ) override; @@ -342,7 +336,22 @@ namespace eosio { namespace chain { std::streampos header_pos; std::streampos section_pos; uint64_t row_count; + }; + class ostream_json_snapshot_writer : public snapshot_writer { + public: + explicit ostream_json_snapshot_writer(std::ostream& snapshot); + + void write_start_section( const std::string& section_name ) override; + void write_row( const detail::abstract_snapshot_row_writer& row_writer ) override; + void write_end_section() override; + void finalize(); + + static const uint32_t magic_number = 0x30510550; + + private: + detail::ostream_wrapper snapshot; + uint64_t row_count; }; class istream_snapshot_reader : public snapshot_reader { @@ -350,7 +359,6 @@ namespace eosio { namespace chain { explicit istream_snapshot_reader(std::istream& snapshot); void validate() const override; - bool has_section( const string& section_name ) override; void set_section( const string& section_name ) override; bool read_row( detail::abstract_snapshot_row_reader& row_reader ) override; bool empty ( ) override; @@ -366,6 +374,24 @@ namespace eosio { namespace chain { uint64_t cur_row; }; + class istream_json_snapshot_reader : public snapshot_reader { + public: + explicit istream_json_snapshot_reader(const fc::path& p); + ~istream_json_snapshot_reader(); + + void validate() const override; + void set_section( const string& section_name ) override; + bool read_row( detail::abstract_snapshot_row_reader& row_reader ) override; + bool empty ( ) override; + void clear_section() override; + void return_to_header() override; + + private: + bool validate_section() const; + + std::unique_ptr impl; + }; + class integrity_hash_snapshot_writer : public snapshot_writer { public: explicit integrity_hash_snapshot_writer(fc::sha256::encoder& enc); diff --git a/libraries/chain/snapshot.cpp b/libraries/chain/snapshot.cpp index 66966ef6be..3d87afdb81 100644 --- a/libraries/chain/snapshot.cpp +++ b/libraries/chain/snapshot.cpp @@ -1,6 +1,15 @@ #include #include #include +#include + +#define RAPIDJSON_NAMESPACE eosio_rapidjson +#include +#include +#include +#include + +using namespace eosio_rapidjson; namespace eosio { namespace chain { @@ -76,17 +85,6 @@ void variant_snapshot_reader::validate() const { } } -bool variant_snapshot_reader::has_section( const string& section_name ) { - const auto& sections = snapshot["sections"].get_array(); - for( const auto& section: sections ) { - if (section["name"].as_string() == section_name) { - return true; - } - } - - return false; -} - void variant_snapshot_reader::set_section( const string& section_name ) { const auto& sections = snapshot["sections"].get_array(); for( const auto& section: sections ) { @@ -190,6 +188,45 @@ void ostream_snapshot_writer::finalize() { snapshot.write((char*)&end_marker, sizeof(end_marker)); } +ostream_json_snapshot_writer::ostream_json_snapshot_writer(std::ostream& snapshot) + :snapshot(snapshot) + ,row_count(0) +{ + snapshot << "{\n"; + // write magic number + auto totem = magic_number; + snapshot << "\"magic_number\":" << fc::json::to_string(totem, fc::time_point::maximum()) << "\n"; + + // write version + auto version = current_snapshot_version; + snapshot << ",\"version\":" << fc::json::to_string(version, fc::time_point::maximum()) << "\n"; +} + +void ostream_json_snapshot_writer::write_start_section( const std::string& section_name ) +{ + row_count = 0; + snapshot.inner << "," << fc::json::to_string(section_name, fc::time_point::maximum()) << ":{\n\"rows\":[\n"; +} + +void ostream_json_snapshot_writer::write_row( const detail::abstract_snapshot_row_writer& row_writer ) { + const auto yield = [&](size_t s) {}; + + if(row_count != 0) snapshot.inner << ","; + snapshot.inner << fc::json::to_string(row_writer.to_variant(), yield) << "\n"; + ++row_count; +} + +void ostream_json_snapshot_writer::write_end_section( ) { + snapshot.inner << "],\n\"num_rows\":" << row_count << "\n}\n"; + row_count = 0; +} + +void ostream_json_snapshot_writer::finalize() { + snapshot.inner << "}\n"; + snapshot.inner.flush(); +} + + istream_snapshot_reader::istream_snapshot_reader(std::istream& snapshot) :snapshot(snapshot) ,header_pos(snapshot.tellg()) @@ -246,44 +283,6 @@ bool istream_snapshot_reader::validate_section() const { return true; } -bool istream_snapshot_reader::has_section( const string& section_name ) { - auto restore_pos = fc::make_scoped_exit([this,pos=snapshot.tellg()](){ - snapshot.seekg(pos); - }); - - const std::streamoff header_size = sizeof(ostream_snapshot_writer::magic_number) + sizeof(current_snapshot_version); - - auto next_section_pos = header_pos + header_size; - - while (true) { - snapshot.seekg(next_section_pos); - uint64_t section_size = 0; - snapshot.read((char*)§ion_size,sizeof(section_size)); - if (section_size == std::numeric_limits::max()) { - break; - } - - next_section_pos = snapshot.tellg() + std::streamoff(section_size); - - uint64_t ignore = 0; - snapshot.read((char*)&ignore,sizeof(ignore)); - - bool match = true; - for(auto c : section_name) { - if(snapshot.get() != c) { - match = false; - break; - } - } - - if (match && snapshot.get() == 0) { - return true; - } - } - - return false; -} - void istream_snapshot_reader::set_section( const string& section_name ) { auto restore_pos = fc::make_scoped_exit([this,pos=snapshot.tellg()](){ snapshot.seekg(pos); @@ -346,6 +345,91 @@ void istream_snapshot_reader::return_to_header() { clear_section(); } +struct istream_json_snapshot_reader_impl { + uint64_t num_rows; + uint64_t cur_row; + eosio_rapidjson::Document doc; + std::string sec_name; +}; + +istream_json_snapshot_reader::~istream_json_snapshot_reader() = default; + +istream_json_snapshot_reader::istream_json_snapshot_reader(const fc::path& p) + : impl{new istream_json_snapshot_reader_impl{0, 0, {}, {}}} +{ + FILE* fp = fopen(p.string().c_str(), "rb"); + EOS_ASSERT(fp, snapshot_exception, "Failed to JSON snapshot: ${file}", ("file", p)); + auto close = fc::make_scoped_exit( [&fp]() { fclose( fp ); } ); + char readBuffer[65536]; + eosio_rapidjson::FileReadStream is(fp, readBuffer, sizeof(readBuffer)); + impl->doc.ParseStream(is); +} + +void istream_json_snapshot_reader::validate() const { + try { + // validate totem + auto expected_totem = ostream_json_snapshot_writer::magic_number; + EOS_ASSERT(impl->doc.HasMember("magic_number"), snapshot_exception, "magic_number section not found" ); + auto actual_totem = impl->doc["magic_number"].GetInt(); + EOS_ASSERT( actual_totem == expected_totem, snapshot_exception, "JSON snapshot has unexpected magic number" ); + + // validate version + auto expected_version = current_snapshot_version; + EOS_ASSERT(impl->doc.HasMember("version"), snapshot_exception, "version section not found" ); + auto actual_version = impl->doc["version"].GetInt(); + EOS_ASSERT( actual_version == expected_version, snapshot_exception, + "JSON snapshot is an unsupported version. Expected : ${expected}, Got: ${actual}", + ("expected", expected_version)( "actual", actual_version ) ); + + } catch( const std::exception& e ) { \ + snapshot_exception fce(FC_LOG_MESSAGE( warn, "JSON snapshot validation threw IO exception (${what})",("what",e.what()))); + throw fce; + } +} + +bool istream_json_snapshot_reader::validate_section() const { + return true; +} + +void istream_json_snapshot_reader::set_section( const string& section_name ) { + EOS_ASSERT( impl->doc.HasMember( section_name.c_str() ), snapshot_exception, "JSON snapshot has no section ${sec}", ("sec", section_name) ); + EOS_ASSERT( impl->doc[section_name.c_str()].HasMember( "num_rows" ), snapshot_exception, "JSON snapshot ${sec} num_rows not found", ("sec", section_name) ); + EOS_ASSERT( impl->doc[section_name.c_str()].HasMember( "rows" ), snapshot_exception, "JSON snapshot ${sec} rows not found", ("sec", section_name) ); + EOS_ASSERT( impl->doc[section_name.c_str()]["rows"].IsArray(), snapshot_exception, "JSON snapshot ${sec} rows is not an array", ("sec_name", section_name) ); + + impl->sec_name = section_name; + impl->num_rows = impl->doc[section_name.c_str()]["num_rows"].GetInt(); + ilog( "reading ${section_name}, num_rows: ${num_rows}", ("section_name", section_name)( "num_rows", impl->num_rows ) ); +} + +bool istream_json_snapshot_reader::read_row( detail::abstract_snapshot_row_reader& row_reader ) { + EOS_ASSERT( impl->cur_row < impl->num_rows, snapshot_exception, "JSON snapshot ${sect}'s cur_row ${cur_row} >= num_rows ${num_rows}", + ("sect_name", impl->sec_name)( "cur_row", impl->cur_row )( "num_rows", impl->num_rows ) ); + + const eosio_rapidjson::Value& rows = impl->doc[impl->sec_name.c_str()]["rows"]; + eosio_rapidjson::StringBuffer buffer; + eosio_rapidjson::Writer writer( buffer ); + rows[impl->cur_row].Accept( writer ); + + const auto& row = fc::json::from_string( buffer.GetString() ); + row_reader.provide( row ); + return ++impl->cur_row < impl->num_rows; +} + +bool istream_json_snapshot_reader::empty ( ) { + return impl->num_rows == 0; +} + +void istream_json_snapshot_reader::clear_section() { + impl->num_rows = 0; + impl->cur_row = 0; + impl->sec_name = ""; +} + +void istream_json_snapshot_reader::return_to_header() { + clear_section(); +} + integrity_hash_snapshot_writer::integrity_hash_snapshot_writer(fc::sha256::encoder& enc) :enc(enc) { diff --git a/libraries/rapidjson b/libraries/rapidjson new file mode 160000 index 0000000000..27c3a8dc0e --- /dev/null +++ b/libraries/rapidjson @@ -0,0 +1 @@ +Subproject commit 27c3a8dc0e2c9218fe94986d249a12b5ed838f1d diff --git a/libraries/testing/include/eosio/testing/snapshot_suites.hpp b/libraries/testing/include/eosio/testing/snapshot_suites.hpp index adec77a22b..ba3ea691de 100644 --- a/libraries/testing/include/eosio/testing/snapshot_suites.hpp +++ b/libraries/testing/include/eosio/testing/snapshot_suites.hpp @@ -106,5 +106,62 @@ struct buffered_snapshot_suite { } }; -using snapshot_suites = boost::mpl::list; + +struct json_snapshot_suite { + using writer_t = ostream_json_snapshot_writer; + using reader_t = istream_json_snapshot_reader; + using write_storage_t = std::ostringstream; + using snapshot_t = std::string; + using read_storage_t = std::istringstream; + + struct writer : public writer_t { + writer( const std::shared_ptr& storage ) + :writer_t(*storage) + ,storage(storage) + { + + } + + std::shared_ptr storage; + }; + + struct reader : public reader_t { + explicit reader(const fc::path& p) + :reader_t(p) + {} + ~reader() { + remove("temp.bin.json"); + } + }; + + + static auto get_writer() { + return std::make_shared(std::make_shared()); + } + + static auto finalize(const std::shared_ptr& w) { + w->finalize(); + return w->storage->str(); + } + + static auto get_reader( const snapshot_t& buffer) { + std::ofstream fs("temp.bin.json"); + fs << buffer; + fs.close(); + fc::path p("temp.bin.json"); + return std::make_shared(p); + } + + static snapshot_t load_from_file(const std::string& filename) { + snapshot_input_file file(filename); + return file.read_as_string(); + } + + static void write_to_file( const std::string& basename, const snapshot_t& snapshot ) { + snapshot_output_file file(basename); + file.write(snapshot); + } +}; + +using snapshot_suites = boost::mpl::list; diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 9141072cde..cf6e40db50 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -161,6 +161,7 @@ class chain_plugin_impl { std::optional wasm_runtime; fc::microseconds abi_serializer_max_time_us; std::optional snapshot_path; + snapshot_reader_ptr snapshot_json_reader_ptr; // retained references to channels for easy publication @@ -360,6 +361,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "print build environment information to console as JSON and exit") ("extract-build-info", bpo::value(), "extract build environment information as JSON, write into specified file, and exit") + ("snapshot-to-json", bpo::value(), + "snapshot file to convert to JSON format, writes to .json (tmp state dir used), and exit") ("force-all-checks", bpo::bool_switch()->default_value(false), "do not skip any validation checks while replaying blocks (useful for replaying blocks from untrusted source)") ("disable-replay-opts", bpo::bool_switch()->default_value(false), @@ -374,7 +377,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "stop hard replay / block log recovery at this block number (if set to non-zero number)") ("terminate-at-block", bpo::value()->default_value(0), "terminate after reaching this block number (if set to a non-zero number)") - ("snapshot", bpo::value(), "File to read Snapshot State from") + ("snapshot", bpo::value(), "File to read snapshot state from, .json extension for JSON input.") ; } @@ -862,6 +865,58 @@ void chain_plugin::plugin_initialize(const variables_map& options) { EOS_THROW( extract_genesis_state_exception, "extracted genesis state from blocks.log" ); } + std::optional chain_id; + if( options.count("snapshot-to-json") ) { + my->snapshot_path = options.at( "snapshot-to-json" ).as(); + EOS_ASSERT( fc::exists(*my->snapshot_path), plugin_config_exception, + "Cannot load snapshot, ${name} does not exist", ("name", my->snapshot_path->generic_string()) ); + + // recover genesis information from the snapshot + // used for validation code below + auto infile = std::ifstream(my->snapshot_path->generic_string(), (std::ios::in | std::ios::binary)); + istream_snapshot_reader reader(infile); + reader.validate(); + chain_id = controller::extract_chain_id(reader); + infile.close(); + + boost::filesystem::path temp_dir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + my->chain_config->state_dir = temp_dir / "state"; + my->blocks_dir = temp_dir / "blocks"; + my->chain_config->blocks_dir = my->blocks_dir; + try { + auto shutdown = [](){ return app().quit(); }; + auto check_shutdown = [](){ return app().is_quiting(); }; + auto infile = std::ifstream(my->snapshot_path->generic_string(), (std::ios::in | std::ios::binary)); + auto reader = std::make_shared(infile); + my->chain.emplace( *my->chain_config, std::move(pfs), *chain_id ); + my->chain->add_indices(); + my->chain->startup(shutdown, check_shutdown, reader); + infile.close(); + EOS_ASSERT( !app().is_quiting(), snapshot_exception, "Loading of snapshot failed" ); + app().quit(); // shutdown as we will be finished after writing the snapshot + + ilog("Writing snapshot: ${s}", ("s", my->snapshot_path->generic_string() + ".json")); + auto snap_out = std::ofstream( my->snapshot_path->generic_string() + ".json", (std::ios::out) ); + auto writer = std::make_shared( snap_out ); + my->chain->write_snapshot( writer ); + writer->finalize(); + snap_out.flush(); + snap_out.close(); + } catch (const database_guard_exception& e) { + log_guard_exception(e); + // make sure to properly close the db + my->chain.reset(); + fc::remove_all(temp_dir); + throw; + } + my->chain.reset(); + fc::remove_all(temp_dir); + ilog("Completed writing snapshot: ${s}", ("s", my->snapshot_path->generic_string() + ".json")); + ilog("==== Ignore any additional log messages. ===="); + + EOS_THROW( node_management_success, "extracted json from snapshot" ); + } + // move fork_db to new location upgrade_from_reversible_to_fork_db( my.get() ); @@ -888,46 +943,45 @@ void chain_plugin::plugin_initialize(const variables_map& options) { wlog( "The --truncate-at-block option can only be used with --hard-replay-blockchain." ); } - std::optional chain_id; if (options.count( "snapshot" )) { - my->snapshot_path = options.at( "snapshot" ).as(); - EOS_ASSERT( fc::exists(*my->snapshot_path), plugin_config_exception, - "Cannot load snapshot, ${name} does not exist", ("name", my->snapshot_path->generic_string()) ); - - // recover genesis information from the snapshot - // used for validation code below - auto infile = std::ifstream(my->snapshot_path->generic_string(), (std::ios::in | std::ios::binary)); - istream_snapshot_reader reader(infile); - reader.validate(); - chain_id = controller::extract_chain_id(reader); - infile.close(); + bfs::path snap_path = options.at( "snapshot" ).as(); + EOS_ASSERT( fc::exists(snap_path), plugin_config_exception, + "Cannot load snapshot, ${name} does not exist", ("name", snap_path.generic_string()) ); + my->snapshot_path = snap_path; + + if( my->snapshot_path->extension().generic_string().compare( ".json" ) == 0 ) { + ilog("Reading JSON snapshot, this may take a significant amount of time."); + my->snapshot_json_reader_ptr.reset( new istream_json_snapshot_reader(snap_path) ); + my->snapshot_json_reader_ptr->validate(); + chain_id = controller::extract_chain_id(*my->snapshot_json_reader_ptr); + } else { + std::ifstream infile = std::ifstream(snap_path.generic_string(), (std::ios::in | std::ios::binary)); + istream_snapshot_reader reader(infile); + reader.validate(); + chain_id = controller::extract_chain_id(reader); + } - EOS_ASSERT( options.count( "genesis-timestamp" ) == 0, - plugin_config_exception, - "--snapshot is incompatible with --genesis-timestamp as the snapshot contains genesis information"); - EOS_ASSERT( options.count( "genesis-json" ) == 0, - plugin_config_exception, + EOS_ASSERT( options.count( "genesis-timestamp" ) == 0, plugin_config_exception, + "--snapshot is incompatible with --genesis-timestamp as the snapshot contains genesis information"); + EOS_ASSERT( options.count( "genesis-json" ) == 0, plugin_config_exception, "--snapshot is incompatible with --genesis-json as the snapshot contains genesis information"); auto shared_mem_path = my->chain_config->state_dir / "shared_memory.bin"; - EOS_ASSERT( !fc::is_regular_file(shared_mem_path), - plugin_config_exception, - "Snapshot can only be used to initialize an empty database." ); + EOS_ASSERT( !fc::is_regular_file(shared_mem_path), plugin_config_exception, + "A snapshot can only be used to initialize an empty database." ); if( fc::is_regular_file( my->blocks_dir / "blocks.log" )) { auto block_log_genesis = block_log::extract_genesis_state(my->blocks_dir); if( block_log_genesis ) { const auto& block_log_chain_id = block_log_genesis->compute_chain_id(); - EOS_ASSERT( *chain_id == block_log_chain_id, - plugin_config_exception, + EOS_ASSERT( *chain_id == block_log_chain_id, plugin_config_exception, "snapshot chain ID (${snapshot_chain_id}) does not match the chain ID from the genesis state in the block log (${block_log_chain_id})", ("snapshot_chain_id", *chain_id) ("block_log_chain_id", block_log_chain_id) ); } else { const auto& block_log_chain_id = block_log::extract_chain_id(my->blocks_dir); - EOS_ASSERT( *chain_id == block_log_chain_id, - plugin_config_exception, + EOS_ASSERT( *chain_id == block_log_chain_id, plugin_config_exception, "snapshot chain ID (${snapshot_chain_id}) does not match the chain ID (${block_log_chain_id}) in the block log", ("snapshot_chain_id", *chain_id) ("block_log_chain_id", block_log_chain_id) @@ -1253,10 +1307,14 @@ void chain_plugin::plugin_startup() auto shutdown = [](){ return app().quit(); }; auto check_shutdown = [](){ return app().is_quiting(); }; if (my->snapshot_path) { - auto infile = std::ifstream(my->snapshot_path->generic_string(), (std::ios::in | std::ios::binary)); - auto reader = std::make_shared(infile); - my->chain->startup(shutdown, check_shutdown, reader); - infile.close(); + if( my->snapshot_json_reader_ptr ) { + my->chain->startup(shutdown, check_shutdown, my->snapshot_json_reader_ptr); + my->snapshot_json_reader_ptr.reset(); + } else { + auto infile = std::ifstream( my->snapshot_path->generic_string(), (std::ios::in | std::ios::binary) ); + auto reader = std::make_shared( infile ); + my->chain->startup( shutdown, check_shutdown, reader ); + } } else if( my->genesis ) { my->chain->startup(shutdown, check_shutdown, *my->genesis); } else { diff --git a/unittests/snapshots.hpp.in b/unittests/snapshots.hpp.in index 762872b6ff..46f8944e48 100644 --- a/unittests/snapshots.hpp.in +++ b/unittests/snapshots.hpp.in @@ -19,10 +19,12 @@ namespace eosio { // tags for snapshot type struct json {}; struct binary {}; + struct json_snapshot{}; } // ns eosio::testing::snapshot static inline constexpr snapshot::json json_tag; static inline constexpr snapshot::binary binary_tag; + static inline constexpr snapshot::json_snapshot json_snapshot_tag; namespace snapshot { template @@ -30,6 +32,9 @@ namespace eosio { template static inline constexpr bool is_binary_v =std::is_same_v, snapshot::binary>; + + template + static inline constexpr bool is_json_snapshot_v =std::is_same_v, snapshot::json_snapshot>; } // ns eosio::testing::snapshot template @@ -38,8 +43,10 @@ namespace eosio { static inline constexpr auto file_suffix() { if constexpr (snapshot::is_json_v) return ".json.gz"; - else + else if constexpr (snapshot::is_binary_v) return ".bin.gz"; + else + return ".bin.json.gz"; } std::string file_name; @@ -67,8 +74,10 @@ namespace eosio { auto read() const { if constexpr (snapshot::is_json_v) { return fc::json::from_string(read_as_string()); + } else if constexpr (snapshot::is_binary_v) { + return fc::json::from_string(read_as_string()); } else { - static_assert(snapshot::is_binary_v, "unsupported type"); + static_assert(snapshot::is_json_snapshot_v, "unsupported type"); return fc::json::from_string(read_as_string()); } } @@ -85,8 +94,12 @@ namespace eosio { if constexpr (snapshot::is_json_v) { out_string = fc::json::to_string(snapshot, fc::time_point::maximum()); + } else if constexpr (snapshot::is_binary_v) { + std::ostringstream out_stream; + out_stream.write(snapshot.data(), snapshot.size()); + out_string = out_stream.str(); } else { - static_assert(snapshot::is_binary_v, "unsupported type"); + static_assert(snapshot::is_json_snapshot_v, "unsupported type"); std::ostringstream out_stream; out_stream.write(snapshot.data(), snapshot.size()); out_string = out_stream.str(); diff --git a/unittests/snapshots/CMakeLists.txt b/unittests/snapshots/CMakeLists.txt index 26912f3ff7..5b085b16b1 100644 --- a/unittests/snapshots/CMakeLists.txt +++ b/unittests/snapshots/CMakeLists.txt @@ -1,14 +1,20 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/blocks.log ${CMAKE_CURRENT_BINARY_DIR}/blocks.log COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v2.bin.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v2.bin.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v2.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v2.json.gz COPYONLY ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v2.bin.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v2.bin.json.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/prod_sched/blocks.log ${CMAKE_CURRENT_BINARY_DIR}/prod_sched/blocks.log COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v2_prod_sched.bin.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v2_prod_sched.bin.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v2_prod_sched.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v2_prod_sched.json.gz COPYONLY ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v2_prod_sched.bin.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v2_prod_sched.bin.json.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v3.bin.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v3.bin.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v3.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v3.json.gz COPYONLY ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v3.bin.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v3.bin.json.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v4.bin.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v4.bin.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v4.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v4.json.gz COPYONLY ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v4.bin.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v4.bin.json.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v5.bin.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v5.bin.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v5.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v5.json.gz COPYONLY ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v5.bin.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v5.bin.json.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v6.bin.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v6.bin.gz COPYONLY ) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v6.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v6.json.gz COPYONLY ) +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/snap_v6.bin.json.gz ${CMAKE_CURRENT_BINARY_DIR}/snap_v6.bin.json.gz COPYONLY ) diff --git a/unittests/snapshots/snap_v2.bin.json.gz b/unittests/snapshots/snap_v2.bin.json.gz new file mode 100644 index 0000000000..26dc275e41 Binary files /dev/null and b/unittests/snapshots/snap_v2.bin.json.gz differ diff --git a/unittests/snapshots/snap_v2_prod_sched.bin.json.gz b/unittests/snapshots/snap_v2_prod_sched.bin.json.gz new file mode 100644 index 0000000000..caf2524c66 Binary files /dev/null and b/unittests/snapshots/snap_v2_prod_sched.bin.json.gz differ diff --git a/unittests/snapshots/snap_v3.bin.json.gz b/unittests/snapshots/snap_v3.bin.json.gz new file mode 100644 index 0000000000..9be784ea07 Binary files /dev/null and b/unittests/snapshots/snap_v3.bin.json.gz differ diff --git a/unittests/snapshots/snap_v4.bin.json.gz b/unittests/snapshots/snap_v4.bin.json.gz new file mode 100644 index 0000000000..bb21c1ec57 Binary files /dev/null and b/unittests/snapshots/snap_v4.bin.json.gz differ diff --git a/unittests/snapshots/snap_v5.bin.json.gz b/unittests/snapshots/snap_v5.bin.json.gz new file mode 100644 index 0000000000..2264879c2f Binary files /dev/null and b/unittests/snapshots/snap_v5.bin.json.gz differ diff --git a/unittests/snapshots/snap_v6.bin.json.gz b/unittests/snapshots/snap_v6.bin.json.gz new file mode 100644 index 0000000000..b513f97b1e Binary files /dev/null and b/unittests/snapshots/snap_v6.bin.json.gz differ