diff --git a/libraries/state_history/include/eosio/state_history/types.hpp b/libraries/state_history/include/eosio/state_history/types.hpp index 038f039184..a5f74de9ef 100644 --- a/libraries/state_history/include/eosio/state_history/types.hpp +++ b/libraries/state_history/include/eosio/state_history/types.hpp @@ -127,4 +127,6 @@ FC_REFLECT_EMPTY(eosio::state_history::get_status_request_v0); FC_REFLECT(eosio::state_history::get_status_result_v0, (head)(last_irreversible)(trace_begin_block)(trace_end_block)(chain_state_begin_block)(chain_state_end_block)(chain_id)); FC_REFLECT(eosio::state_history::get_blocks_request_v0, (start_block_num)(end_block_num)(max_messages_in_flight)(have_positions)(irreversible_only)(fetch_block)(fetch_traces)(fetch_deltas)); FC_REFLECT(eosio::state_history::get_blocks_ack_request_v0, (num_messages)); +FC_REFLECT(eosio::state_history::get_blocks_result_base, (head)(last_irreversible)(this_block)(prev_block)(block)); +FC_REFLECT_DERIVED(eosio::state_history::get_blocks_result_v0, (eosio::state_history::get_blocks_result_base), (traces)(deltas)); // clang-format on diff --git a/plugins/state_history_plugin/state_history_plugin.cpp b/plugins/state_history_plugin/state_history_plugin.cpp index ea2ed32d1b..c00c06c61c 100644 --- a/plugins/state_history_plugin/state_history_plugin.cpp +++ b/plugins/state_history_plugin/state_history_plugin.cpp @@ -164,11 +164,6 @@ struct state_history_plugin_impl : std::enable_shared_from_this - void post_task_main_thread_medium(Task&& task) { - app().post(priority::medium, std::forward(task)); - } - void listen() { boost::system::error_code ec; diff --git a/plugins/state_history_plugin/tests/session_test.cpp b/plugins/state_history_plugin/tests/session_test.cpp index 3069ca5660..abe08a7732 100644 --- a/plugins/state_history_plugin/tests/session_test.cpp +++ b/plugins/state_history_plugin/tests/session_test.cpp @@ -80,9 +80,13 @@ fc::datastream& operator>>(fc::datastream& ds, eosio::state_history::get //------------------------------------------------------------------------------ -fc::sha256 block_id_for(const uint32_t bnum) { - fc::sha256 m = fc::sha256::hash(fc::sha256::hash(std::to_string(bnum))); +std::unordered_map block_ids; +fc::sha256 block_id_for(const uint32_t bnum, const std::string& nonce = {}) { + if (auto it = block_ids.find(bnum); it != block_ids.end()) + return it->second; + fc::sha256 m = fc::sha256::hash(fc::sha256::hash(std::to_string(bnum)+nonce)); m._hash[0] = fc::endian_reverse_u32(bnum); + block_ids[bnum] = m; return m; } @@ -134,11 +138,6 @@ struct mock_state_history_plugin { session_mgr.insert(std::move(s)); } - template - void post_task_main_thread_medium(Task&& task) { - boost::asio::post(main_ioc, std::forward(task)); - } - }; using session_type = eosio::session; @@ -273,6 +272,9 @@ struct state_history_test_fixture { state_history_test_fixture() : ws(ioc) { + // mock uses DEFAULT_LOGGER + fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); + // start the server with 2 threads server.run(); connect_to(server.local_address); @@ -318,7 +320,7 @@ struct state_history_test_fixture { fc::raw::unpack(ds, result); } - void verify_statue(const eosio::state_history::get_status_result_v0& status) { + void verify_status(const eosio::state_history::get_status_result_v0& status) { send_status_request(); eosio::state_history::state_result result; @@ -447,7 +449,7 @@ BOOST_FIXTURE_TEST_CASE(test_session_no_prune, state_history_test_fixture) { add_to_log(3, 1, generate_data(n)); // format to encode decompressed size to avoid decompress entire data upfront. // send a get_status_request and verify the result is what we expected - verify_statue(eosio::state_history::get_status_result_v0{ + verify_status(eosio::state_history::get_status_result_v0{ .head = {head_block_num, block_id_for(head_block_num)}, .last_irreversible = {head_block_num, block_id_for(head_block_num)}, .trace_begin_block = 1, @@ -504,7 +506,7 @@ BOOST_FIXTURE_TEST_CASE(test_session_with_prune, state_history_test_fixture) { add_to_log(3, 1, generate_data(n)); // format to encode decompressed size to avoid decompress entire data upfront. // send a get_status_request and verify the result is what we expected - verify_statue(eosio::state_history::get_status_result_v0{ + verify_status(eosio::state_history::get_status_result_v0{ .head = {head_block_num, block_id_for(head_block_num)}, .last_irreversible = {head_block_num, block_id_for(head_block_num)}, .trace_begin_block = 2, @@ -552,3 +554,114 @@ BOOST_FIXTURE_TEST_CASE(test_session_with_prune, state_history_test_fixture) { } FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE(test_session_fork, state_history_test_fixture) { + try { + // setup block head for the server + server.setup_state_history_log(); + uint32_t head_block_num = 4; + server.block_head = {head_block_num, block_id_for(head_block_num)}; + + // generate the log data used for traces and deltas + uint32_t n = mock_state_history_plugin::default_frame_size; + add_to_log(1, n * sizeof(uint32_t), generate_data(n)); // original data format + add_to_log(2, 0, generate_data(n)); // format to accommodate the compressed size greater than 4GB + add_to_log(3, 1, generate_data(n)); // format to encode decompressed size to avoid decompress entire data upfront. + add_to_log(4, 1, generate_data(n)); // format to encode decompressed size to avoid decompress entire data upfront. + + // send a get_status_request and verify the result is what we expected + verify_status(eosio::state_history::get_status_result_v0{ + .head = {head_block_num, block_id_for(head_block_num)}, + .last_irreversible = {head_block_num, block_id_for(head_block_num)}, + .trace_begin_block = 1, + .trace_end_block = head_block_num + 1, + .chain_state_begin_block = 1, + .chain_state_end_block = head_block_num + 1}); + + // send a get_blocks_request to server + send_request(eosio::state_history::get_blocks_request_v0{ + .start_block_num = 1, + .end_block_num = UINT32_MAX, + .max_messages_in_flight = UINT32_MAX, + .have_positions = {}, + .irreversible_only = false, + .fetch_block = true, + .fetch_traces = true, + .fetch_deltas = true}); + + std::vector have_positions; + eosio::state_history::state_result result; + // we should get 4 consecutive block result + for (int i = 0; i < 4; ++i) { + receive_result(result); + BOOST_REQUIRE(std::holds_alternative(result)); + auto r = std::get(result); + BOOST_REQUIRE_EQUAL(r.head.block_num, server.block_head.block_num); + BOOST_REQUIRE(r.traces.has_value()); + BOOST_REQUIRE(r.deltas.has_value()); + auto traces = r.traces.value(); + auto deltas = r.deltas.value(); + auto& data = written_data[i]; + auto data_size = data.size() * sizeof(int32_t); + BOOST_REQUIRE_EQUAL(traces.size(), data_size); + BOOST_REQUIRE_EQUAL(deltas.size(), data_size); + BOOST_REQUIRE(r.this_block.has_value()); + BOOST_REQUIRE_EQUAL(r.this_block->block_num, i+1); + have_positions.push_back(*r.this_block); + + BOOST_REQUIRE(std::equal(traces.begin(), traces.end(), (const char*)data.data())); + BOOST_REQUIRE(std::equal(deltas.begin(), deltas.end(), (const char*)data.data())); + } + + // generate a fork that includes blocks 3,4 and verify new data retrieved + block_ids.extract(3); block_id_for(3, "fork"); + block_ids.extract(4); block_id_for(4, "fork"); + server.block_head = {head_block_num, block_id_for(head_block_num)}; + add_to_log(3, 0, generate_data(n)); + add_to_log(4, 1, generate_data(n)); + + // send a get_status_request and verify the result is what we expected + verify_status(eosio::state_history::get_status_result_v0{ + .head = {head_block_num, block_id_for(head_block_num)}, + .last_irreversible = {head_block_num, block_id_for(head_block_num)}, + .trace_begin_block = 1, + .trace_end_block = head_block_num + 1, + .chain_state_begin_block = 1, + .chain_state_end_block = head_block_num + 1}); + + // send a get_blocks_request to server starting at 5, will send 3,4 because of fork + send_request(eosio::state_history::get_blocks_request_v0{ + .start_block_num = 5, + .end_block_num = UINT32_MAX, + .max_messages_in_flight = UINT32_MAX, + .have_positions = std::move(have_positions), + .irreversible_only = false, + .fetch_block = true, + .fetch_traces = true, + .fetch_deltas = true}); + + eosio::state_history::state_result fork_result; + // we should now get data for fork 3,4 + for (int i = 2; i < 4; ++i) { + receive_result(fork_result); + BOOST_REQUIRE(std::holds_alternative(fork_result)); + auto r = std::get(fork_result); + BOOST_REQUIRE_EQUAL(r.head.block_num, server.block_head.block_num); + BOOST_REQUIRE(r.this_block.has_value()); + BOOST_REQUIRE_EQUAL(r.this_block->block_num, i+1); + BOOST_REQUIRE(r.traces.has_value()); + BOOST_REQUIRE(r.deltas.has_value()); + auto traces = r.traces.value(); + auto deltas = r.deltas.value(); + auto& data = written_data[i]; + auto data_size = data.size() * sizeof(int32_t); + BOOST_REQUIRE_EQUAL(traces.size(), data_size); + BOOST_REQUIRE_EQUAL(deltas.size(), data_size); + + BOOST_REQUIRE(std::equal(traces.begin(), traces.end(), (const char*)data.data())); + BOOST_REQUIRE(std::equal(deltas.begin(), deltas.end(), (const char*)data.data())); + } + } + FC_LOG_AND_RETHROW() +} +