diff --git a/plugins/producer_plugin/CMakeLists.txt b/plugins/producer_plugin/CMakeLists.txt index e9dbe40a38..a64518f7c9 100644 --- a/plugins/producer_plugin/CMakeLists.txt +++ b/plugins/producer_plugin/CMakeLists.txt @@ -2,6 +2,7 @@ file(GLOB HEADERS "include/eosio/producer_plugin/*.hpp") add_library( producer_plugin producer_plugin.cpp + pending_snapshot.cpp ${HEADERS} ) @@ -10,4 +11,3 @@ target_include_directories( producer_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../chain_interface/include" ) add_subdirectory( test ) - diff --git a/plugins/producer_plugin/include/eosio/producer_plugin/pending_snapshot.hpp b/plugins/producer_plugin/include/eosio/producer_plugin/pending_snapshot.hpp new file mode 100644 index 0000000000..02814ca824 --- /dev/null +++ b/plugins/producer_plugin/include/eosio/producer_plugin/pending_snapshot.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace eosio { + +class pending_snapshot { +public: + using next_t = producer_plugin::next_function; + + pending_snapshot(const chain::block_id_type& block_id, next_t& next, std::string pending_path, std::string final_path) + : block_id(block_id) + , next(next) + , pending_path(pending_path) + , final_path(final_path) + {} + + uint32_t get_height() const { + return chain::block_header::num_from_id(block_id); + } + + static bfs::path get_final_path(const chain::block_id_type& block_id, const bfs::path& snapshots_dir) { + return snapshots_dir / fc::format_string("snapshot-${id}.bin", fc::mutable_variant_object()("id", block_id)); + } + + static bfs::path get_pending_path(const chain::block_id_type& block_id, const bfs::path& snapshots_dir) { + return snapshots_dir / fc::format_string(".pending-snapshot-${id}.bin", fc::mutable_variant_object()("id", block_id)); + } + + static bfs::path get_temp_path(const chain::block_id_type& block_id, const bfs::path& snapshots_dir) { + return snapshots_dir / fc::format_string(".incomplete-snapshot-${id}.bin", fc::mutable_variant_object()("id", block_id)); + } + + producer_plugin::snapshot_information finalize( const chain::controller& chain ) const; + + chain::block_id_type block_id; + next_t next; + std::string pending_path; + std::string final_path; +}; + +} // namespace eosio diff --git a/plugins/producer_plugin/pending_snapshot.cpp b/plugins/producer_plugin/pending_snapshot.cpp new file mode 100644 index 0000000000..cd94291c0d --- /dev/null +++ b/plugins/producer_plugin/pending_snapshot.cpp @@ -0,0 +1,28 @@ +#include +#include + +namespace eosio { + +producer_plugin::snapshot_information pending_snapshot::finalize( const chain::controller& chain ) const { + auto block_ptr = chain.fetch_block_by_id( block_id ); + auto in_chain = (bool)block_ptr; + boost::system::error_code ec; + + if (!in_chain) { + bfs::remove(bfs::path(pending_path), ec); + EOS_THROW(chain::snapshot_finalization_exception, + "Snapshotted block was forked out of the chain. ID: ${block_id}", + ("block_id", block_id)); + } + + bfs::rename(bfs::path(pending_path), bfs::path(final_path), ec); + EOS_ASSERT(!ec, chain::snapshot_finalization_exception, + "Unable to finalize valid snapshot of block number ${bn}: [code: ${ec}] ${message}", + ("bn", get_height()) + ("ec", ec.value()) + ("message", ec.message())); + + return {block_id, block_ptr->block_num(), block_ptr->timestamp, chain::chain_snapshot_header::current_version, final_path}; +} + +} // namespace eosio diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 77758da3e6..c6d7e1a4ea 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -116,61 +117,6 @@ using transaction_id_with_expiry_index = multi_index_container< struct by_height; -class pending_snapshot { -public: - using next_t = producer_plugin::next_function; - - pending_snapshot(const block_id_type& block_id, next_t& next, std::string pending_path, std::string final_path) - : block_id(block_id) - , next(next) - , pending_path(pending_path) - , final_path(final_path) - {} - - uint32_t get_height() const { - return block_header::num_from_id(block_id); - } - - static bfs::path get_final_path(const block_id_type& block_id, const bfs::path& snapshots_dir) { - return snapshots_dir / fc::format_string("snapshot-${id}.bin", fc::mutable_variant_object()("id", block_id)); - } - - static bfs::path get_pending_path(const block_id_type& block_id, const bfs::path& snapshots_dir) { - return snapshots_dir / fc::format_string(".pending-snapshot-${id}.bin", fc::mutable_variant_object()("id", block_id)); - } - - static bfs::path get_temp_path(const block_id_type& block_id, const bfs::path& snapshots_dir) { - return snapshots_dir / fc::format_string(".incomplete-snapshot-${id}.bin", fc::mutable_variant_object()("id", block_id)); - } - - producer_plugin::snapshot_information finalize( const chain::controller& chain ) const { - auto block_ptr = chain.fetch_block_by_id( block_id ); - auto in_chain = (bool)block_ptr; - boost::system::error_code ec; - - if (!in_chain) { - bfs::remove(bfs::path(pending_path), ec); - EOS_THROW(snapshot_finalization_exception, - "Snapshotted block was forked out of the chain. ID: ${block_id}", - ("block_id", block_id)); - } - - bfs::rename(bfs::path(pending_path), bfs::path(final_path), ec); - EOS_ASSERT(!ec, snapshot_finalization_exception, - "Unable to finalize valid snapshot of block number ${bn}: [code: ${ec}] ${message}", - ("bn", get_height()) - ("ec", ec.value()) - ("message", ec.message())); - - return {block_id, block_ptr->block_num(), block_ptr->timestamp, chain_snapshot_header::current_version, final_path}; - } - - block_id_type block_id; - next_t next; - std::string pending_path; - std::string final_path; -}; - using pending_snapshot_index = multi_index_container< pending_snapshot, indexed_by< diff --git a/plugins/producer_plugin/test/CMakeLists.txt b/plugins/producer_plugin/test/CMakeLists.txt index 5eab584c90..a5dc01df96 100644 --- a/plugins/producer_plugin/test/CMakeLists.txt +++ b/plugins/producer_plugin/test/CMakeLists.txt @@ -7,3 +7,8 @@ add_executable( test_trx_full test_trx_full.cpp ) target_link_libraries( test_trx_full producer_plugin eosio_testing ) add_test(NAME test_trx_full COMMAND plugins/producer_plugin/test/test_trx_full WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +add_executable( test_snapshot_information test_snapshot_information.cpp ) +target_link_libraries( test_snapshot_information producer_plugin eosio_testing ) + +add_test(NAME test_snapshot_information COMMAND plugins/producer_plugin/test/test_snapshot_information WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/plugins/producer_plugin/test/test_snapshot_information.cpp b/plugins/producer_plugin/test/test_snapshot_information.cpp new file mode 100644 index 0000000000..599b561ba4 --- /dev/null +++ b/plugins/producer_plugin/test/test_snapshot_information.cpp @@ -0,0 +1,71 @@ +#define BOOST_TEST_MODULE snapshot_information +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace eosio; +using namespace eosio::testing; +using namespace boost::system; + +namespace { + eosio::producer_plugin::snapshot_information test_snap_info; +} + +BOOST_AUTO_TEST_SUITE(snapshot_tests) + +using next_t = eosio::producer_plugin::next_function; + +BOOST_AUTO_TEST_CASE_TEMPLATE(test_snapshot_information, SNAPSHOT_SUITE, snapshot_suites) { + tester chain; + const chainbase::bfs::path parent_path = chain.get_config().blocks_dir.parent_path(); + + chain.create_account("snapshot"_n); + chain.produce_blocks(1); + chain.set_code("snapshot"_n, contracts::snapshot_test_wasm()); + chain.set_abi("snapshot"_n, contracts::snapshot_test_abi().data()); + chain.produce_blocks(1); + + auto block = chain.produce_block(); + BOOST_REQUIRE_EQUAL(block->block_num(), 6); // ensure that test setup stays consistent with original snapshot setup + // undo the auto-pending from tester + chain.control->abort_block(); + + auto block2 = chain.produce_block(); + BOOST_REQUIRE_EQUAL(block2->block_num(), 7); // ensure that test setup stays consistent with original snapshot setup + // undo the auto-pending from tester + chain.control->abort_block(); + + // write snapshot + auto write_snapshot = [&]( const bfs::path& p ) -> void { + if ( !bfs::exists( p.parent_path() ) ) + bfs::create_directory( p.parent_path() ); + + // create the snapshot + auto snap_out = std::ofstream(p.generic_string(), (std::ios::out | std::ios::binary)); + auto writer = std::make_shared(snap_out); + (*chain.control).write_snapshot(writer); + writer->finalize(); + snap_out.flush(); + snap_out.close(); + }; + + auto final_path = eosio::pending_snapshot::get_final_path(block2->previous, "../snapshots/"); + auto pending_path = eosio::pending_snapshot::get_pending_path(block2->previous, "../snapshots/"); + + write_snapshot( pending_path ); + next_t next; + eosio::pending_snapshot pending{ block2->previous, next, pending_path.generic_string(), final_path.generic_string() }; + test_snap_info = pending.finalize(*chain.control); + BOOST_REQUIRE_EQUAL(test_snap_info.head_block_num, 6); + BOOST_REQUIRE_EQUAL(test_snap_info.version, chain_snapshot_header::current_version); +} + +BOOST_AUTO_TEST_SUITE_END()