From bbeafe9effcf2058687d7436735bf7beba566469 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 2 May 2023 11:40:43 -0500 Subject: [PATCH 01/32] GH-1062 Remove deadline from trace_api_plugin --- .../include/eosio/chain/abi_serializer.hpp | 9 +- plugins/trace_api_plugin/abi_data_handler.cpp | 6 +- .../eosio/trace_api/abi_data_handler.hpp | 9 +- .../eosio/trace_api/request_handler.hpp | 24 +-- plugins/trace_api_plugin/request_handler.cpp | 34 ++-- .../test/test_data_handlers.cpp | 18 +- .../trace_api_plugin/test/test_responses.cpp | 176 +++--------------- plugins/trace_api_plugin/trace_api_plugin.cpp | 61 +++--- 8 files changed, 94 insertions(+), 243 deletions(-) diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index eef8795d29..98406e59c5 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -120,7 +120,7 @@ struct abi_serializer { static constexpr size_t max_recursion_depth = 32; // arbitrary depth to prevent infinite recursion // create standard yield function that checks for max_serialization_time and max_recursion_depth. - // now() deadline caputered at time of this call + // now() deadline captured at time of this call static yield_function_t create_yield_function(const fc::microseconds& max_serialization_time) { fc::time_point deadline = fc::time_point::now(); if( max_serialization_time > fc::microseconds::maximum() - deadline.time_since_epoch() ) { @@ -128,12 +128,13 @@ struct abi_serializer { } else { deadline += max_serialization_time; } - return [max_serialization_time, deadline](size_t recursion_depth) { + return [max_serialization_time, last_call=deadline](size_t recursion_depth) mutable { EOS_ASSERT( recursion_depth < max_recursion_depth, abi_recursion_depth_exception, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) ); - EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, + EOS_ASSERT( (fc::time_point::now() - last_call) < max_serialization_time, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) ); + last_call = fc::time_point::now(); }; } @@ -495,7 +496,7 @@ namespace impl { _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place mvo( "data", abi._binary_to_variant( type, act.data, _ctx )); } catch(...) { - // any failure to serialize data, then leave as not serailzed + // any failure to serialize data, then leave as not serialized set_hex_data(mvo, "data", act.data); } } else { diff --git a/plugins/trace_api_plugin/abi_data_handler.cpp b/plugins/trace_api_plugin/abi_data_handler.cpp index b0c8b46c49..574eb9919b 100644 --- a/plugins/trace_api_plugin/abi_data_handler.cpp +++ b/plugins/trace_api_plugin/abi_data_handler.cpp @@ -9,7 +9,7 @@ namespace eosio::trace_api { std::make_shared(std::move(abi), chain::abi_serializer::create_yield_function(fc::microseconds::maximum()))); } - std::tuple> abi_data_handler::serialize_to_variant(const std::variant & action, const yield_function& yield ) { + std::tuple> abi_data_handler::serialize_to_variant(const std::variant& action) { auto account = std::visit([](auto &&action) -> auto { return action.account; }, action); if (abi_serializer_by_account.count(account) > 0) { @@ -20,8 +20,8 @@ namespace eosio::trace_api { if (!type_name.empty()) { try { // abi_serializer expects a yield function that takes a recursion depth - auto abi_yield = [yield](size_t recursion_depth) { - yield(); + // abis are user provided, do not use a deadline + auto abi_yield = [](size_t recursion_depth) { EOS_ASSERT( recursion_depth < chain::abi_serializer::max_recursion_depth, chain::abi_recursion_depth_exception, "exceeded max_recursion_depth ${r} ", ("r", chain::abi_serializer::max_recursion_depth) ); }; diff --git a/plugins/trace_api_plugin/include/eosio/trace_api/abi_data_handler.hpp b/plugins/trace_api_plugin/include/eosio/trace_api/abi_data_handler.hpp index 7ff5729786..4676a0b784 100644 --- a/plugins/trace_api_plugin/include/eosio/trace_api/abi_data_handler.hpp +++ b/plugins/trace_api_plugin/include/eosio/trace_api/abi_data_handler.hpp @@ -34,13 +34,12 @@ namespace eosio { * Given an action trace, produce a tuple representing the `data` and `return_value` fields in the trace * * @param action - trace of the action including metadata necessary for finding the ABI - * @param yield - a yield function to allow cooperation during long running tasks * @return tuple where the first element is a variant representing the `data` field of the action interpreted by known ABIs OR an empty variant, and the second element represents the `return_value` field of the trace. */ - std::tuple> serialize_to_variant(const std::variant & action, const yield_function& yield ); + std::tuple> serialize_to_variant(const std::variant & action); /** - * Utility class that allows mulitple request_handlers to share the same abi_data_handler + * Utility class that allows multiple request_handlers to share the same abi_data_handler */ class shared_provider { public: @@ -48,8 +47,8 @@ namespace eosio { :handler(handler) {} - std::tuple> serialize_to_variant( const std::variant & action, const yield_function& yield ) { - return handler->serialize_to_variant(action, yield); + std::tuple> serialize_to_variant( const std::variant & action ) { + return handler->serialize_to_variant(action); } std::shared_ptr handler; diff --git a/plugins/trace_api_plugin/include/eosio/trace_api/request_handler.hpp b/plugins/trace_api_plugin/include/eosio/trace_api/request_handler.hpp index cbc24e8c44..e31aa433e2 100644 --- a/plugins/trace_api_plugin/include/eosio/trace_api/request_handler.hpp +++ b/plugins/trace_api_plugin/include/eosio/trace_api/request_handler.hpp @@ -6,12 +6,12 @@ #include namespace eosio::trace_api { - using data_handler_function = std::function>( const std::variant & action_trace_t, const yield_function&)>; + using data_handler_function = std::function>( const std::variant & action_trace_t)>; namespace detail { class response_formatter { public: - static fc::variant process_block( const data_log_entry& trace, bool irreversible, const data_handler_function& data_handler, const yield_function& yield ); + static fc::variant process_block( const data_log_entry& trace, bool irreversible, const data_handler_function& data_handler ); }; } @@ -31,28 +31,24 @@ namespace eosio::trace_api { * (eg JSON) * * @param block_height - the height of the block whose trace is requested - * @param yield - a yield function to allow cooperation during long running tasks * @return a properly formatted variant representing the trace for the given block height if it exists, an * empty variant otherwise. - * @throws yield_exception if a call to `yield` throws. * @throws bad_data_exception when there are issues with the underlying data preventing processing. */ - fc::variant get_block_trace( uint32_t block_height, const yield_function& yield = {}) { - auto data = logfile_provider.get_block(block_height, yield); + fc::variant get_block_trace( uint32_t block_height ) { + auto data = logfile_provider.get_block(block_height); if (!data) { _log("No block found at block height " + std::to_string(block_height) ); return {}; } - yield(); - - auto data_handler = [this](const auto& action, const yield_function& yield) -> std::tuple> { + auto data_handler = [this](const auto& action) -> std::tuple> { return std::visit([&](const auto& action_trace_t) { - return data_handler_provider.serialize_to_variant(action_trace_t, yield); + return data_handler_provider.serialize_to_variant(action_trace_t); }, action); }; - return detail::response_formatter::process_block(std::get<0>(*data), std::get<1>(*data), data_handler, yield); + return detail::response_formatter::process_block(std::get<0>(*data), std::get<1>(*data), data_handler); } /** @@ -61,17 +57,15 @@ namespace eosio::trace_api { * * @param trxid - the transaction id whose trace is requested * @param block_height - the height of the block whose trace contains requested transaction trace - * @param yield - a yield function to allow cooperation during long running tasks * @return a properly formatted variant representing the trace for the given transaction id if it exists, an * empty variant otherwise. - * @throws yield_exception if a call to `yield` throws. * @throws bad_data_exception when there are issues with the underlying data preventing processing. */ - fc::variant get_transaction_trace(chain::transaction_id_type trxid, uint32_t block_height, const yield_function& yield = {}){ + fc::variant get_transaction_trace(chain::transaction_id_type trxid, uint32_t block_height){ _log("get_transaction_trace called" ); fc::variant result = {}; // extract the transaction trace from the block trace - auto resp = get_block_trace(block_height, yield); + auto resp = get_block_trace(block_height); if (!resp.is_null()) { auto& b_mvo = resp.get_object(); if (b_mvo.contains("transactions")) { diff --git a/plugins/trace_api_plugin/request_handler.cpp b/plugins/trace_api_plugin/request_handler.cpp index 20755cf79e..c1ebdffc17 100644 --- a/plugins/trace_api_plugin/request_handler.cpp +++ b/plugins/trace_api_plugin/request_handler.cpp @@ -11,12 +11,10 @@ namespace { return (std::string)t + "Z"; } - fc::variants process_authorizations(const std::vector& authorizations, const yield_function& yield ) { + fc::variants process_authorizations(const std::vector& authorizations) { fc::variants result; result.reserve(authorizations.size()); for ( const auto& a: authorizations) { - yield(); - result.emplace_back(fc::mutable_variant_object() ("account", a.account.to_string()) ("permission", a.permission.to_string()) @@ -28,7 +26,7 @@ namespace { } template - fc::variants process_actions(const std::vector& actions, const data_handler_function & data_handler, const yield_function& yield ) { + fc::variants process_actions(const std::vector& actions, const data_handler_function & data_handler) { fc::variants result; result.reserve(actions.size()); // create a vector of indices to sort based on actions to avoid copies @@ -38,8 +36,6 @@ namespace { return actions.at(lhs).global_sequence < actions.at(rhs).global_sequence; }); for ( int index : indices) { - yield(); - const auto& a = actions.at(index); auto common_mvo = fc::mutable_variant_object(); @@ -47,13 +43,13 @@ namespace { ("receiver", a.receiver.to_string()) ("account", a.account.to_string()) ("action", a.action.to_string()) - ("authorization", process_authorizations(a.authorization, yield)) + ("authorization", process_authorizations(a.authorization)) ("data", fc::to_hex(a.data.data(), a.data.size())); auto action_variant = fc::mutable_variant_object(); if constexpr(std::is_same_v){ action_variant(std::move(common_mvo)); - auto [params, return_data] = data_handler(a, yield); + auto [params, return_data] = data_handler(a); if (!params.is_null()) { action_variant("params", params); } @@ -61,7 +57,7 @@ namespace { else if constexpr(std::is_same_v){ action_variant(std::move(common_mvo)); action_variant("return_value", fc::to_hex(a.return_value.data(),a.return_value.size())) ; - auto [params, return_data] = data_handler(a, yield); + auto [params, return_data] = data_handler(a); if (!params.is_null()) { action_variant("params", params); } @@ -75,17 +71,15 @@ namespace { } template - fc::variants process_transactions(const std::vector& transactions, const data_handler_function& data_handler, const yield_function& yield ) { + fc::variants process_transactions(const std::vector& transactions, const data_handler_function& data_handler) { fc::variants result; result.reserve(transactions.size()); for ( const auto& t: transactions) { - yield(); - if constexpr(std::is_same_v){ result.emplace_back( fc::mutable_variant_object() ("id", t.id.str()) - ("actions", process_actions(t.actions, data_handler, yield))); + ("actions", process_actions(t.actions, data_handler))); } else { auto common_mvo = fc::mutable_variant_object(); common_mvo("status", t.status) @@ -97,14 +91,14 @@ namespace { result.emplace_back( fc::mutable_variant_object() ("id", t.id.str()) - ("actions", process_actions(t.actions, data_handler, yield)) + ("actions", process_actions(t.actions, data_handler)) (std::move(common_mvo))); } else if constexpr(std::is_same_v){ result.emplace_back( fc::mutable_variant_object() ("id", t.id.str()) - ("actions", process_actions(std::get>(t.actions), data_handler, yield)) + ("actions", process_actions(std::get>(t.actions), data_handler)) (std::move(common_mvo))); } else if constexpr(std::is_same_v){ @@ -114,7 +108,7 @@ namespace { ("block_num", t.block_num) ("block_time", t.block_time) ("producer_block_id", t.producer_block_id) - ("actions", process_actions(std::get>(t.actions), data_handler, yield)) + ("actions", process_actions(std::get>(t.actions), data_handler)) (std::move(common_mvo)) ); } @@ -126,7 +120,7 @@ namespace { } namespace eosio::trace_api::detail { - fc::variant response_formatter::process_block( const data_log_entry& trace, bool irreversible, const data_handler_function& data_handler, const yield_function& yield ) { + fc::variant response_formatter::process_block( const data_log_entry& trace, bool irreversible, const data_handler_function& data_handler ) { auto common_mvo = std::visit([&](auto&& arg) -> fc::mutable_variant_object { return fc::mutable_variant_object() ("id", arg.id.str()) @@ -139,7 +133,7 @@ namespace eosio::trace_api::detail { auto& block_trace = std::get(trace); return fc::mutable_variant_object() (std::move(common_mvo)) - ("transactions", process_transactions(block_trace.transactions, data_handler, yield )); + ("transactions", process_transactions(block_trace.transactions, data_handler )); }else if(std::holds_alternative(trace)){ auto& block_trace = std::get(trace); return fc::mutable_variant_object() @@ -147,11 +141,11 @@ namespace eosio::trace_api::detail { ("transaction_mroot", block_trace.transaction_mroot) ("action_mroot", block_trace.action_mroot) ("schedule_version", block_trace.schedule_version) - ("transactions", process_transactions( block_trace.transactions_v1, data_handler, yield )) ; + ("transactions", process_transactions( block_trace.transactions_v1, data_handler )) ; }else if(std::holds_alternative(trace)){ auto& block_trace = std::get(trace); auto transactions = std::visit([&](auto&& arg){ - return process_transactions(arg, data_handler, yield); + return process_transactions(arg, data_handler); }, block_trace.transactions); return fc::mutable_variant_object() (std::move(common_mvo)) diff --git a/plugins/trace_api_plugin/test/test_data_handlers.cpp b/plugins/trace_api_plugin/test/test_data_handlers.cpp index 94efbb74b7..464b3a81c1 100644 --- a/plugins/trace_api_plugin/test/test_data_handlers.cpp +++ b/plugins/trace_api_plugin/test/test_data_handlers.cpp @@ -18,7 +18,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) abi_data_handler handler(exception_handler{}); auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -33,7 +33,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) abi_data_handler handler(exception_handler{}); auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) abi_data_handler handler(exception_handler{}); auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) abi_data_handler handler(exception_handler{}); auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) ("c", 2) ("d", 3); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) ("c", 2) ("d", 3); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -158,7 +158,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); } @@ -217,7 +217,7 @@ BOOST_AUTO_TEST_SUITE(abi_data_handler_tests) auto expected = fc::variant(); - auto actual = handler.serialize_to_variant(action_trace_t, [](){}); + auto actual = handler.serialize_to_variant(action_trace_t); BOOST_TEST(to_kv(expected) == to_kv(std::get<0>(actual)), boost::test_tools::per_element()); BOOST_TEST(log_called); diff --git a/plugins/trace_api_plugin/test/test_responses.cpp b/plugins/trace_api_plugin/test/test_responses.cpp index 83f2b4c7d1..aa9c32012a 100644 --- a/plugins/trace_api_plugin/test/test_responses.cpp +++ b/plugins/trace_api_plugin/test/test_responses.cpp @@ -25,17 +25,17 @@ struct response_test_fixture { * optional containing a 2-tuple of the block_trace and a flag indicating irreversibility * @throws bad_data_exception : if the data is corrupt in some way */ - get_block_t get_block(uint32_t height, const yield_function& yield= {}) { - return fixture.mock_get_block(height, yield); + get_block_t get_block(uint32_t height) { + return fixture.mock_get_block(height); } response_test_fixture& fixture; }; - constexpr static auto default_mock_data_handler_v0 = [](const action_trace_v0& a, const yield_function&) ->std::tuple> { + constexpr static auto default_mock_data_handler_v0 = [](const action_trace_v0& a) ->std::tuple> { return {fc::mutable_variant_object()("hex" , fc::to_hex(a.data.data(), a.data.size())),{}}; }; - constexpr static auto default_mock_data_handler_v1 = [](const action_trace_v1& a, const yield_function&) -> std::tuple>{ + constexpr static auto default_mock_data_handler_v1 = [](const action_trace_v1& a) -> std::tuple>{ return {fc::mutable_variant_object()("hex" , fc::to_hex(a.data.data(), a.data.size())), {fc::mutable_variant_object()("hex" , fc::to_hex(a.return_value.data(), a.return_value.size()))}}; }; @@ -45,12 +45,12 @@ struct response_test_fixture { {} template - std::tuple> serialize_to_variant(const ActionTrace & action, const yield_function& yield) { + std::tuple> serialize_to_variant(const ActionTrace & action) { if constexpr(std::is_same_v){ - return fixture.mock_data_handler_v0(action, yield); + return fixture.mock_data_handler_v0(action); } else if constexpr(std::is_same_v){ - return fixture.mock_data_handler_v1(action, yield); + return fixture.mock_data_handler_v1(action); } } @@ -68,14 +68,14 @@ struct response_test_fixture { } - fc::variant get_block_trace( uint32_t block_height, const yield_function& yield = {} ) { - return response_impl.get_block_trace( block_height, yield ); + fc::variant get_block_trace( uint32_t block_height ) { + return response_impl.get_block_trace( block_height ); } // fixture data and methods - std::function mock_get_block; - std::function>(const action_trace_v0&, const yield_function&)> mock_data_handler_v0 = default_mock_data_handler_v0; - std::function>(const action_trace_v1&, const yield_function&)> mock_data_handler_v1 = default_mock_data_handler_v1; + std::function mock_get_block; + std::function>(const action_trace_v0&)> mock_data_handler_v0 = default_mock_data_handler_v0; + std::function>(const action_trace_v1&)> mock_data_handler_v1 = default_mock_data_handler_v1; response_impl_type response_impl; @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) ("transactions", fc::variants() ) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry{block_trace}, false); }; @@ -201,7 +201,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) })) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; @@ -287,13 +287,13 @@ BOOST_AUTO_TEST_SUITE(trace_responses) })) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; // simulate an inability to parse the parameters - mock_data_handler_v0 = [](const action_trace_v0&, const yield_function&) -> std::tuple> { + mock_data_handler_v0 = [](const action_trace_v0&) -> std::tuple> { return {}; }; @@ -414,13 +414,13 @@ BOOST_AUTO_TEST_SUITE(trace_responses) })) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; // simulate an inability to parse the parameters - mock_data_handler_v0 = [](const action_trace_v0&, const yield_function&) -> std::tuple> { + mock_data_handler_v0 = [](const action_trace_v0&) -> std::tuple> { return {}; }; @@ -458,7 +458,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) ("transactions", fc::variants() ) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), true); }; @@ -470,7 +470,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) BOOST_FIXTURE_TEST_CASE(corrupt_block_data, response_test_fixture) { - mock_get_block = []( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = []( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); throw bad_data_exception("mock exception"); }; @@ -480,7 +480,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) BOOST_FIXTURE_TEST_CASE(missing_block_data, response_test_fixture) { - mock_get_block = []( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = []( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return {}; }; @@ -490,71 +490,6 @@ BOOST_AUTO_TEST_SUITE(trace_responses) BOOST_TEST(null_response.is_null()); } - BOOST_FIXTURE_TEST_CASE(yield_throws, response_test_fixture) - { - auto block_trace = block_trace_v1 { - { - "b000000000000000000000000000000000000000000000000000000000000001"_h, - 1, - "0000000000000000000000000000000000000000000000000000000000000000"_h, - chain::block_timestamp_type(0), - "bp.one"_n - }, - "0000000000000000000000000000000000000000000000000000000000000000"_h, - "0000000000000000000000000000000000000000000000000000000000000000"_h, - 0, - { - { - { - "0000000000000000000000000000000000000000000000000000000000000001"_h, - { - { - 0, - "receiver"_n, "contract"_n, "action"_n, - {{ "alice"_n, "active"_n }}, - { 0x00, 0x01, 0x02, 0x03 } - } - } - }, - fc::enum_type{chain::transaction_receipt_header::status_enum::executed}, - 10, - 5, - std::vector{chain::signature_type()}, - {chain::time_point(), 1, 0, 100, 50, 0} - } - } - }; - - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { - BOOST_TEST(height == 1); - return std::make_tuple(data_log_entry(block_trace), false); - }; - - int countdown = 3; - yield_function yield = [&]() { - if (countdown-- == 0) { - throw yield_exception("mock"); - } - }; - - BOOST_REQUIRE_THROW(get_block_trace( 1, yield ), yield_exception); - } - - BOOST_FIXTURE_TEST_CASE(yield_throws_from_get_block, response_test_fixture) - { - // no other yield calls will throw - yield_function yield = [&]() { - }; - - // simulate a yield throw inside get block - mock_get_block = []( uint32_t height, const yield_function& yield) -> get_block_t { - throw yield_exception("mock exception"); - }; - - - BOOST_REQUIRE_THROW(get_block_trace( 1, yield ), yield_exception); - } - BOOST_FIXTURE_TEST_CASE(old_version_block_response, response_test_fixture) { auto block_trace = block_trace_v0 { @@ -607,7 +542,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) })) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; @@ -644,7 +579,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) ("transactions", fc::variants() ) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry{block_trace}, false); }; @@ -738,7 +673,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) })) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; @@ -826,13 +761,13 @@ BOOST_AUTO_TEST_SUITE(trace_responses) }) ); - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; // simulate an inability to parse the parameters and return_data - mock_data_handler_v1 = [](const action_trace_v1&, const yield_function&) -> std::tuple> { + mock_data_handler_v1 = [](const action_trace_v1&) -> std::tuple> { return {}; }; @@ -965,13 +900,13 @@ BOOST_AUTO_TEST_SUITE(trace_responses) })) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), false); }; // simulate an inability to parse the parameters and return_data - mock_data_handler_v1 = [](const action_trace_v1&, const yield_function&) -> std::tuple> { + mock_data_handler_v1 = [](const action_trace_v1&) -> std::tuple> { return {}; }; @@ -1007,7 +942,7 @@ BOOST_AUTO_TEST_SUITE(trace_responses) ("transactions", fc::variants() ) ; - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { + mock_get_block = [&block_trace]( uint32_t height ) -> get_block_t { BOOST_TEST(height == 1); return std::make_tuple(data_log_entry(block_trace), true); }; @@ -1017,57 +952,4 @@ BOOST_AUTO_TEST_SUITE(trace_responses) } - BOOST_FIXTURE_TEST_CASE(yield_throws_v2, response_test_fixture) - { - auto action_trace = action_trace_v1 { - { - 0, - "receiver"_n, "contract"_n, "action"_n, - {{ "alice"_n, "active"_n }}, - { 0x00, 0x01, 0x02, 0x03 } - }, - { 0x04, 0x05, 0x06, 0x07 } - }; - - auto transaction_trace = transaction_trace_v2 { - "0000000000000000000000000000000000000000000000000000000000000001"_h, - std::vector { - action_trace - }, - fc::enum_type{chain::transaction_receipt_header::status_enum::executed}, - 10, - 5, - std::vector{chain::signature_type()}, - {chain::time_point(), 1, 0, 100, 50, 0} - }; - - auto block_trace = block_trace_v2 { - "b000000000000000000000000000000000000000000000000000000000000001"_h, - 1, - "0000000000000000000000000000000000000000000000000000000000000000"_h, - chain::block_timestamp_type(0), - "bp.one"_n, - "0000000000000000000000000000000000000000000000000000000000000000"_h, - "0000000000000000000000000000000000000000000000000000000000000000"_h, - 0, - std::vector{ - transaction_trace - } - }; - - mock_get_block = [&block_trace]( uint32_t height, const yield_function& ) -> get_block_t { - BOOST_TEST(height == 1); - return std::make_tuple(data_log_entry(block_trace), false); - }; - - int countdown = 3; - yield_function yield = [&]() { - if (countdown-- == 0) { - throw yield_exception("mock"); - } - }; - - BOOST_REQUIRE_THROW(get_block_trace( 1, yield ), yield_exception); - } - BOOST_AUTO_TEST_SUITE_END() diff --git a/plugins/trace_api_plugin/trace_api_plugin.cpp b/plugins/trace_api_plugin/trace_api_plugin.cpp index 3f295a7b07..3750ba50a3 100644 --- a/plugins/trace_api_plugin/trace_api_plugin.cpp +++ b/plugins/trace_api_plugin/trace_api_plugin.cpp @@ -70,7 +70,7 @@ namespace { template struct shared_store_provider { - shared_store_provider(const std::shared_ptr& store) + explicit shared_store_provider(const std::shared_ptr& store) :store(store) {} @@ -83,8 +83,8 @@ namespace { store->append_lib(new_lib); } - get_block_t get_block(uint32_t height, const yield_function& yield) { - return store->get_block(height, yield); + get_block_t get_block(uint32_t height) { + return store->get_block(height); } void append_trx_ids(block_trxs_entry tt){ @@ -178,7 +178,7 @@ struct trace_api_common_impl { */ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this { - trace_api_rpc_plugin_impl( const std::shared_ptr& common ) + explicit trace_api_rpc_plugin_impl( const std::shared_ptr& common ) :common(common) {} static void set_program_options(appbase::options_description& cli, appbase::options_description& cfg) { @@ -233,30 +233,17 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this fc::microseconds::maximum() - deadline.time_since_epoch() ) { - deadline = fc::time_point::maximum(); - } else { - deadline += max_serialization_time; - } - return deadline; - } - void plugin_startup() { auto& http = app().get_plugin(); - fc::microseconds max_response_time = http.get_max_response_time(); http.add_async_handler("/v1/trace_api/get_block", - [wthis=weak_from_this(), max_response_time](std::string, std::string body, url_response_callback cb) + [wthis=weak_from_this()](std::string, std::string body, url_response_callback cb) { auto that = wthis.lock(); if (!that) { return; } - const auto deadline = that->calc_deadline( max_response_time ); - auto block_number = ([&body]() -> std::optional { if (body.empty()) { return {}; @@ -276,18 +263,18 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_thisreq_handler->get_block_trace(*block_number, [deadline]() { FC_CHECK_DEADLINE(deadline); }); + auto resp = that->req_handler->get_block_trace(*block_number); if (resp.is_null()) { error_results results{404, "Trace API: block trace missing"}; - cb( 404, deadline, fc::variant( results )); + cb( 404, fc::time_point::maximum(), fc::variant( results )); } else { - cb( 200, deadline, std::move(resp) ); + cb( 200, fc::time_point::maximum(), std::move(resp) ); } } catch (...) { http_plugin::handle_exception("trace_api", "get_block", body, cb); @@ -296,15 +283,13 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_thiscalc_deadline( max_response_time ); - auto trx_id = ([&body]() -> std::optional { if (body.empty()) { return {}; @@ -323,23 +308,23 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_thisstore->get_trx_block_number(*trx_id, common->minimum_irreversible_history_blocks, [deadline]() { FC_CHECK_DEADLINE(deadline); }); + get_block_n blk_num = common->store->get_trx_block_number(*trx_id, common->minimum_irreversible_history_blocks); if (!blk_num.has_value()){ error_results results{404, "Trace API: transaction id missing in the transaction id log files"}; - cb( 404, deadline, fc::variant( results )); + cb( 404, fc::time_point::maximum(), fc::variant( results )); } else { - auto resp = that->req_handler->get_transaction_trace(*trx_id, *blk_num, [deadline]() { FC_CHECK_DEADLINE(deadline); }); + auto resp = that->req_handler->get_transaction_trace(*trx_id, *blk_num); if (resp.is_null()) { error_results results{404, "Trace API: transaction trace missing"}; - cb( 404, deadline, fc::variant( results )); + cb( 404, fc::time_point::maximum(), fc::variant( results )); } else { - cb( 200, deadline, std::move(resp) ); + cb( 200, fc::time_point::maximum(), std::move(resp) ); } } } catch (...) { @@ -358,7 +343,7 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_this& common ) + explicit trace_api_plugin_impl( const std::shared_ptr& common ) :common(common) {} void plugin_initialize(const appbase::variables_map& options) { @@ -421,11 +406,9 @@ struct trace_api_plugin_impl { std::optional irreversible_block_connection; }; -trace_api_plugin::trace_api_plugin() -{} +trace_api_plugin::trace_api_plugin() = default; -trace_api_plugin::~trace_api_plugin() -{} +trace_api_plugin::~trace_api_plugin() = default; void trace_api_plugin::set_program_options(appbase::options_description& cli, appbase::options_description& cfg) { trace_api_common_impl::set_program_options(cli, cfg); @@ -460,11 +443,9 @@ void trace_api_plugin::handle_sighup() { fc::logger::update( logger_name, _log ); } -trace_api_rpc_plugin::trace_api_rpc_plugin() -{} +trace_api_rpc_plugin::trace_api_rpc_plugin() = default; -trace_api_rpc_plugin::~trace_api_rpc_plugin() -{} +trace_api_rpc_plugin::~trace_api_rpc_plugin() = default; void trace_api_rpc_plugin::set_program_options(appbase::options_description& cli, appbase::options_description& cfg) { trace_api_common_impl::set_program_options(cli, cfg); From 4692fd819b157fc8c4c70e4ce8e3cc1ba07217a0 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 2 May 2023 13:06:32 -0500 Subject: [PATCH 02/32] GH-1062 Remove unused struct --- .../test_control_api_plugin.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/plugins/test_control_api_plugin/test_control_api_plugin.cpp b/plugins/test_control_api_plugin/test_control_api_plugin.cpp index 4f0e21c17e..61dd5943ac 100644 --- a/plugins/test_control_api_plugin/test_control_api_plugin.cpp +++ b/plugins/test_control_api_plugin/test_control_api_plugin.cpp @@ -11,26 +11,19 @@ using namespace eosio; class test_control_api_plugin_impl { public: - test_control_api_plugin_impl(controller& db) + explicit test_control_api_plugin_impl(controller& db) : db(db) {} controller& db; }; -test_control_api_plugin::test_control_api_plugin(){} -test_control_api_plugin::~test_control_api_plugin(){} +test_control_api_plugin::test_control_api_plugin() = default; +test_control_api_plugin::~test_control_api_plugin() = default; void test_control_api_plugin::set_program_options(options_description&, options_description&) {} void test_control_api_plugin::plugin_initialize(const variables_map&) {} -struct async_result_visitor : public fc::visitor { - template - std::string operator()(const T& v) const { - return fc::json::to_string(v); - } -}; - #define CALL_WITH_API_400(api_name, api_handle, api_namespace, call_name, http_response_code, params_type) \ {std::string("/v1/" #api_name "/" #call_name), \ [api_handle](string&&, string&& body, url_response_callback&& cb) mutable { \ From 2eb261c5922af8dee9c460b0f494b4f8b1a9fd3b Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 2 May 2023 13:07:56 -0500 Subject: [PATCH 03/32] GH-1062 Enforce http_max_response_time on main thread only and always return at least one. --- .../producer_api_plugin.cpp | 20 ++++--------------- plugins/producer_plugin/producer_plugin.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/plugins/producer_api_plugin/producer_api_plugin.cpp b/plugins/producer_api_plugin/producer_api_plugin.cpp index 9d477ed425..61537ab7c0 100644 --- a/plugins/producer_api_plugin/producer_api_plugin.cpp +++ b/plugins/producer_api_plugin/producer_api_plugin.cpp @@ -1,8 +1,8 @@ #include #include +#include #include -#include #include @@ -20,20 +20,12 @@ namespace eosio { using namespace eosio; -struct async_result_visitor : public fc::visitor { - template - fc::variant operator()(const T& v) const { - return fc::variant(v); - } -}; - #define CALL_WITH_400(api_name, api_handle, call_name, INVOKE, http_response_code) \ {std::string("/v1/" #api_name "/" #call_name), \ - [&api_handle, http_max_response_time](string&&, string&& body, url_response_callback&& cb) mutable { \ + [&](string&&, string&& body, url_response_callback&& cb) mutable { \ try { \ - auto deadline = fc::time_point::now() + http_max_response_time; \ INVOKE \ - cb(http_response_code, deadline, fc::variant(result)); \ + cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ @@ -69,6 +61,7 @@ struct async_result_visitor : public fc::visitor { auto result = api_handle.call_name(std::move(params)); #define INVOKE_R_R_D(api_handle, call_name, in_param) \ + auto deadline = fc::time_point::now() + http_max_response_time; \ auto params = parse_params(body);\ auto result = api_handle.call_name(std::move(params), deadline); @@ -84,11 +77,6 @@ struct async_result_visitor : public fc::visitor { api_handle.call_name(std::move(params)); \ eosio::detail::producer_api_plugin_response result{"ok"}; -#define INVOKE_V_R_II(api_handle, call_name, in_param) \ - auto params = parse_params(body);\ - api_handle.call_name(std::move(params)); \ - eosio::detail::producer_api_plugin_response result{"ok"}; - #define INVOKE_V_V(api_handle, call_name) \ body = parse_params(body); \ api_handle.call_name(); \ diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 103223b903..42e5c77032 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1549,6 +1549,7 @@ producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_pa fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + params_deadline = std::min(deadline, params_deadline); auto& ua = my->_unapplied_transactions; @@ -1588,8 +1589,7 @@ producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_pa result.incoming_size = ua.incoming_size(); uint32_t remaining = p.limit ? *p.limit : std::numeric_limits::max(); - while (itr != ua.end() && remaining > 0 && params_deadline > fc::time_point::now()) { - FC_CHECK_DEADLINE(deadline); + while (itr != ua.end() && remaining > 0) { auto& r = result.trxs.emplace_back(); r.trx_id = itr->id(); r.expiration = itr->expiration(); @@ -1607,6 +1607,8 @@ producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_pa ++itr; remaining--; + if (params_deadline <= fc::time_point::now()) + break; } if (itr != ua.end()) { From 6c831db8f30d2d2e2d1bb7c7e2ad967ce84c0700 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 2 May 2023 15:32:57 -0500 Subject: [PATCH 04/32] GH-1062 Use maximum() instead of 365 days for max --- plugins/http_plugin/http_plugin.cpp | 5 ++--- .../include/eosio/http_plugin/beast_http_session.hpp | 5 +++-- plugins/http_plugin/include/eosio/http_plugin/common.hpp | 5 +++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/http_plugin/http_plugin.cpp b/plugins/http_plugin/http_plugin.cpp index 96a43c4a1f..ec5f2021d4 100644 --- a/plugins/http_plugin/http_plugin.cpp +++ b/plugins/http_plugin/http_plugin.cpp @@ -200,7 +200,7 @@ namespace eosio { ("http-max-in-flight-requests", bpo::value()->default_value(-1), "Maximum number of requests http_plugin should use for processing http requests. 429 error response when exceeded." ) ("http-max-response-time-ms", bpo::value()->default_value(30), - "Maximum time for processing a request, -1 for unlimited") + "Maximum time on main thread for processing a request, -1 for unlimited") ("verbose-http-errors", bpo::bool_switch()->default_value(false), "Append the error log to HTTP responses") ("http-validate-host", boost::program_options::value()->default_value(true), @@ -236,9 +236,8 @@ namespace eosio { int64_t max_reponse_time_ms = options.at("http-max-response-time-ms").as(); EOS_ASSERT( max_reponse_time_ms == -1 || max_reponse_time_ms >= 0, chain::plugin_config_exception, "http-max-response-time-ms must be -1, or non-negative: ${m}", ("m", max_reponse_time_ms) ); - // set to one year for -1, unlimited, since this is added to fc::time_point::now() for a deadline my->plugin_state->max_response_time = max_reponse_time_ms == -1 ? - fc::days(365) : fc::microseconds( max_reponse_time_ms * 1000 ); + fc::microseconds::maximum() : fc::microseconds( max_reponse_time_ms * 1000 ); my->plugin_state->validate_host = options.at("http-validate-host").as(); if( options.count( "http-alias" )) { diff --git a/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp b/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp index 4c2384ca14..83148ca84c 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -421,7 +422,7 @@ class beast_http_session : public detail::abstract_conn { error_results results{static_cast(http::status::internal_server_error), "Internal Service Error", error_results::error_info( e, http_plugin::verbose_errors() )}; - err_str = fc::json::to_string( results, fc::time_point::now() + plugin_state_->max_response_time ); + err_str = fc::json::to_string( results, plugin_state_->get_max_response_deadline() ); } } catch(std::exception& e) { err_str = e.what(); @@ -431,7 +432,7 @@ class beast_http_session : public detail::abstract_conn { "Internal Service Error", error_results::error_info( fc::exception( FC_LOG_MESSAGE( error, err_str ) ), http_plugin::verbose_errors() )}; - err_str = fc::json::to_string( results, fc::time_point::now() + plugin_state_->max_response_time ); + err_str = fc::json::to_string( results, plugin_state_->get_max_response_deadline() ); } } catch(...) { err_str = "Unknown exception"; diff --git a/plugins/http_plugin/include/eosio/http_plugin/common.hpp b/plugins/http_plugin/include/eosio/http_plugin/common.hpp index d39e257adc..a39e7db34a 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/common.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/common.hpp @@ -136,6 +136,11 @@ struct http_plugin_state { explicit http_plugin_state(fc::logger& log) : logger(log) {} + + fc::time_point get_max_response_deadline() const { + return max_response_time == fc::microseconds::maximum() ? fc::time_point::maximum() + : fc::time_point::now() + max_response_time; + } }; /** From 8cb572cab0f02ca42a965b74917facd1c91d203b Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 2 May 2023 15:44:36 -0500 Subject: [PATCH 05/32] GH-1062 Add 1000 limit --- .../producer_api_plugin/producer_api_plugin.cpp | 3 ++- plugins/producer_plugin/producer_plugin.cpp | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/producer_api_plugin/producer_api_plugin.cpp b/plugins/producer_api_plugin/producer_api_plugin.cpp index 61537ab7c0..d8f9dee78f 100644 --- a/plugins/producer_api_plugin/producer_api_plugin.cpp +++ b/plugins/producer_api_plugin/producer_api_plugin.cpp @@ -61,7 +61,8 @@ using namespace eosio; auto result = api_handle.call_name(std::move(params)); #define INVOKE_R_R_D(api_handle, call_name, in_param) \ - auto deadline = fc::time_point::now() + http_max_response_time; \ + auto deadline = http_max_response_time == fc::microseconds::maximum() ? fc::time_point::maximum() \ + : fc::time_point::now() + http_max_response_time; \ auto params = parse_params(body);\ auto result = api_handle.call_name(std::move(params), deadline); diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 42e5c77032..9995cd873c 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -18,10 +17,6 @@ #include #include - -#include -#include -#include #include #include #include @@ -30,6 +25,11 @@ #include #include +#include +#include +#include +#include + namespace bmi = boost::multi_index; using bmi::indexed_by; using bmi::ordered_non_unique; @@ -1548,8 +1548,7 @@ producer_plugin::get_unapplied_transactions_result producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_params& p, const fc::time_point& deadline ) const { fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; - params_deadline = std::min(deadline, params_deadline); + fc::time_point params_deadline = std::min(fc::time_point::now() + params_time_limit, deadline); auto& ua = my->_unapplied_transactions; @@ -1589,6 +1588,8 @@ producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_pa result.incoming_size = ua.incoming_size(); uint32_t remaining = p.limit ? *p.limit : std::numeric_limits::max(); + if (deadline != fc::time_point::maximum() && remaining > 1000) + remaining = 1000; while (itr != ua.end() && remaining > 0) { auto& r = result.trxs.emplace_back(); r.trx_id = itr->id(); @@ -1607,7 +1608,7 @@ producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_pa ++itr; remaining--; - if (params_deadline <= fc::time_point::now()) + if (fc::time_point::now() >= params_deadline) break; } From 9bcbb4462490375535c819035c186ab7377e23b7 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:22:15 -0500 Subject: [PATCH 06/32] GH-1062 Add move constructor for variant_object to mutable_variant_object --- libraries/libfc/include/fc/variant_object.hpp | 11 ++++++++++- libraries/libfc/src/variant_object.cpp | 11 +++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libraries/libfc/include/fc/variant_object.hpp b/libraries/libfc/include/fc/variant_object.hpp index fe0dcd4e4e..9c143294b3 100644 --- a/libraries/libfc/include/fc/variant_object.hpp +++ b/libraries/libfc/include/fc/variant_object.hpp @@ -228,11 +228,20 @@ namespace fc mutable_variant_object( mutable_variant_object&& ); mutable_variant_object( const mutable_variant_object& ); - mutable_variant_object( const variant_object& ); + explicit mutable_variant_object( const variant_object& ); + /* + * Use with care as the internal shared state of variant_object is moved. + */ + explicit mutable_variant_object( variant_object&& ); mutable_variant_object& operator=( mutable_variant_object&& ); mutable_variant_object& operator=( const mutable_variant_object& ); mutable_variant_object& operator=( const variant_object& ); + /** + * Use with care as the internal shared state of variant_object is moved. + */ + mutable_variant_object& operator=( variant_object&& ); + private: std::unique_ptr< std::vector< entry > > _key_value; friend class variant_object; diff --git a/libraries/libfc/src/variant_object.cpp b/libraries/libfc/src/variant_object.cpp index fbcca7fe1a..de08d2a79f 100644 --- a/libraries/libfc/src/variant_object.cpp +++ b/libraries/libfc/src/variant_object.cpp @@ -286,6 +286,11 @@ namespace fc { } + mutable_variant_object::mutable_variant_object( variant_object&& obj ) + : _key_value( new std::vector(std::move(*obj._key_value)) ) + { + } + mutable_variant_object::mutable_variant_object( const mutable_variant_object& obj ) : _key_value( new std::vector(*obj._key_value) ) { @@ -302,6 +307,12 @@ namespace fc return *this; } + mutable_variant_object& mutable_variant_object::operator=( variant_object&& obj ) + { + *_key_value = std::move(*obj._key_value); + return *this; + } + mutable_variant_object& mutable_variant_object::operator=( mutable_variant_object&& obj ) { if (this != &obj) From 4e176a137ce30f8b39d54bd594638e69706de237 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:24:11 -0500 Subject: [PATCH 07/32] GH-1062 Change default abi_serializer yield function to enforce max_serialization_time per serialization step. --- libraries/chain/abi_serializer.cpp | 3 +- .../include/eosio/chain/abi_serializer.hpp | 40 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index 58e56bcf02..cc30749503 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -23,7 +23,8 @@ namespace eosio { namespace chain { template inline fc::variant variant_from_stream(fc::datastream& stream, const abi_serializer::yield_function_t& yield) { - fc::yield_function_t y = [&yield](){ yield(0); }; // create yield function matching fc::variant requirements, 0 for recursive depth + yield(0); // reset deadline + fc::yield_function_t y = [&yield](){ yield(1); }; // create yield function matching fc::variant requirements, 0 for recursive depth T temp; fc::raw::unpack( stream, temp ); y(); diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 98406e59c5..8e8688dd84 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -122,19 +122,15 @@ struct abi_serializer { // create standard yield function that checks for max_serialization_time and max_recursion_depth. // now() deadline captured at time of this call static yield_function_t create_yield_function(const fc::microseconds& max_serialization_time) { - fc::time_point deadline = fc::time_point::now(); - if( max_serialization_time > fc::microseconds::maximum() - deadline.time_since_epoch() ) { - deadline = fc::time_point::maximum(); - } else { - deadline += max_serialization_time; - } - return [max_serialization_time, last_call=deadline](size_t recursion_depth) mutable { + return [max_serialization_time, last_call=fc::time_point::now()](size_t recursion_depth) mutable { EOS_ASSERT( recursion_depth < max_recursion_depth, abi_recursion_depth_exception, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) ); - EOS_ASSERT( (fc::time_point::now() - last_call) < max_serialization_time, abi_serialization_deadline_exception, - "serialization time limit ${t}us exceeded", ("t", max_serialization_time) ); + auto prev_call = last_call; last_call = fc::time_point::now(); + // recursion_depth 0 is flag used to indicate a reset of deadline + EOS_ASSERT( recursion_depth == 0 || (last_call - prev_call) < max_serialization_time, abi_serialization_deadline_exception, + "serialization time limit ${t}us exceeded", ("t", max_serialization_time) ); }; } @@ -183,12 +179,13 @@ namespace impl { void check_deadline()const { yield( recursion_depth ); } abi_serializer::yield_function_t get_yield_function() { return yield; } + void reset_deadline() { yield(0); } fc::scoped_exit> enter_scope(); protected: abi_serializer::yield_function_t yield; - size_t recursion_depth = 0; + size_t recursion_depth = 1; bool log = false; }; @@ -509,6 +506,7 @@ namespace impl { set_hex_data(mvo, "data", act.data); } set_hex_data(mvo, "hex_data", act.data); + ctx.reset_deadline(); out(name, std::move(mvo)); } @@ -525,6 +523,7 @@ namespace impl { static void add( mutable_variant_object& out, const char* name, const action_trace& act_trace, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 17); + ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; @@ -542,9 +541,11 @@ namespace impl { mvo("block_time", act_trace.block_time); mvo("producer_block_id", act_trace.producer_block_id); mvo("account_ram_deltas", act_trace.account_ram_deltas); + ctx.reset_deadline(); mvo("except", act_trace.except); mvo("error_code", act_trace.error_code); + ctx.reset_deadline(); mvo("return_value_hex_data", act_trace.return_value); auto act = act_trace.act; try { @@ -553,6 +554,7 @@ namespace impl { const abi_serializer& abi = *abi_optional; auto type = abi.get_action_result_type(act.name); if (!type.empty()) { + ctx.reset_deadline(); binary_to_variant_context _ctx(abi, ctx, type); _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place mvo( "return_value_data", abi._binary_to_variant( type, act_trace.return_value, _ctx )); @@ -575,16 +577,23 @@ namespace impl { static void add( mutable_variant_object &out, const char* name, const packed_transaction& ptrx, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 4); + ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; auto trx = ptrx.get_transaction(); mvo("id", trx.id()); + ctx.reset_deadline(); mvo("signatures", ptrx.get_signatures()); + ctx.reset_deadline(); mvo("compression", ptrx.get_compression()); + ctx.reset_deadline(); mvo("packed_context_free_data", ptrx.get_packed_context_free_data()); + ctx.reset_deadline(); mvo("context_free_data", ptrx.get_context_free_data()); + ctx.reset_deadline(); if( !ctx.is_logging() ) mvo("packed_trx", ptrx.get_packed_transaction()); + ctx.reset_deadline(); add(mvo, "transaction", trx, resolver, ctx); out(name, std::move(mvo)); @@ -599,6 +608,7 @@ namespace impl { static void add( mutable_variant_object &out, const char* name, const transaction& trx, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 9); + ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; mvo("expiration", trx.expiration); @@ -607,7 +617,9 @@ namespace impl { mvo("max_net_usage_words", trx.max_net_usage_words); mvo("max_cpu_usage_ms", trx.max_cpu_usage_ms); mvo("delay_sec", trx.delay_sec); + ctx.reset_deadline(); add(mvo, "context_free_actions", trx.context_free_actions, resolver, ctx); + ctx.reset_deadline(); add(mvo, "actions", trx.actions, resolver, ctx); // process contents of block.transaction_extensions @@ -630,6 +642,7 @@ namespace impl { static void add( mutable_variant_object &out, const char* name, const signed_block& block, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 12); + ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; mvo("timestamp", block.timestamp); @@ -661,7 +674,9 @@ namespace impl { mvo("new_producer_schedule", new_producer_schedule); } + ctx.reset_deadline(); mvo("producer_signature", block.producer_signature); + ctx.reset_deadline(); add(mvo, "transactions", block.transactions, resolver, ctx); // process contents of block.block_extensions @@ -672,6 +687,7 @@ namespace impl { mvo("additional_signatures", additional_signatures); } + ctx.reset_deadline(); out(name, std::move(mvo)); } }; @@ -977,7 +993,7 @@ using abi_serializer_cache_t = std::unordered_map(const account_name& name)> resolver) : + explicit abi_serializer_cache_builder(std::function(const account_name& name)> resolver) : resolver_(std::move(resolver)) { } From 086f1fb36727b13cb1d450e401918d20e134feab Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:25:08 -0500 Subject: [PATCH 08/32] GH-1062 Add a safe_add that does not overflow. --- libraries/libfc/include/fc/time.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/libfc/include/fc/time.hpp b/libraries/libfc/include/fc/time.hpp index d2d0dfc80a..7e2c88c51c 100644 --- a/libraries/libfc/include/fc/time.hpp +++ b/libraries/libfc/include/fc/time.hpp @@ -49,6 +49,16 @@ namespace fc { std::string to_iso_string()const; static time_point from_iso_string( const std::string& s ); + // protect against overflow + constexpr time_point& safe_add( const microseconds& m ) { + if (m.count() > 0 && elapsed > fc::microseconds::maximum() - m) { + elapsed = microseconds::maximum(); + } else { // does not guard against underflow + elapsed += m; + } + return *this; + } + constexpr const microseconds& time_since_epoch()const { return elapsed; } constexpr uint32_t sec_since_epoch()const { return elapsed.count() / 1000000; } constexpr bool operator > ( const time_point& t )const { return elapsed._count > t.elapsed._count; } From 0e8739efc700b3a976080dde87bc26d7a673ab15 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:26:14 -0500 Subject: [PATCH 09/32] GH-1062 Remove deadline since we no longer want to interrupt json creation on the http thread --- plugins/http_plugin/http_plugin.cpp | 24 +++++++++---------- .../include/eosio/http_plugin/http_plugin.hpp | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/http_plugin/http_plugin.cpp b/plugins/http_plugin/http_plugin.cpp index ec5f2021d4..f15670de68 100644 --- a/plugins/http_plugin/http_plugin.cpp +++ b/plugins/http_plugin/http_plugin.cpp @@ -84,8 +84,8 @@ namespace eosio { return; } - url_response_callback wrapped_then = [then=std::move(then)](int code, const fc::time_point& deadline, std::optional resp) { - then(code, deadline, std::move(resp)); + url_response_callback wrapped_then = [then=std::move(then)](int code, std::optional resp) { + then(code, std::move(resp)); }; // post to the app thread taking shared ownership of next (via std::shared_ptr), @@ -342,7 +342,7 @@ namespace eosio { [&](string&&, string&& body, url_response_callback&& cb) { try { auto result = (*this).get_supported_apis(); - cb(200, fc::time_point::maximum(), fc::variant(result)); + cb(200, fc::variant(result)); } catch (...) { handle_exception("node", "get_supported_apis", body.empty() ? "{}" : body, cb); } @@ -400,48 +400,48 @@ namespace eosio { throw; } catch (chain::unknown_block_exception& e) { error_results results{400, "Unknown Block", error_results::error_info(e, verbose_http_errors)}; - cb( 400, fc::time_point::maximum(), fc::variant( results )); + cb( 400, fc::variant( results )); fc_dlog( logger(), "Unknown block while processing ${api}.${call}: ${e}", ("api", api_name)("call", call_name)("e", e.to_detail_string()) ); } catch (chain::invalid_http_request& e) { error_results results{400, "Invalid Request", error_results::error_info(e, verbose_http_errors)}; - cb( 400, fc::time_point::maximum(), fc::variant( results )); + cb( 400, fc::variant( results )); fc_dlog( logger(), "Invalid http request while processing ${api}.${call}: ${e}", ("api", api_name)("call", call_name)("e", e.to_detail_string()) ); } catch (chain::account_query_exception& e) { error_results results{400, "Account lookup", error_results::error_info(e, verbose_http_errors)}; - cb( 400, fc::time_point::maximum(), fc::variant( results )); + cb( 400, fc::variant( results )); fc_dlog( logger(), "Account query exception while processing ${api}.${call}: ${e}", ("api", api_name)("call", call_name)("e", e.to_detail_string()) ); } catch (chain::unsatisfied_authorization& e) { error_results results{401, "UnAuthorized", error_results::error_info(e, verbose_http_errors)}; - cb( 401, fc::time_point::maximum(), fc::variant( results )); + cb( 401, fc::variant( results )); fc_dlog( logger(), "Auth error while processing ${api}.${call}: ${e}", ("api", api_name)("call", call_name)("e", e.to_detail_string()) ); } catch (chain::tx_duplicate& e) { error_results results{409, "Conflict", error_results::error_info(e, verbose_http_errors)}; - cb( 409, fc::time_point::maximum(), fc::variant( results )); + cb( 409, fc::variant( results )); fc_dlog( logger(), "Duplicate trx while processing ${api}.${call}: ${e}", ("api", api_name)("call", call_name)("e", e.to_detail_string()) ); } catch (fc::eof_exception& e) { error_results results{422, "Unprocessable Entity", error_results::error_info(e, verbose_http_errors)}; - cb( 422, fc::time_point::maximum(), fc::variant( results )); + cb( 422, fc::variant( results )); fc_elog( logger(), "Unable to parse arguments to ${api}.${call}", ("api", api_name)( "call", call_name ) ); fc_dlog( logger(), "Bad arguments: ${args}", ("args", body) ); } catch (fc::exception& e) { error_results results{500, "Internal Service Error", error_results::error_info(e, verbose_http_errors)}; - cb( 500, fc::time_point::maximum(), fc::variant( results )); + cb( 500, fc::variant( results )); fc_dlog( logger(), "Exception while processing ${api}.${call}: ${e}", ("api", api_name)( "call", call_name )("e", e.to_detail_string()) ); } catch (std::exception& e) { error_results results{500, "Internal Service Error", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, e.what())), verbose_http_errors)}; - cb( 500, fc::time_point::maximum(), fc::variant( results )); + cb( 500, fc::variant( results )); fc_dlog( logger(), "STD Exception encountered while processing ${api}.${call}: ${e}", ("api", api_name)("call", call_name)("e", e.what()) ); } catch (...) { error_results results{500, "Internal Service Error", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, "Unknown Exception" )), verbose_http_errors)}; - cb( 500, fc::time_point::maximum(), fc::variant( results )); + cb( 500, fc::variant( results )); fc_elog( logger(), "Unknown Exception encountered while processing ${api}.${call}", ("api", api_name)( "call", call_name ) ); } diff --git a/plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp b/plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp index 5de6eb3efa..c9520e9f29 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp @@ -13,9 +13,9 @@ namespace eosio { * @brief A callback function provided to a URL handler to * allow it to specify the HTTP response code and body * - * Arguments: response_code, deadline, response_body + * Arguments: response_code, response_body */ - using url_response_callback = std::function)>; + using url_response_callback = std::function)>; /** * @brief Callback type for a URL handler From cb1b52de8276b9be727581765fe2efbeaf5e42b6 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:28:02 -0500 Subject: [PATCH 10/32] GH-1062 Remove deadline since we no longer want to interrupt json creation on the http thread --- plugins/chain_api_plugin/chain_api_plugin.cpp | 10 +++---- .../db_size_api_plugin/db_size_api_plugin.cpp | 2 +- .../include/eosio/http_plugin/common.hpp | 27 ++++++++----------- .../include/eosio/http_plugin/macros.hpp | 24 +++++------------ plugins/http_plugin/tests/unit_tests.cpp | 6 ++--- plugins/net_api_plugin/net_api_plugin.cpp | 2 +- .../producer_api_plugin.cpp | 4 +-- .../test_control_api_plugin.cpp | 2 +- plugins/trace_api_plugin/trace_api_plugin.cpp | 14 +++++----- .../wallet_api_plugin/wallet_api_plugin.cpp | 2 +- programs/keosd/main.cpp | 2 +- tests/chain_plugin_tests.cpp | 4 +-- 12 files changed, 41 insertions(+), 58 deletions(-) diff --git a/plugins/chain_api_plugin/chain_api_plugin.cpp b/plugins/chain_api_plugin/chain_api_plugin.cpp index ab9816d1c5..8bda17f91f 100644 --- a/plugins/chain_api_plugin/chain_api_plugin.cpp +++ b/plugins/chain_api_plugin/chain_api_plugin.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include namespace eosio { @@ -11,15 +12,15 @@ using namespace eosio; class chain_api_plugin_impl { public: - chain_api_plugin_impl(controller& db) + explicit chain_api_plugin_impl(controller& db) : db(db) {} controller& db; }; -chain_api_plugin::chain_api_plugin(){} -chain_api_plugin::~chain_api_plugin(){} +chain_api_plugin::chain_api_plugin() = default; +chain_api_plugin::~chain_api_plugin() = default; void chain_api_plugin::set_program_options(options_description&, options_description&) {} void chain_api_plugin::plugin_initialize(const variables_map&) {} @@ -47,9 +48,8 @@ parse_params(body);\ - FC_CHECK_DEADLINE(deadline);\ fc::variant result( api_handle.call_name( std::move(params), deadline ) ); \ - cb(http_response_code, deadline, std::move(result)); \ + cb(http_response_code, std::move(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ diff --git a/plugins/db_size_api_plugin/db_size_api_plugin.cpp b/plugins/db_size_api_plugin/db_size_api_plugin.cpp index bb26ddb097..2b2cbdb676 100644 --- a/plugins/db_size_api_plugin/db_size_api_plugin.cpp +++ b/plugins/db_size_api_plugin/db_size_api_plugin.cpp @@ -15,7 +15,7 @@ using namespace eosio; try { \ body = parse_params(body); \ INVOKE \ - cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \ + cb(http_response_code, fc::variant(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ diff --git a/plugins/http_plugin/include/eosio/http_plugin/common.hpp b/plugins/http_plugin/include/eosio/http_plugin/common.hpp index a39e7db34a..dd2db95611 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/common.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/common.hpp @@ -2,18 +2,11 @@ #include // for thread pool #include -#include - -#include -#include -#include -#include -#include -#include #include #include #include +#include #include #include @@ -33,6 +26,13 @@ #include #include +#include +#include +#include +#include +#include +#include + namespace eosio { static uint16_t const uri_default_port = 80; /// Default port for wss:// @@ -153,27 +153,22 @@ struct http_plugin_state { */ auto make_http_response_handler(std::shared_ptr plugin_state, detail::abstract_conn_ptr session_ptr, http_content_type content_type) { return [plugin_state{std::move(plugin_state)}, - session_ptr{std::move(session_ptr)}, content_type](int code, fc::time_point deadline, std::optional response) { + session_ptr{std::move(session_ptr)}, content_type](int code, std::optional response) { auto payload_size = detail::in_flight_sizeof(response); if(auto error_str = session_ptr->verify_max_bytes_in_flight(payload_size); !error_str.empty()) { session_ptr->send_busy_response(std::move(error_str)); return; } - auto start = fc::time_point::now(); - if (deadline == fc::time_point::maximum()) { // no caller supplied deadline so use http configured deadline - deadline = start + plugin_state->max_response_time; - } - plugin_state->bytes_in_flight += payload_size; // post back to an HTTP thread to allow the response handler to be called from any thread boost::asio::post(plugin_state->thread_pool.get_executor(), - [plugin_state, session_ptr, code, deadline, start, payload_size, response = std::move(response), content_type]() { + [plugin_state, session_ptr, code, payload_size, response = std::move(response), content_type]() { try { plugin_state->bytes_in_flight -= payload_size; if (response.has_value()) { - std::string json = (content_type == http_content_type::plaintext) ? response->as_string() : fc::json::to_string(*response, deadline + (fc::time_point::now() - start)); + std::string json = (content_type == http_content_type::plaintext) ? response->as_string() : fc::json::to_string(*response, fc::time_point::maximum()); if (auto error_str = session_ptr->verify_max_bytes_in_flight(json.size()); error_str.empty()) session_ptr->send_response(std::move(json), code); else diff --git a/plugins/http_plugin/include/eosio/http_plugin/macros.hpp b/plugins/http_plugin/include/eosio/http_plugin/macros.hpp index 82cd8cddcd..0c855930ae 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/macros.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/macros.hpp @@ -1,21 +1,13 @@ #pragma once -struct async_result_visitor : public fc::visitor { - template - fc::variant operator()(const T& v) const { - return fc::variant(v); - } -}; - #define CALL_ASYNC_WITH_400(api_name, api_handle, api_namespace, call_name, call_result, http_resp_code, params_type) \ { std::string("/v1/" #api_name "/" #call_name), \ [api_handle, &_http_plugin](string&&, string&& body, url_response_callback&& cb) mutable { \ - auto deadline = api_handle.start(); \ + api_handle.start(); \ try { \ auto params = parse_params(body); \ - FC_CHECK_DEADLINE(deadline); \ using http_fwd_t = std::function()>; \ - api_handle.call_name( std::move(params), \ + api_handle.call_name( std::move(params), /* called on main application thread */ \ [&_http_plugin, cb=std::move(cb), body=std::move(body)] \ (const chain::next_function_variant& result) mutable { \ if (std::holds_alternative(result)) { \ @@ -25,8 +17,7 @@ struct async_result_visitor : public fc::visitor { http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ } else if (std::holds_alternative(result)) { \ - cb(http_resp_code, fc::time_point::maximum(), \ - fc::variant(std::get(std::move(result)))); \ + cb(http_resp_code, fc::variant(std::get(std::move(result)))); \ } else { \ /* api returned a function to be processed on the http_plugin thread pool */ \ assert(std::holds_alternative(result)); \ @@ -41,8 +32,7 @@ struct async_result_visitor : public fc::visitor { http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ } else { \ - cb(resp_code, fc::time_point::maximum(), \ - fc::variant(std::get(std::move(result)))) ; \ + cb(resp_code, fc::variant(std::get(std::move(result)))); \ } \ }); \ } \ @@ -63,10 +53,9 @@ struct async_result_visitor : public fc::visitor { auto deadline = api_handle.start(); \ try { \ auto params = parse_params(body); \ - FC_CHECK_DEADLINE(deadline); \ using http_fwd_t = std::function()>; \ + /* called on main application thread */ \ http_fwd_t http_fwd(api_handle.call_name(std::move(params), deadline)); \ - FC_CHECK_DEADLINE(deadline); \ _http_plugin.post_http_thread_pool([resp_code=http_resp_code, cb=std::move(cb), \ body=std::move(body), \ http_fwd = std::move(http_fwd)]() { \ @@ -78,8 +67,7 @@ struct async_result_visitor : public fc::visitor { http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ } else { \ - cb(resp_code, fc::time_point::maximum(), \ - fc::variant(std::get(std::move(result)))) ; \ + cb(resp_code, fc::variant(std::get(std::move(result)))); \ } \ }); \ } catch (...) { \ diff --git a/plugins/http_plugin/tests/unit_tests.cpp b/plugins/http_plugin/tests/unit_tests.cpp index b7a801334f..3b37d18499 100644 --- a/plugins/http_plugin/tests/unit_tests.cpp +++ b/plugins/http_plugin/tests/unit_tests.cpp @@ -38,18 +38,18 @@ class Db p.add_api({ { std::string("/hello"), [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); } }, { std::string("/echo"), [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant(body)); + cb(200, fc::variant(body)); } }, { std::string("/check_ones"), // returns "yes" if body only has only '1' chars, "no" otherwise [&](string&&, string&& body, url_response_callback&& cb) { bool ok = std::all_of(body.begin(), body.end(), [](char c) { return c == '1'; }); - cb(200, fc::time_point::maximum(), fc::variant(ok ? string("yes") : string("no"))); + cb(200, fc::variant(ok ? string("yes") : string("no"))); } }, }, appbase::exec_queue::read_write); diff --git a/plugins/net_api_plugin/net_api_plugin.cpp b/plugins/net_api_plugin/net_api_plugin.cpp index bbbbec8c07..8136a9cf05 100644 --- a/plugins/net_api_plugin/net_api_plugin.cpp +++ b/plugins/net_api_plugin/net_api_plugin.cpp @@ -24,7 +24,7 @@ using namespace eosio; [&api_handle](string&&, string&& body, url_response_callback&& cb) mutable { \ try { \ INVOKE \ - cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \ + cb(http_response_code, fc::variant(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ diff --git a/plugins/producer_api_plugin/producer_api_plugin.cpp b/plugins/producer_api_plugin/producer_api_plugin.cpp index d8f9dee78f..eebecb97a1 100644 --- a/plugins/producer_api_plugin/producer_api_plugin.cpp +++ b/plugins/producer_api_plugin/producer_api_plugin.cpp @@ -25,7 +25,7 @@ using namespace eosio; [&](string&&, string&& body, url_response_callback&& cb) mutable { \ try { \ INVOKE \ - cb(http_response_code, fc::time_point::maximum(), fc::variant(result)); \ + cb(http_response_code, fc::variant(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ @@ -43,7 +43,7 @@ using namespace eosio; http_plugin::handle_exception(#api_name, #call_name, body, cb);\ }\ } else if (std::holds_alternative(result)) { \ - cb(http_response_code, fc::time_point::maximum(), fc::variant(std::get(result)));\ + cb(http_response_code, fc::variant(std::get(result)));\ } else { \ assert(0); \ } \ diff --git a/plugins/test_control_api_plugin/test_control_api_plugin.cpp b/plugins/test_control_api_plugin/test_control_api_plugin.cpp index 61dd5943ac..bae51661bb 100644 --- a/plugins/test_control_api_plugin/test_control_api_plugin.cpp +++ b/plugins/test_control_api_plugin/test_control_api_plugin.cpp @@ -30,7 +30,7 @@ void test_control_api_plugin::plugin_initialize(const variables_map&) {} try { \ auto params = parse_params(body);\ fc::variant result( api_handle.call_name( std::move(params) ) ); \ - cb(http_response_code, fc::time_point::maximum(), std::move(result)); \ + cb(http_response_code, std::move(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ diff --git a/plugins/trace_api_plugin/trace_api_plugin.cpp b/plugins/trace_api_plugin/trace_api_plugin.cpp index 3750ba50a3..67d5a63bd5 100644 --- a/plugins/trace_api_plugin/trace_api_plugin.cpp +++ b/plugins/trace_api_plugin/trace_api_plugin.cpp @@ -263,7 +263,7 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_thisreq_handler->get_block_trace(*block_number); if (resp.is_null()) { error_results results{404, "Trace API: block trace missing"}; - cb( 404, fc::time_point::maximum(), fc::variant( results )); + cb( 404, fc::variant( results )); } else { - cb( 200, fc::time_point::maximum(), std::move(resp) ); + cb( 200, std::move(resp) ); } } catch (...) { http_plugin::handle_exception("trace_api", "get_block", body, cb); @@ -308,7 +308,7 @@ struct trace_api_rpc_plugin_impl : public std::enable_shared_from_thisstore->get_trx_block_number(*trx_id, common->minimum_irreversible_history_blocks); if (!blk_num.has_value()){ error_results results{404, "Trace API: transaction id missing in the transaction id log files"}; - cb( 404, fc::time_point::maximum(), fc::variant( results )); + cb( 404, fc::variant( results )); } else { auto resp = that->req_handler->get_transaction_trace(*trx_id, *blk_num); if (resp.is_null()) { error_results results{404, "Trace API: transaction trace missing"}; - cb( 404, fc::time_point::maximum(), fc::variant( results )); + cb( 404, fc::variant( results )); } else { - cb( 200, fc::time_point::maximum(), std::move(resp) ); + cb( 200, std::move(resp) ); } } } catch (...) { diff --git a/plugins/wallet_api_plugin/wallet_api_plugin.cpp b/plugins/wallet_api_plugin/wallet_api_plugin.cpp index af592592ea..d15335784d 100644 --- a/plugins/wallet_api_plugin/wallet_api_plugin.cpp +++ b/plugins/wallet_api_plugin/wallet_api_plugin.cpp @@ -25,7 +25,7 @@ using namespace eosio; [&api_handle](string&&, string&& body, url_response_callback&& cb) mutable { \ try { \ INVOKE \ - cb(http_response_code, fc::time_point::now() + fc::days(365), fc::variant(result)); \ + cb(http_response_code, fc::variant(result)); \ } catch (...) { \ http_plugin::handle_exception(#api_name, #call_name, body, cb); \ } \ diff --git a/programs/keosd/main.cpp b/programs/keosd/main.cpp index d55d47b271..90cff48f3e 100644 --- a/programs/keosd/main.cpp +++ b/programs/keosd/main.cpp @@ -109,7 +109,7 @@ int main(int argc, char** argv) auto& http = app->get_plugin(); http.add_handler("/v1/" + keosd::config::key_store_executable_name + "/stop", [&a=app](string, string, url_response_callback cb) { - cb(200, fc::time_point::maximum(), fc::variant(fc::variant_object())); + cb(200, fc::variant(fc::variant_object())); a->quit(); }, appbase::exec_queue::read_write ); app->startup(); diff --git a/tests/chain_plugin_tests.cpp b/tests/chain_plugin_tests.cpp index 05c73db5e7..e31d96bc70 100644 --- a/tests/chain_plugin_tests.cpp +++ b/tests/chain_plugin_tests.cpp @@ -94,7 +94,7 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, validating_tester ) try { // block should be decoded successfully auto block = plugin.get_raw_block(param, fc::time_point::maximum()); auto abi_cache = plugin.get_block_serializers(block, fc::microseconds::maximum()); - std::string block_str = json::to_pretty_string(plugin.convert_block(block, abi_cache, fc::microseconds::maximum())); + std::string block_str = json::to_pretty_string(plugin.convert_block(block, abi_cache)); BOOST_TEST(block_str.find("procassert") != std::string::npos); BOOST_TEST(block_str.find("condition") != std::string::npos); BOOST_TEST(block_str.find("Should Not Assert!") != std::string::npos); @@ -114,7 +114,7 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, validating_tester ) try { // get the same block as string, results in decode failed(invalid abi) but not exception auto block2 = plugin.get_raw_block(param, fc::time_point::maximum()); auto abi_cache2 = plugin.get_block_serializers(block2, fc::microseconds::maximum()); - std::string block_str2 = json::to_pretty_string(plugin.convert_block(block2, abi_cache2, fc::microseconds::maximum())); + std::string block_str2 = json::to_pretty_string(plugin.convert_block(block2, abi_cache2)); BOOST_TEST(block_str2.find("procassert") != std::string::npos); BOOST_TEST(block_str2.find("condition") == std::string::npos); // decode failed BOOST_TEST(block_str2.find("Should Not Assert!") == std::string::npos); // decode failed From d480a26815ebf90d144e314d5df335a737e0c604 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:28:30 -0500 Subject: [PATCH 11/32] GH-1062 mutable_variant_object constructor for variant_object now explicit --- programs/cleos/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 35503113db..fcb835ab16 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -1555,7 +1555,7 @@ struct get_transaction_id_subcommand { // if actions.data & actions.hex_data provided, use the hex_data since only currently support unexploded data if( vo.contains("actions") ) { if( vo["actions"].is_array() ) { - fc::mutable_variant_object mvo = vo; + fc::mutable_variant_object mvo{vo}; fc::variants& action_variants = mvo["actions"].get_array(); for( auto& action_v : action_variants ) { if( !action_v.is_object() ) { @@ -1564,7 +1564,7 @@ struct get_transaction_id_subcommand { } fc::variant_object& action_vo = action_v.get_object(); if( action_vo.contains( "data" ) && action_vo.contains( "hex_data" ) ) { - fc::mutable_variant_object maction_vo = action_vo; + fc::mutable_variant_object maction_vo{action_vo}; maction_vo["data"] = maction_vo["hex_data"]; action_vo = maction_vo; vo = mvo; From 7e2bd04ef8cb815ed572de93ec3f8e272cf52f89 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 5 May 2023 13:30:26 -0500 Subject: [PATCH 12/32] GH-1062 Enforce processing on the main thread to http-max-response-time up to a max of 1000 items or http-max-response-time is -1. --- plugins/chain_plugin/chain_plugin.cpp | 120 ++++++++---------- .../eosio/chain_plugin/chain_plugin.hpp | 37 +++--- 2 files changed, 74 insertions(+), 83 deletions(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 716d266876..c5b6766277 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -149,7 +149,6 @@ class chain_plugin_impl { std::optional chain_config; std::optional chain; std::optional genesis; - //txn_msg_rate_limits rate_limits; std::optional wasm_runtime; fc::microseconds abi_serializer_max_time_us; std::optional snapshot_path; @@ -348,16 +347,6 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "If set to 0, no blocks are be written to the block log; block log file is removed after startup."); -// TODO: rate limiting - /*("per-authorized-account-transaction-msg-rate-limit-time-frame-sec", bpo::value()->default_value(default_per_auth_account_time_frame_seconds), - "The time frame, in seconds, that the per-authorized-account-transaction-msg-rate-limit is imposed over.") - ("per-authorized-account-transaction-msg-rate-limit", bpo::value()->default_value(default_per_auth_account), - "Limits the maximum rate of transaction messages that an account is allowed each per-authorized-account-transaction-msg-rate-limit-time-frame-sec.") - ("per-code-account-transaction-msg-rate-limit-time-frame-sec", bpo::value()->default_value(default_per_code_account_time_frame_seconds), - "The time frame, in seconds, that the per-code-account-transaction-msg-rate-limit is imposed over.") - ("per-code-account-transaction-msg-rate-limit", bpo::value()->default_value(default_per_code_account), - "Limits the maximum rate of transaction messages that an account's code is allowed each per-code-account-transaction-msg-rate-limit-time-frame-sec.")*/ - cli.add_options() ("genesis-json", bpo::value(), "File to read Genesis State from") ("genesis-timestamp", bpo::value(), "override the initial timestamp in the Genesis State file") @@ -1291,7 +1280,7 @@ read_only::get_info_results read_only::get_info(const read_only::get_info_params } read_only::get_transaction_status_results -read_only::get_transaction_status(const read_only::get_transaction_status_params& param, const fc::time_point& deadline) const { +read_only::get_transaction_status(const read_only::get_transaction_status_params& param, const fc::time_point&) const { EOS_ASSERT(trx_finality_status_proc, unsupported_feature, "Transaction Status Interface not enabled. To enable, configure nodeos with '--transaction-finality-status-max-storage-size-gb '."); trx_finality_status_processing::chain_state ch_state = trx_finality_status_proc->get_chain_state(); @@ -1338,7 +1327,7 @@ read_only::get_activated_protocol_features( const read_only::get_activated_proto return result; fc::microseconds params_time_limit = params.time_limit_ms ? fc::milliseconds(*params.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); auto walk_range = [&]( auto itr, auto end_itr, auto&& convert_iterator ) { fc::mutable_variant_object mvo; @@ -1348,18 +1337,15 @@ read_only::get_activated_protocol_features( const read_only::get_activated_proto auto& activation_ordinal_value = mvo["activation_ordinal"]; auto& activation_block_num_value = mvo["activation_block_num"]; - auto cur_time = fc::time_point::now(); - for( unsigned int count = 0; - cur_time <= params_deadline && count < params.limit && itr != end_itr; - ++itr, cur_time = fc::time_point::now() ) - { - FC_CHECK_DEADLINE(deadline); + // activated protocol features unlikely to ever reach max_return_items + for( unsigned int count = 0; count < params.limit && itr != end_itr; ++itr, ++count ) { const auto& conv_itr = convert_iterator( itr ); activation_ordinal_value = conv_itr.activation_ordinal(); activation_block_num_value = conv_itr.activation_block_num(); result.activated_protocol_features.emplace_back( conv_itr->to_variant( false, &mvo ) ); - ++count; + if (fc::time_point::now() >= params_deadline) + break; } if( itr != end_itr ) { result.more = convert_iterator( itr ).activation_ordinal() ; @@ -1608,7 +1594,7 @@ read_only::get_table_by_scope_result read_only::get_table_by_scope( const read_o const fc::time_point& deadline )const { fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); read_only::get_table_by_scope_result result; const auto& d = db.db(); @@ -1632,14 +1618,16 @@ read_only::get_table_by_scope_result read_only::get_table_by_scope( const read_o return result; auto walk_table_range = [&]( auto itr, auto end_itr ) { - auto cur_time = fc::time_point::now(); - for( unsigned int count = 0; cur_time <= params_deadline && count < p.limit && itr != end_itr; ++itr, cur_time = fc::time_point::now() ) { - FC_CHECK_DEADLINE(deadline); + uint32_t limit = p.limit; + if (deadline != fc::time_point::maximum() && limit > max_return_items) + limit = max_return_items; + for( unsigned int count = 0; count < limit && itr != end_itr; ++itr, ++count ) { if( p.table && itr->table != p.table ) continue; result.rows.push_back( {itr->code, itr->scope, itr->table, itr->payer, itr->count} ); - ++count; + if (fc::time_point::now() >= params_deadline) + break; } if( itr != end_itr ) { result.more = itr->scope.to_string(); @@ -1657,7 +1645,7 @@ read_only::get_table_by_scope_result read_only::get_table_by_scope( const read_o return result; } -vector read_only::get_currency_balance( const read_only::get_currency_balance_params& p, const fc::time_point& deadline )const { +vector read_only::get_currency_balance( const read_only::get_currency_balance_params& p, const fc::time_point& )const { const abi_def abi = eosio::chain_apis::get_abi( db, p.code ); (void)get_table_type( abi, name("accounts") ); @@ -1683,7 +1671,7 @@ vector read_only::get_currency_balance( const read_only::get_currency_bal return results; } -fc::variant read_only::get_currency_stats( const read_only::get_currency_stats_params& p, const fc::time_point& deadline )const { +fc::variant read_only::get_currency_stats( const read_only::get_currency_stats_params& p, const fc::time_point& )const { fc::mutable_variant_object results; const abi_def abi = eosio::chain_apis::get_abi( db, p.code ); @@ -1760,19 +1748,22 @@ read_only::get_producers( const read_only::get_producers_params& params, const f }(); fc::microseconds params_time_limit = params.time_limit_ms ? fc::milliseconds(*params.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); + uint32_t limit = params.limit; + if (deadline != fc::time_point::maximum() && limit > max_return_items) + limit = max_return_items; - for( ; it != secondary_index_by_secondary.end() && it->t_id == secondary_table_id->id; ++it ) { - FC_CHECK_DEADLINE(deadline); - if (result.rows.size() >= params.limit || fc::time_point::now() > params_deadline) { - result.more = name{it->primary_key}.to_string(); - break; - } + for( unsigned int count = 0; count < limit && it != secondary_index_by_secondary.end() && it->t_id == secondary_table_id->id; ++it, ++count ) { copy_inline_row(*kv_index.find(boost::make_tuple(table_id->id, it->primary_key)), data); if (params.json) result.rows.emplace_back( abis.binary_to_variant( abis.get_table_type("producers"_n), data, abi_serializer::create_yield_function( abi_serializer_max_time ), shorten_abi_errors ) ); else - result.rows.emplace_back(fc::variant(data)); + result.rows.emplace_back(data); + if (fc::time_point::now() >= params_deadline) + break; + } + if( it != secondary_index_by_secondary.end() ) { + result.more = name{it->primary_key}.to_string(); } result.total_producer_vote_weight = get_global_row(d, abi, abis, abi_serializer_max_time, shorten_abi_errors)["total_producer_vote_weight"].as_double(); @@ -1802,7 +1793,7 @@ read_only::get_producers( const read_only::get_producers_params& params, const f return result; } -read_only::get_producer_schedule_result read_only::get_producer_schedule( const read_only::get_producer_schedule_params& p, const fc::time_point& deadline ) const { +read_only::get_producer_schedule_result read_only::get_producer_schedule( const read_only::get_producer_schedule_params& p, const fc::time_point& ) const { read_only::get_producer_schedule_result result; to_variant(db.active_producers(), result.active); if(!db.pending_producers().producers.empty()) @@ -1817,7 +1808,7 @@ read_only::get_scheduled_transactions_result read_only::get_scheduled_transactions( const read_only::get_scheduled_transactions_params& p, const fc::time_point& deadline ) const { fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); const auto& d = db.db(); @@ -1852,8 +1843,9 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio auto resolver = make_resolver(db, abi_serializer::create_yield_function( abi_serializer_max_time )); uint32_t remaining = p.limit; - while (itr != idx_by_delay.end() && remaining > 0 && params_deadline > fc::time_point::now()) { - FC_CHECK_DEADLINE(deadline); + if (deadline != fc::time_point::maximum() && remaining > max_return_items) + remaining = max_return_items; + while (itr != idx_by_delay.end() && remaining > 0) { auto row = fc::mutable_variant_object() ("trx_id", itr->trx_id) ("sender", itr->sender) @@ -1881,6 +1873,8 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio result.transactions.emplace_back(std::move(row)); ++itr; remaining--; + if (fc::time_point::now() >= params_deadline) + break; } if (itr != idx_by_delay.end()) { @@ -1890,7 +1884,7 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio return result; } -chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_params& params, const fc::time_point& deadline) const { +chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_params& params, const fc::time_point&) const { signed_block_ptr block; std::optional block_num; @@ -1912,7 +1906,6 @@ chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_ } EOS_ASSERT( block, unknown_block_exception, "Could not find block: ${block}", ("block", params.block_num_or_id)); - FC_CHECK_DEADLINE(deadline); return block; } @@ -1920,17 +1913,15 @@ chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_ std::function()> read_only::get_block(const get_raw_block_params& params, const fc::time_point& deadline) const { chain::signed_block_ptr block = get_raw_block(params, deadline); - auto yield = abi_serializer::create_yield_function(deadline - fc::time_point::now()); + auto yield = abi_serializer::create_yield_function(abi_serializer_max_time); auto abi_cache = abi_serializer_cache_builder(make_resolver(db, std::move(yield))).add_serializers(block).get(); - FC_CHECK_DEADLINE(deadline); using return_type = t_or_exception; return [this, - remaining_time = deadline - fc::time_point::now(), resolver = abi_resolver(std::move(abi_cache)), block = std::move(block)]() mutable -> return_type { try { - return convert_block(block, resolver, remaining_time); + return convert_block(block, resolver); } CATCH_AND_RETURN(return_type); }; } @@ -1971,9 +1962,6 @@ read_only::get_block_header_result read_only::get_block_header(const read_only:: EOS_ASSERT( block, unknown_block_exception, "Could not find block header: ${block}", ("block", params.block_num_or_id)); return { block->calculate_id(), fc::variant{static_cast(*block)}, block->block_extensions}; } - - FC_CHECK_DEADLINE(deadline); - } abi_resolver @@ -1982,22 +1970,20 @@ read_only::get_block_serializers( const chain::signed_block_ptr& block, const fc return abi_resolver(abi_serializer_cache_builder(make_resolver(db, std::move(yield))).add_serializers(block).get()); } -fc::variant read_only::convert_block( const chain::signed_block_ptr& block, - abi_resolver& resolver, - const fc::microseconds& max_time ) const { +fc::variant read_only::convert_block( const chain::signed_block_ptr& block, abi_resolver& resolver ) const { fc::variant pretty_output; - abi_serializer::to_variant( *block, pretty_output, resolver, abi_serializer::create_yield_function( max_time ) ); + abi_serializer::to_variant( *block, pretty_output, resolver, abi_serializer::create_yield_function( abi_serializer_max_time ) ); const auto block_id = block->calculate_id(); uint32_t ref_block_prefix = block_id._hash[1]; - return fc::mutable_variant_object( pretty_output.get_object() ) + return fc::mutable_variant_object( std::move(pretty_output.get_object()) ) ( "id", block_id ) ( "block_num", block->block_num() ) ( "ref_block_prefix", ref_block_prefix ); } -fc::variant read_only::get_block_info(const read_only::get_block_info_params& params, const fc::time_point& deadline) const { +fc::variant read_only::get_block_info(const read_only::get_block_info_params& params, const fc::time_point&) const { signed_block_ptr block; try { @@ -2026,7 +2012,7 @@ fc::variant read_only::get_block_info(const read_only::get_block_info_params& pa ("ref_block_prefix", ref_block_prefix); } -fc::variant read_only::get_block_header_state(const get_block_header_state_params& params, const fc::time_point& deadline) const { +fc::variant read_only::get_block_header_state(const get_block_header_state_params& params, const fc::time_point&) const { block_state_ptr b; std::optional block_num; std::exception_ptr e; @@ -2276,7 +2262,7 @@ void read_write::send_transaction2(read_write::send_transaction2_params params, return send_transaction_gen(*this, std::move(gen_params), std::move(next)); } -read_only::get_abi_results read_only::get_abi( const get_abi_params& params, const fc::time_point& deadline )const { +read_only::get_abi_results read_only::get_abi( const get_abi_params& params, const fc::time_point& )const { try { get_abi_results result; result.account_name = params.account_name; @@ -2291,7 +2277,7 @@ read_only::get_abi_results read_only::get_abi( const get_abi_params& params, con } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account abi") } -read_only::get_code_results read_only::get_code( const get_code_params& params, const fc::time_point& deadline )const { +read_only::get_code_results read_only::get_code( const get_code_params& params, const fc::time_point& )const { try { get_code_results result; result.account_name = params.account_name; @@ -2315,7 +2301,7 @@ read_only::get_code_results read_only::get_code( const get_code_params& params, } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account code") } -read_only::get_code_hash_results read_only::get_code_hash( const get_code_hash_params& params, const fc::time_point& deadline )const { +read_only::get_code_hash_results read_only::get_code_hash( const get_code_hash_params& params, const fc::time_point& )const { try { get_code_hash_results result; result.account_name = params.account_name; @@ -2329,7 +2315,7 @@ read_only::get_code_hash_results read_only::get_code_hash( const get_code_hash_p } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account code hash") } -read_only::get_raw_code_and_abi_results read_only::get_raw_code_and_abi( const get_raw_code_and_abi_params& params, const fc::time_point& deadline)const { +read_only::get_raw_code_and_abi_results read_only::get_raw_code_and_abi( const get_raw_code_and_abi_params& params, const fc::time_point& )const { try { get_raw_code_and_abi_results result; result.account_name = params.account_name; @@ -2347,7 +2333,7 @@ read_only::get_raw_code_and_abi_results read_only::get_raw_code_and_abi( const g } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account code/abi") } -read_only::get_raw_abi_results read_only::get_raw_abi( const get_raw_abi_params& params, const fc::time_point& deadline )const { +read_only::get_raw_abi_results read_only::get_raw_abi( const get_raw_abi_params& params, const fc::time_point& )const { try { get_raw_abi_results result; result.account_name = params.account_name; @@ -2365,7 +2351,7 @@ read_only::get_raw_abi_results read_only::get_raw_abi( const get_raw_abi_params& } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account abi") } -read_only::get_account_return_t read_only::get_account( const get_account_params& params, const fc::time_point& deadline ) const { +read_only::get_account_return_t read_only::get_account( const get_account_params& params, const fc::time_point& ) const { try { get_account_results result; result.account_name = params.account_name; @@ -2407,8 +2393,8 @@ read_only::get_account_return_t read_only::get_account( const get_account_params std::multimap result; while (iter != links.end() && iter->account == params.account_name ) { - auto action = iter->message_type.empty() ? std::optional() : std::optional(iter->message_type); - result.emplace(std::make_pair(iter->required_permission, linked_action{iter->code, std::move(action)})); + auto action_name = iter->message_type.empty() ? std::optional() : std::optional(iter->message_type); + result.emplace(iter->required_permission, linked_action{iter->code, action_name}); ++iter; } @@ -2528,7 +2514,7 @@ read_only::get_account_return_t read_only::get_account( const get_account_params } EOS_RETHROW_EXCEPTIONS(chain::account_query_exception, "unable to retrieve account info") } -read_only::get_required_keys_result read_only::get_required_keys( const get_required_keys_params& params, const fc::time_point& deadline )const { +read_only::get_required_keys_result read_only::get_required_keys( const get_required_keys_params& params, const fc::time_point& )const { transaction pretty_input; auto resolver = make_resolver(db, abi_serializer::create_yield_function( abi_serializer_max_time )); try { @@ -2559,13 +2545,13 @@ void read_only::send_read_only_transaction(send_read_only_transaction_params par return send_transaction_gen(*this, std::move(gen_params), std::move(next)); } -read_only::get_transaction_id_result read_only::get_transaction_id( const read_only::get_transaction_id_params& params, const fc::time_point& deadline) const { +read_only::get_transaction_id_result read_only::get_transaction_id( const read_only::get_transaction_id_params& params, const fc::time_point& ) const { return params.id(); } account_query_db::get_accounts_by_authorizers_result -read_only::get_accounts_by_authorizers( const account_query_db::get_accounts_by_authorizers_params& args, const fc::time_point& deadline) const +read_only::get_accounts_by_authorizers( const account_query_db::get_accounts_by_authorizers_params& args, const fc::time_point& ) const { EOS_ASSERT(aqdb.has_value(), plugin_config_exception, "Account Queries being accessed when not enabled"); return aqdb->get_accounts_by_authorizers(args); @@ -2611,7 +2597,7 @@ chain::symbol read_only::extract_core_symbol()const { } read_only::get_consensus_parameters_results -read_only::get_consensus_parameters(const get_consensus_parameters_params&, const fc::time_point& deadline ) const { +read_only::get_consensus_parameters(const get_consensus_parameters_params&, const fc::time_point& ) const { get_consensus_parameters_results results; results.chain_config = db.get_global_properties().configuration; diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index 82fd082e48..28560396aa 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -21,6 +21,7 @@ #include #include +#include namespace fc { class variant; } @@ -92,6 +93,7 @@ class read_write; class api_base { public: + static constexpr uint32_t max_return_items = 1000; static void handle_db_exhaustion(); static void handle_bad_alloc(); @@ -147,7 +149,8 @@ class read_only : public api_base { // return deadline for call fc::time_point start() const { validate(); - return fc::time_point::now() + http_max_response_time; + return http_max_response_time == fc::microseconds::maximum() ? fc::time_point::maximum() + : fc::time_point::now() + http_max_response_time; } void set_shorten_abi_errors( bool f ) { shorten_abi_errors = f; } @@ -371,8 +374,7 @@ class read_only : public api_base { // call from any thread fc::variant convert_block( const chain::signed_block_ptr& block, - abi_resolver& resolver, - const fc::microseconds& max_time ) const; + abi_resolver& resolver ) const; struct get_block_header_params { string block_num_or_id; @@ -564,7 +566,7 @@ class read_only : public api_base { ConvFn conv ) const { fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); struct http_params_t { name table; @@ -632,16 +634,17 @@ class read_only : public api_base { }; auto walk_table_row_range = [&]( auto itr, auto end_itr ) { - auto cur_time = fc::time_point::now(); vector data; - for( unsigned int count = 0; - cur_time <= params_deadline && count < p.limit && itr != end_itr; - ++count, ++itr, cur_time = fc::time_point::now() ) { - FC_CHECK_DEADLINE(deadline); + uint32_t limit = p.limit; + if (deadline != fc::time_point::maximum() && limit > max_return_items) + limit = max_return_items; + for( unsigned int count = 0; count < limit && itr != end_itr; ++count, ++itr ) { const auto* itr2 = d.find( boost::make_tuple(t_id->id, itr->primary_key) ); if( itr2 == nullptr ) continue; copy_inline_row(*itr2, data); http_params.rows.emplace_back(std::move(data), itr->payer); + if (fc::time_point::now() >= params_deadline) + break; } if( itr != end_itr ) { http_params.more = true; @@ -696,7 +699,7 @@ class read_only : public api_base { const fc::time_point& deadline ) const { fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = fc::time_point::now() + params_time_limit; + fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); struct http_params_t { name table; @@ -746,14 +749,15 @@ class read_only : public api_base { }; auto walk_table_row_range = [&]( auto itr, auto end_itr ) { - auto cur_time = fc::time_point::now(); vector data; - for( unsigned int count = 0; - cur_time <= params_deadline && count < p.limit && itr != end_itr; - ++count, ++itr, cur_time = fc::time_point::now() ) { - FC_CHECK_DEADLINE(deadline); + uint32_t limit = p.limit; + if (deadline != fc::time_point::maximum() && limit > max_return_items) + limit = max_return_items; + for( unsigned int count = 0; count < limit && itr != end_itr; ++count, ++itr ) { copy_inline_row(*itr, data); http_params.rows.emplace_back(std::move(data), itr->payer); + if (fc::time_point::now() >= params_deadline) + break; } if( itr != end_itr ) { http_params.more = true; @@ -832,7 +836,8 @@ class read_write : public api_base { // return deadline for call fc::time_point start() const { validate(); - return fc::time_point::now() + http_max_response_time; + return http_max_response_time == fc::microseconds::maximum() ? fc::time_point::maximum() + : fc::time_point::now() + http_max_response_time; } using push_block_params = chain::signed_block; From a7c5d3c04869468cc76db4cb05ac03278f161a22 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 6 May 2023 08:17:37 -0500 Subject: [PATCH 13/32] GH-1062 Avoid unneeded copies --- libraries/chain/include/eosio/chain/abi_serializer.hpp | 2 +- libraries/libfc/include/fc/io/json_relaxed.hpp | 2 +- libraries/libfc/include/fc/variant_object.hpp | 2 +- libraries/libfc/src/exception.cpp | 2 +- libraries/libfc/src/io/json.cpp | 2 +- libraries/libfc/src/log/log_message.cpp | 2 +- plugins/chain_plugin/chain_plugin.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 8e8688dd84..627b04d5ba 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -664,7 +664,7 @@ namespace impl { for (auto feature : new_protocol_features) { mutable_variant_object feature_mvo; add(feature_mvo, "feature_digest", feature, resolver, ctx); - pf_array.push_back(feature_mvo); + pf_array.push_back(std::move(feature_mvo)); } mvo("new_protocol_features", pf_array); } diff --git a/libraries/libfc/include/fc/io/json_relaxed.hpp b/libraries/libfc/include/fc/io/json_relaxed.hpp index 3983e783dc..9362a7a442 100644 --- a/libraries/libfc/include/fc/io/json_relaxed.hpp +++ b/libraries/libfc/include/fc/io/json_relaxed.hpp @@ -606,7 +606,7 @@ namespace fc { namespace json_relaxed in.get(); return obj; } - FC_THROW_EXCEPTION( parse_error_exception, "Expected '}' after ${variant}", ("variant", obj ) ); + FC_THROW_EXCEPTION( parse_error_exception, "Expected '}' after ${variant}", ("variant", std::move(obj) ) ); } catch( const fc::eof_exception& e ) { diff --git a/libraries/libfc/include/fc/variant_object.hpp b/libraries/libfc/include/fc/variant_object.hpp index 9c143294b3..ee4b8550b5 100644 --- a/libraries/libfc/include/fc/variant_object.hpp +++ b/libraries/libfc/include/fc/variant_object.hpp @@ -212,7 +212,7 @@ namespace fc explicit mutable_variant_object( T&& v ) :_key_value( new std::vector() ) { - *this = variant(fc::forward(v)).get_object(); + *this = std::move(variant(fc::forward(v)).get_object()); } mutable_variant_object(); diff --git a/libraries/libfc/src/exception.cpp b/libraries/libfc/src/exception.cpp index b715f54c4a..6231f83291 100644 --- a/libraries/libfc/src/exception.cpp +++ b/libraries/libfc/src/exception.cpp @@ -133,7 +133,7 @@ namespace fc } void from_variant( const variant& v, exception& ll ) { - auto obj = v.get_object(); + const auto& obj = v.get_object(); if( obj.contains( "stack" ) ) ll.my->_elog = obj["stack"].as(); if( obj.contains( "code" ) ) diff --git a/libraries/libfc/src/io/json.cpp b/libraries/libfc/src/io/json.cpp index e7ed058995..b731e9ffbd 100644 --- a/libraries/libfc/src/io/json.cpp +++ b/libraries/libfc/src/io/json.cpp @@ -206,7 +206,7 @@ namespace fc in.get(); return obj; } - FC_THROW_EXCEPTION( parse_error_exception, "Expected '}' after ${variant}", ("variant", obj ) ); + FC_THROW_EXCEPTION( parse_error_exception, "Expected '}' after ${variant}", ("variant", std::move(obj) ) ); } catch( const fc::eof_exception& e ) { diff --git a/libraries/libfc/src/log/log_message.cpp b/libraries/libfc/src/log/log_message.cpp index a43bb4afb2..1c99402cef 100644 --- a/libraries/libfc/src/log/log_message.cpp +++ b/libraries/libfc/src/log/log_message.cpp @@ -57,7 +57,7 @@ namespace fc log_context::log_context( const variant& v ) :my( std::make_shared() ) { - auto obj = v.get_object(); + const auto& obj = v.get_object(); my->level = obj["level"].as(); my->file = obj["file"].as_string(); my->line = obj["line"].as_uint64(); diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index c5b6766277..9012c44f69 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -2111,7 +2111,7 @@ void read_write::push_transaction(const read_write::push_transaction_params& par fc::mutable_variant_object output_mvo(output); output_mvo["action_traces"] = convert_act_trace_to_tree_struct(0); - output = output_mvo; + output = std::move(output_mvo); } catch( chain::abi_exception& ) { output = *trx_trace_ptr; } From 0966651a9c8d05f5a0b3668852f3c561b011233a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 May 2023 09:37:46 -0500 Subject: [PATCH 14/32] GH-1062 Fix issue with variant internal variant_object being shared --- libraries/libfc/include/fc/variant_object.hpp | 10 ++++++++-- tests/trx_generator/trx_generator.cpp | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/libfc/include/fc/variant_object.hpp b/libraries/libfc/include/fc/variant_object.hpp index ee4b8550b5..12e5aeccd8 100644 --- a/libraries/libfc/include/fc/variant_object.hpp +++ b/libraries/libfc/include/fc/variant_object.hpp @@ -206,9 +206,15 @@ namespace fc ///@} + explicit mutable_variant_object( variant v ) + :_key_value( new std::vector() ) + { + *this = v.get_object(); + } + template>::value>> + typename = std::enable_if_t>::value && + !std::is_base_of>::value>> explicit mutable_variant_object( T&& v ) :_key_value( new std::vector() ) { diff --git a/tests/trx_generator/trx_generator.cpp b/tests/trx_generator/trx_generator.cpp index 2365bf6953..41f56ab79c 100644 --- a/tests/trx_generator/trx_generator.cpp +++ b/tests/trx_generator/trx_generator.cpp @@ -152,7 +152,7 @@ namespace eosio::testing { void update_key_word_fields_in_sub_action(const std::string& key, fc::mutable_variant_object& action_mvo, const std::string& action_inner_key, const std::string& key_word) { if (action_mvo.find(action_inner_key) != action_mvo.end()) { - auto inner = action_mvo[action_inner_key].get_object(); + const auto& inner = action_mvo[action_inner_key].get_object(); if (inner.find(key) != inner.end()) { fc::mutable_variant_object inner_mvo = fc::mutable_variant_object(inner); inner_mvo.set(key, key_word); @@ -235,7 +235,7 @@ namespace eosio::testing { const std::string gen_acct_name_per_trx("ACCT_PER_TRX"); - auto action_array = unpacked_actions_data_json.get_array(); + const auto& action_array = unpacked_actions_data_json.get_array(); _unpacked_actions.reserve(action_array.size()); std::transform(action_array.begin(), action_array.end(), std::back_inserter(_unpacked_actions), [&](const auto& var) { From 05ff75236dc755de87e0ee3cafd5702db16103a8 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 May 2023 09:38:58 -0500 Subject: [PATCH 15/32] GH-1062 Need to copy yield function since it is a const& --- libraries/chain/abi_serializer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index cc30749503..2daf186b3d 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -24,7 +24,7 @@ namespace eosio { namespace chain { template inline fc::variant variant_from_stream(fc::datastream& stream, const abi_serializer::yield_function_t& yield) { yield(0); // reset deadline - fc::yield_function_t y = [&yield](){ yield(1); }; // create yield function matching fc::variant requirements, 0 for recursive depth + fc::yield_function_t y = [yield](){ yield(1); }; // create yield function matching fc::variant requirements, 0 for recursive depth T temp; fc::raw::unpack( stream, temp ); y(); From 0ef8b0960b563a192453d7bdb185aa43d0f94368 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 May 2023 09:45:28 -0500 Subject: [PATCH 16/32] GH-1062 Only reset deadline when explicitly asked to do so. --- libraries/chain/include/eosio/chain/abi_serializer.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 627b04d5ba..0a993dd8ea 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -126,10 +126,10 @@ struct abi_serializer { EOS_ASSERT( recursion_depth < max_recursion_depth, abi_recursion_depth_exception, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) ); - auto prev_call = last_call; - last_call = fc::time_point::now(); + auto now = fc::time_point::now(); // recursion_depth 0 is flag used to indicate a reset of deadline - EOS_ASSERT( recursion_depth == 0 || (last_call - prev_call) < max_serialization_time, abi_serialization_deadline_exception, + if (recursion_depth == 0) last_call = now; + EOS_ASSERT( (now - last_call) < max_serialization_time, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) ); }; } From 0eec87e9e63dd9857ec5714d4be423f7ca0b013f Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 8 May 2023 14:33:46 -0500 Subject: [PATCH 17/32] GH-1062 small optimization --- plugins/chain_plugin/chain_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 9012c44f69..14c99f9ec0 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -2108,7 +2108,7 @@ void read_write::push_transaction(const read_write::push_transaction_params& par return restructured_act_traces; }; - fc::mutable_variant_object output_mvo(output); + fc::mutable_variant_object output_mvo(std::move(output.get_object())); output_mvo["action_traces"] = convert_act_trace_to_tree_struct(0); output = std::move(output_mvo); From 1d33a6885ee1d011cb7b4da8b96ddaef87f233cc Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 10 May 2023 06:57:06 -0500 Subject: [PATCH 18/32] GH-1062 Add variant_object to the exclude list of universal reference constructor of mutable_variant_object since there is an explicit constructor for it. --- libraries/libfc/include/fc/variant_object.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/libfc/include/fc/variant_object.hpp b/libraries/libfc/include/fc/variant_object.hpp index 12e5aeccd8..e687a5a50c 100644 --- a/libraries/libfc/include/fc/variant_object.hpp +++ b/libraries/libfc/include/fc/variant_object.hpp @@ -214,7 +214,8 @@ namespace fc template>::value && - !std::is_base_of>::value>> + !std::is_base_of>::value && + !std::is_base_of>::value>> explicit mutable_variant_object( T&& v ) :_key_value( new std::vector() ) { From 724be07d50eebca8b32ce088e37c819c56d01560 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 10 May 2023 17:21:37 -0500 Subject: [PATCH 19/32] GH-1062 Also guard against underflow --- libraries/libfc/include/fc/time.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/libfc/include/fc/time.hpp b/libraries/libfc/include/fc/time.hpp index 7e2c88c51c..53a284d788 100644 --- a/libraries/libfc/include/fc/time.hpp +++ b/libraries/libfc/include/fc/time.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include #ifdef _MSC_VER #pragma warning (push) @@ -49,12 +50,14 @@ namespace fc { std::string to_iso_string()const; static time_point from_iso_string( const std::string& s ); - // protect against overflow + // protect against overflow/underflow constexpr time_point& safe_add( const microseconds& m ) { if (m.count() > 0 && elapsed > fc::microseconds::maximum() - m) { elapsed = microseconds::maximum(); - } else { // does not guard against underflow - elapsed += m; + } else if (m.count() < 0 && elapsed.count() < std::numeric_limits::min() - m.count()) { + elapsed = microseconds(std::numeric_limits::min()); + } else { + elapsed += m; } return *this; } From 6096c0bfc1f813847ec19433020a43ee121badcb Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 10 May 2023 17:31:46 -0500 Subject: [PATCH 20/32] GH-1062 Refactor abi_serializer to accept a max_action_data_serialization_time which applies only to action data deserialization --- libraries/chain/abi_serializer.cpp | 37 ++- .../include/eosio/chain/abi_serializer.hpp | 110 ++++---- .../chain/include/eosio/chain/controller.hpp | 20 -- plugins/chain_plugin/chain_plugin.cpp | 36 ++- .../eosio/chain_plugin/chain_plugin.hpp | 32 ++- plugins/chain_plugin/trx_retry_db.cpp | 6 +- programs/cleos/main.cpp | 4 +- programs/leap-util/actions/blocklog.cpp | 3 +- unittests/abi_tests.cpp | 247 +++++++++++++++--- unittests/wasm_tests.cpp | 3 + 10 files changed, 345 insertions(+), 153 deletions(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index 2daf186b3d..2235dbf1f7 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace eosio { namespace chain { @@ -130,7 +131,7 @@ namespace eosio { namespace chain { } void abi_serializer::set_abi(abi_def abi, const yield_function_t& yield) { - impl::abi_traverse_context ctx(yield); + impl::abi_traverse_context ctx(yield, fc::microseconds{}); EOS_ASSERT(starts_with(abi.version, "eosio::abi/1."), unsupported_abi_version_exception, "ABI has an unsupported version"); @@ -236,7 +237,7 @@ namespace eosio { namespace chain { } bool abi_serializer::is_type(const std::string_view& type, const yield_function_t& yield)const { - impl::abi_traverse_context ctx(yield); + impl::abi_traverse_context ctx(yield, fc::microseconds{}); return _is_type(type, ctx); } @@ -465,23 +466,27 @@ namespace eosio { namespace chain { } fc::variant abi_serializer::binary_to_variant( const std::string_view& type, const bytes& binary, const yield_function_t& yield, bool short_path )const { - impl::binary_to_variant_context ctx(*this, yield, type); + impl::binary_to_variant_context ctx(*this, yield, fc::microseconds{}, type); ctx.short_path = short_path; return _binary_to_variant(type, binary, ctx); } - fc::variant abi_serializer::binary_to_variant( const std::string_view& type, const bytes& binary, const fc::microseconds& max_serialization_time, bool short_path )const { - return binary_to_variant( type, binary, create_yield_function(max_serialization_time), short_path ); + fc::variant abi_serializer::binary_to_variant( const std::string_view& type, const bytes& binary, const fc::microseconds& max_action_data_serialization_time, bool short_path )const { + impl::binary_to_variant_context ctx(*this, create_depth_yield_function(), max_action_data_serialization_time, type); + ctx.short_path = short_path; + return _binary_to_variant(type, binary, ctx); } fc::variant abi_serializer::binary_to_variant( const std::string_view& type, fc::datastream& binary, const yield_function_t& yield, bool short_path )const { - impl::binary_to_variant_context ctx(*this, yield, type); + impl::binary_to_variant_context ctx(*this, yield, fc::microseconds{}, type); ctx.short_path = short_path; return _binary_to_variant(type, binary, ctx); } - fc::variant abi_serializer::binary_to_variant( const std::string_view& type, fc::datastream& binary, const fc::microseconds& max_serialization_time, bool short_path )const { - return binary_to_variant( type, binary, create_yield_function(max_serialization_time), short_path ); + fc::variant abi_serializer::binary_to_variant( const std::string_view& type, fc::datastream& binary, const fc::microseconds& max_action_data_serialization_time, bool short_path )const { + impl::binary_to_variant_context ctx(*this, create_depth_yield_function(), max_action_data_serialization_time, type); + ctx.short_path = short_path; + return _binary_to_variant(type, binary, ctx); } void abi_serializer::_variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, impl::variant_to_binary_context& ctx )const @@ -604,23 +609,27 @@ namespace eosio { namespace chain { } FC_CAPTURE_AND_RETHROW() } bytes abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, const yield_function_t& yield, bool short_path )const { - impl::variant_to_binary_context ctx(*this, yield, type); + impl::variant_to_binary_context ctx(*this, yield, fc::microseconds{}, type); ctx.short_path = short_path; return _variant_to_binary(type, var, ctx); } - bytes abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, const fc::microseconds& max_serialization_time, bool short_path ) const { - return variant_to_binary( type, var, create_yield_function(max_serialization_time), short_path ); + bytes abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, const fc::microseconds& max_action_data_serialization_time, bool short_path ) const { + impl::variant_to_binary_context ctx(*this, create_depth_yield_function(), max_action_data_serialization_time, type); + ctx.short_path = short_path; + return _variant_to_binary(type, var, ctx); } void abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, const yield_function_t& yield, bool short_path )const { - impl::variant_to_binary_context ctx(*this, yield, type); + impl::variant_to_binary_context ctx(*this, yield, fc::microseconds{}, type); ctx.short_path = short_path; _variant_to_binary(type, var, ds, ctx); } - void abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, const fc::microseconds& max_serialization_time, bool short_path ) const { - variant_to_binary( type, var, create_yield_function(max_serialization_time), short_path ); + void abi_serializer::variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, const fc::microseconds& max_action_data_serialization_time, bool short_path ) const { + impl::variant_to_binary_context ctx(*this, create_depth_yield_function(), max_action_data_serialization_time, type); + ctx.short_path = short_path; + _variant_to_binary(type, var, ds, ctx); } type_name abi_serializer::get_action_type(name action)const { diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 0a993dd8ea..6467dc52e8 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace eosio::chain { @@ -48,7 +49,6 @@ struct abi_serializer { bool is_szarray(const std::string_view& type)const; bool is_optional(const std::string_view& type)const; bool is_type( const std::string_view& type, const yield_function_t& yield )const; - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] bool is_type(const std::string_view& type, const fc::microseconds& max_serialization_time)const; bool is_builtin_type(const std::string_view& type)const; bool is_integer(const std::string_view& type) const; @@ -67,33 +67,29 @@ struct abi_serializer { std::optional get_error_message( uint64_t error_code )const; fc::variant binary_to_variant( const std::string_view& type, const bytes& binary, const yield_function_t& yield, bool short_path = false )const; - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] - fc::variant binary_to_variant( const std::string_view& type, const bytes& binary, const fc::microseconds& max_serialization_time, bool short_path = false )const; + fc::variant binary_to_variant( const std::string_view& type, const bytes& binary, const fc::microseconds& max_action_data_serialization_time, bool short_path = false )const; fc::variant binary_to_variant( const std::string_view& type, fc::datastream& binary, const yield_function_t& yield, bool short_path = false )const; - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] - fc::variant binary_to_variant( const std::string_view& type, fc::datastream& binary, const fc::microseconds& max_serialization_time, bool short_path = false )const; + fc::variant binary_to_variant( const std::string_view& type, fc::datastream& binary, const fc::microseconds& max_action_data_serialization_time, bool short_path = false )const; - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] - bytes variant_to_binary( const std::string_view& type, const fc::variant& var, const fc::microseconds& max_serialization_time, bool short_path = false )const; + bytes variant_to_binary( const std::string_view& type, const fc::variant& var, const fc::microseconds& max_action_data_serialization_time, bool short_path = false )const; bytes variant_to_binary( const std::string_view& type, const fc::variant& var, const yield_function_t& yield, bool short_path = false )const; - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] - void variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, const fc::microseconds& max_serialization_time, bool short_path = false )const; + void variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, const fc::microseconds& max_action_data_serialization_time, bool short_path = false )const; void variant_to_binary( const std::string_view& type, const fc::variant& var, fc::datastream& ds, const yield_function_t& yield, bool short_path = false )const; template static void to_variant( const T& o, fc::variant& vo, const Resolver& resolver, const yield_function_t& yield ); template - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] - static void to_variant( const T& o, fc::variant& vo, const Resolver& resolver, const fc::microseconds& max_serialization_time ); + static void to_variant( const T& o, fc::variant& vo, const Resolver& resolver, const fc::microseconds& max_action_data_serialization_time ); template static void to_log_variant( const T& o, fc::variant& vo, const Resolver& resolver, const yield_function_t& yield ); + template + static void to_log_variant( const T& o, fc::variant& vo, const Resolver& resolver, const fc::microseconds& max_action_data_serialization_time ); template static void from_variant( const fc::variant& v, T& o, const Resolver& resolver, const yield_function_t& yield ); template - [[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]] - static void from_variant( const fc::variant& v, T& o, const Resolver& resolver, const fc::microseconds& max_serialization_time ); + static void from_variant( const fc::variant& v, T& o, const Resolver& resolver, const fc::microseconds& max_action_data_serialization_time ); template static bool is_empty_abi(const Vec& abi_vec) @@ -120,20 +116,26 @@ struct abi_serializer { static constexpr size_t max_recursion_depth = 32; // arbitrary depth to prevent infinite recursion // create standard yield function that checks for max_serialization_time and max_recursion_depth. + // restricts serialization time from creation of yield function until serialization is complete. // now() deadline captured at time of this call static yield_function_t create_yield_function(const fc::microseconds& max_serialization_time) { - return [max_serialization_time, last_call=fc::time_point::now()](size_t recursion_depth) mutable { + fc::time_point deadline = fc::time_point::now().safe_add(max_serialization_time); + return [max_serialization_time, deadline](size_t recursion_depth) { EOS_ASSERT( recursion_depth < max_recursion_depth, abi_recursion_depth_exception, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) ); - auto now = fc::time_point::now(); - // recursion_depth 0 is flag used to indicate a reset of deadline - if (recursion_depth == 0) last_call = now; - EOS_ASSERT( (now - last_call) < max_serialization_time, abi_serialization_deadline_exception, + EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) ); }; } + static yield_function_t create_depth_yield_function() { + return [](size_t recursion_depth) { + EOS_ASSERT( recursion_depth < max_recursion_depth, abi_recursion_depth_exception, + "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) ); + }; + } + private: map> typedefs; @@ -169,8 +171,9 @@ struct abi_serializer { namespace impl { const static size_t hex_log_max_size = 64; struct abi_traverse_context { - explicit abi_traverse_context( abi_serializer::yield_function_t yield ) + abi_traverse_context( abi_serializer::yield_function_t yield, fc::microseconds max_action_data_serialization ) : yield(std::move( yield )) + , max_action_serialization_time(max_action_data_serialization) { } @@ -179,12 +182,24 @@ namespace impl { void check_deadline()const { yield( recursion_depth ); } abi_serializer::yield_function_t get_yield_function() { return yield; } - void reset_deadline() { yield(0); } + + void start_action_serialization() { + if (max_action_serialization_time.count() > 0) { + fc::time_point deadline = fc::time_point::now().safe_add(max_action_serialization_time); + yield = [deadline, y=yield, max=max_action_serialization_time](size_t depth) { + y(depth); // call provided yield that might include an overall time limit or not + EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, + "serialization action data time limit ${t}us exceeded", ("t", max) ); + }; + } + } fc::scoped_exit> enter_scope(); protected: abi_serializer::yield_function_t yield; + // if set then restricts each individual action data serialization + fc::microseconds max_action_serialization_time; size_t recursion_depth = 1; bool log = false; }; @@ -224,8 +239,8 @@ namespace impl { using path_item = std::variant; struct abi_traverse_context_with_path : public abi_traverse_context { - abi_traverse_context_with_path( const abi_serializer& abis, abi_serializer::yield_function_t yield, const std::string_view& type ) - : abi_traverse_context( std::move( yield ) ), abis(abis) + abi_traverse_context_with_path( const abi_serializer& abis, abi_serializer::yield_function_t yield, fc::microseconds max_action_data_serialization_time, const std::string_view& type ) + : abi_traverse_context( std::move( yield ), max_action_data_serialization_time ), abis(abis) { set_path_root(type); } @@ -491,6 +506,7 @@ namespace impl { try { binary_to_variant_context _ctx(abi, ctx, type); _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place + _ctx.start_action_serialization(); mvo( "data", abi._binary_to_variant( type, act.data, _ctx )); } catch(...) { // any failure to serialize data, then leave as not serialized @@ -506,7 +522,6 @@ namespace impl { set_hex_data(mvo, "data", act.data); } set_hex_data(mvo, "hex_data", act.data); - ctx.reset_deadline(); out(name, std::move(mvo)); } @@ -523,7 +538,6 @@ namespace impl { static void add( mutable_variant_object& out, const char* name, const action_trace& act_trace, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 17); - ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; @@ -541,11 +555,9 @@ namespace impl { mvo("block_time", act_trace.block_time); mvo("producer_block_id", act_trace.producer_block_id); mvo("account_ram_deltas", act_trace.account_ram_deltas); - ctx.reset_deadline(); mvo("except", act_trace.except); mvo("error_code", act_trace.error_code); - ctx.reset_deadline(); mvo("return_value_hex_data", act_trace.return_value); auto act = act_trace.act; try { @@ -554,9 +566,9 @@ namespace impl { const abi_serializer& abi = *abi_optional; auto type = abi.get_action_result_type(act.name); if (!type.empty()) { - ctx.reset_deadline(); binary_to_variant_context _ctx(abi, ctx, type); _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place + _ctx.start_action_serialization(); mvo( "return_value_data", abi._binary_to_variant( type, act_trace.return_value, _ctx )); } } @@ -577,23 +589,16 @@ namespace impl { static void add( mutable_variant_object &out, const char* name, const packed_transaction& ptrx, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 4); - ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; auto trx = ptrx.get_transaction(); mvo("id", trx.id()); - ctx.reset_deadline(); mvo("signatures", ptrx.get_signatures()); - ctx.reset_deadline(); mvo("compression", ptrx.get_compression()); - ctx.reset_deadline(); mvo("packed_context_free_data", ptrx.get_packed_context_free_data()); - ctx.reset_deadline(); mvo("context_free_data", ptrx.get_context_free_data()); - ctx.reset_deadline(); if( !ctx.is_logging() ) mvo("packed_trx", ptrx.get_packed_transaction()); - ctx.reset_deadline(); add(mvo, "transaction", trx, resolver, ctx); out(name, std::move(mvo)); @@ -608,7 +613,6 @@ namespace impl { static void add( mutable_variant_object &out, const char* name, const transaction& trx, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 9); - ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; mvo("expiration", trx.expiration); @@ -617,9 +621,7 @@ namespace impl { mvo("max_net_usage_words", trx.max_net_usage_words); mvo("max_cpu_usage_ms", trx.max_cpu_usage_ms); mvo("delay_sec", trx.delay_sec); - ctx.reset_deadline(); add(mvo, "context_free_actions", trx.context_free_actions, resolver, ctx); - ctx.reset_deadline(); add(mvo, "actions", trx.actions, resolver, ctx); // process contents of block.transaction_extensions @@ -642,7 +644,6 @@ namespace impl { static void add( mutable_variant_object &out, const char* name, const signed_block& block, const Resolver& resolver, abi_traverse_context& ctx ) { static_assert(fc::reflector::total_member_count == 12); - ctx.reset_deadline(); auto h = ctx.enter_scope(); mutable_variant_object mvo; mvo("timestamp", block.timestamp); @@ -674,9 +675,7 @@ namespace impl { mvo("new_producer_schedule", new_producer_schedule); } - ctx.reset_deadline(); mvo("producer_signature", block.producer_signature); - ctx.reset_deadline(); add(mvo, "transactions", block.transactions, resolver, ctx); // process contents of block.block_extensions @@ -687,7 +686,6 @@ namespace impl { mvo("additional_signatures", additional_signatures); } - ctx.reset_deadline(); out(name, std::move(mvo)); } }; @@ -958,36 +956,48 @@ namespace impl { template void abi_serializer::to_variant( const T& o, fc::variant& vo, const Resolver& resolver, const yield_function_t& yield ) try { mutable_variant_object mvo; - impl::abi_traverse_context ctx( yield ); + impl::abi_traverse_context ctx( yield, fc::microseconds{} ); impl::abi_to_variant::add(mvo, "_", o, resolver, ctx); vo = std::move(mvo["_"]); } FC_RETHROW_EXCEPTIONS(error, "Failed to serialize: ${type}", ("type", boost::core::demangle( typeid(o).name() ) )) template -void abi_serializer::to_variant( const T& o, fc::variant& vo, const Resolver& resolver, const fc::microseconds& max_serialization_time ) { - to_variant( o, vo, resolver, create_yield_function(max_serialization_time) ); -} +void abi_serializer::to_variant( const T& o, fc::variant& vo, const Resolver& resolver, const fc::microseconds& max_action_data_serialization_time ) try { + mutable_variant_object mvo; + impl::abi_traverse_context ctx( create_depth_yield_function(), max_action_data_serialization_time ); + impl::abi_to_variant::add(mvo, "_", o, resolver, ctx); + vo = std::move(mvo["_"]); +} FC_RETHROW_EXCEPTIONS(error, "Failed to serialize: ${type}", ("type", boost::core::demangle( typeid(o).name() ) )) template void abi_serializer::to_log_variant( const T& o, fc::variant& vo, const Resolver& resolver, const yield_function_t& yield ) try { mutable_variant_object mvo; - impl::abi_traverse_context ctx( yield ); + impl::abi_traverse_context ctx( yield, fc::microseconds{} ); ctx.logging(); impl::abi_to_variant::add(mvo, "_", o, resolver, ctx); vo = std::move(mvo["_"]); } FC_RETHROW_EXCEPTIONS(error, "Failed to serialize: ${type}", ("type", boost::core::demangle( typeid(o).name() ) )) +template +void abi_serializer::to_log_variant( const T& o, fc::variant& vo, const Resolver& resolver, const fc::microseconds& max_action_data_serialization_time ) try { + mutable_variant_object mvo; + impl::abi_traverse_context ctx( create_depth_yield_function(), max_action_data_serialization_time ); + ctx.logging(); + impl::abi_to_variant::add(mvo, "_", o, resolver, ctx); + vo = std::move(mvo["_"]); +} FC_RETHROW_EXCEPTIONS(error, "Failed to serialize: ${type}", ("type", boost::core::demangle( typeid(o).name() ) )) template void abi_serializer::from_variant( const fc::variant& v, T& o, const Resolver& resolver, const yield_function_t& yield ) try { - impl::abi_traverse_context ctx( yield ); + impl::abi_traverse_context ctx( yield, fc::microseconds{} ); impl::abi_from_variant::extract(v, o, resolver, ctx); } FC_RETHROW_EXCEPTIONS(error, "Failed to deserialize variant", ("variant",v)) template -void abi_serializer::from_variant( const fc::variant& v, T& o, const Resolver& resolver, const fc::microseconds& max_serialization_time ) { - from_variant( v, o, resolver, create_yield_function(max_serialization_time) ); -} +void abi_serializer::from_variant( const fc::variant& v, T& o, const Resolver& resolver, const fc::microseconds& max_action_data_serialization_time ) try { + impl::abi_traverse_context ctx( create_depth_yield_function(), max_action_data_serialization_time ); + impl::abi_from_variant::extract(v, o, resolver, ctx); +} FC_RETHROW_EXCEPTIONS(error, "Failed to deserialize variant", ("variant",v)) using abi_serializer_cache_t = std::unordered_map>; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index c10aff3e53..97bc488084 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -348,26 +348,6 @@ namespace eosio { namespace chain { const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const; wasm_interface& get_wasm_interface(); - - std::optional get_abi_serializer( account_name n, const abi_serializer::yield_function_t& yield )const { - if( n.good() ) { - try { - const auto& a = get_account( n ); - if( abi_def abi; abi_serializer::to_abi( a.abi, abi )) - return abi_serializer( std::move(abi), yield ); - } FC_CAPTURE_AND_LOG((n)) - } - return std::optional(); - } - - template - fc::variant to_variant_with_abi( const T& obj, const abi_serializer::yield_function_t& yield )const { - fc::variant pretty_output; - abi_serializer::to_variant( obj, pretty_output, - [&]( account_name n ){ return get_abi_serializer( n, yield ); }, yield ); - return pretty_output; - } - static chain_id_type extract_chain_id(snapshot_reader& snapshot); static std::optional extract_chain_id_from_db( const path& state_dir ); diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 14c99f9ec0..4236037309 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1840,7 +1840,7 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio read_only::get_scheduled_transactions_result result; - auto resolver = make_resolver(db, abi_serializer::create_yield_function( abi_serializer_max_time )); + auto resolver = make_resolver(db, abi_serializer_max_time, throw_on_yield::no); uint32_t remaining = p.limit; if (deadline != fc::time_point::maximum() && remaining > max_return_items) @@ -1863,7 +1863,7 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio fc::datastream ds( itr->packed_trx.data(), itr->packed_trx.size() ); fc::raw::unpack(ds,trx); - abi_serializer::to_variant(trx, pretty_transaction, resolver, abi_serializer::create_yield_function( abi_serializer_max_time )); + abi_serializer::to_variant(trx, pretty_transaction, resolver, abi_serializer_max_time); row("transaction", pretty_transaction); } else { auto packed_transaction = bytes(itr->packed_trx.begin(), itr->packed_trx.end()); @@ -1913,8 +1913,7 @@ chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_ std::function()> read_only::get_block(const get_raw_block_params& params, const fc::time_point& deadline) const { chain::signed_block_ptr block = get_raw_block(params, deadline); - auto yield = abi_serializer::create_yield_function(abi_serializer_max_time); - auto abi_cache = abi_serializer_cache_builder(make_resolver(db, std::move(yield))).add_serializers(block).get(); + auto abi_cache = abi_serializer_cache_builder(make_resolver(db, abi_serializer_max_time, throw_on_yield::no)).add_serializers(block).get(); using return_type = t_or_exception; return [this, @@ -1966,13 +1965,12 @@ read_only::get_block_header_result read_only::get_block_header(const read_only:: abi_resolver read_only::get_block_serializers( const chain::signed_block_ptr& block, const fc::microseconds& max_time ) const { - auto yield = abi_serializer::create_yield_function(max_time); - return abi_resolver(abi_serializer_cache_builder(make_resolver(db, std::move(yield))).add_serializers(block).get()); + return abi_resolver(abi_serializer_cache_builder(make_resolver(db, max_time, throw_on_yield::no)).add_serializers(block).get()); } fc::variant read_only::convert_block( const chain::signed_block_ptr& block, abi_resolver& resolver ) const { fc::variant pretty_output; - abi_serializer::to_variant( *block, pretty_output, resolver, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + abi_serializer::to_variant( *block, pretty_output, resolver, abi_serializer_max_time ); const auto block_id = block->calculate_id(); uint32_t ref_block_prefix = block_id._hash[1]; @@ -2049,7 +2047,7 @@ void read_write::push_block(read_write::push_block_params&& params, next_functio void read_write::push_transaction(const read_write::push_transaction_params& params, next_function next) { try { auto pretty_input = std::make_shared(); - auto resolver = make_resolver(db, abi_serializer::create_yield_function( abi_serializer_max_time )); + auto resolver = make_resolver(db, abi_serializer_max_time, throw_on_yield::yes); try { abi_serializer::from_variant(params, *pretty_input, std::move( resolver ), abi_serializer::create_yield_function( abi_serializer_max_time )); } EOS_RETHROW_EXCEPTIONS(chain::packed_transaction_type_exception, "Invalid packed transaction") @@ -2064,7 +2062,9 @@ void read_write::push_transaction(const read_write::push_transaction_params& par try { fc::variant output; try { - output = db.to_variant_with_abi( *trx_trace_ptr, abi_serializer::create_yield_function( abi_serializer_max_time ) ); + auto abi_cache = abi_serializer_cache_builder(make_resolver(db, abi_serializer_max_time, throw_on_yield::no)).add_serializers(trx_trace_ptr).get(); + auto resolver = abi_resolver(std::move(abi_cache)); + abi_serializer::to_variant(*trx_trace_ptr, output, std::move(resolver), abi_serializer_max_time); // Create map of (closest_unnotified_ancestor_action_ordinal, global_sequence) with action trace std::map< std::pair, fc::mutable_variant_object > act_traces_map; @@ -2170,7 +2170,7 @@ template void api_base::send_transaction_gen(API &api, send_transaction_params_t params, next_function next) { try { auto ptrx = std::make_shared(); - auto resolver = make_resolver(api.db, abi_serializer::create_yield_function( api.abi_serializer_max_time )); + auto resolver = make_resolver(api.db, api.abi_serializer_max_time, throw_on_yield::yes); try { abi_serializer::from_variant(params.transaction, *ptrx, resolver, abi_serializer::create_yield_function( api.abi_serializer_max_time )); } EOS_RETHROW_EXCEPTIONS(packed_transaction_type_exception, "Invalid packed transaction") @@ -2217,15 +2217,13 @@ void api_base::send_transaction_gen(API &api, send_transaction_params_t params, } if (!retried) { // we are still on main thread here. The lambda passed to `next()` below will be executed on the http thread pool - auto yield = abi_serializer::create_yield_function(fc::microseconds::maximum()); - auto abi_cache = abi_serializer_cache_builder(api_base::make_resolver(api.db, std::move(yield))).add_serializers(trx_trace_ptr).get(); + auto abi_cache = abi_serializer_cache_builder(make_resolver(api.db, api.abi_serializer_max_time, throw_on_yield::no)).add_serializers(trx_trace_ptr).get(); using return_type = t_or_exception; next([&api, trx_trace_ptr, resolver = abi_resolver(std::move(abi_cache))]() mutable { try { fc::variant output; try { - abi_serializer::to_variant(*trx_trace_ptr, output, std::move(resolver), - abi_serializer::create_yield_function(api.abi_serializer_max_time)); + abi_serializer::to_variant(*trx_trace_ptr, output, std::move(resolver), api.abi_serializer_max_time); } catch( abi_exception& ) { output = *trx_trace_ptr; } @@ -2516,7 +2514,7 @@ read_only::get_account_return_t read_only::get_account( const get_account_params read_only::get_required_keys_result read_only::get_required_keys( const get_required_keys_params& params, const fc::time_point& )const { transaction pretty_input; - auto resolver = make_resolver(db, abi_serializer::create_yield_function( abi_serializer_max_time )); + auto resolver = make_resolver(db, abi_serializer_max_time, throw_on_yield::yes); try { abi_serializer::from_variant(params.transaction, pretty_input, resolver, abi_serializer::create_yield_function( abi_serializer_max_time )); } EOS_RETHROW_EXCEPTIONS(chain::transaction_type_exception, "Invalid transaction") @@ -2612,8 +2610,8 @@ fc::variant chain_plugin::get_log_trx_trace(const transaction_trace_ptr& trx_tra fc::variant pretty_output; try { abi_serializer::to_log_variant(trx_trace, pretty_output, - chain_apis::api_base::make_resolver(chain(), abi_serializer::create_yield_function(get_abi_serializer_max_time())), - abi_serializer::create_yield_function(get_abi_serializer_max_time())); + make_resolver(chain(), get_abi_serializer_max_time(), throw_on_yield::no), + get_abi_serializer_max_time()); } catch (...) { pretty_output = trx_trace; } @@ -2624,8 +2622,8 @@ fc::variant chain_plugin::get_log_trx(const transaction& trx) const { fc::variant pretty_output; try { abi_serializer::to_log_variant(trx, pretty_output, - chain_apis::api_base::make_resolver(chain(), abi_serializer::create_yield_function(get_abi_serializer_max_time())), - abi_serializer::create_yield_function(get_abi_serializer_max_time())); + make_resolver(chain(), get_abi_serializer_max_time(), throw_on_yield::no), + get_abi_serializer_max_time()); } catch (...) { pretty_output = trx; } diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index 28560396aa..7547e38dcf 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -48,6 +48,26 @@ namespace eosio { using chain::abi_resolver; using chain::packed_transaction; + enum class throw_on_yield { no, yes }; + inline auto make_resolver(const controller& control, fc::microseconds abi_serializer_max_time, throw_on_yield yield_throw ) { + return [&control, abi_serializer_max_time, yield_throw](const account_name& name) -> std::optional { + if (name.good()) { + const auto* accnt = control.db().template find( name ); + if( accnt != nullptr ) { + try { + if( abi_def abi; abi_serializer::to_abi( accnt->abi, abi ) ) { + return abi_serializer( std::move( abi ), abi_serializer::create_yield_function( abi_serializer_max_time ) ); + } + } catch( ... ) { + if( yield_throw == throw_on_yield::yes ) + throw; + } + } + } + return {}; + }; + } + namespace chain_apis { struct empty{}; @@ -97,18 +117,6 @@ class api_base { static void handle_db_exhaustion(); static void handle_bad_alloc(); - static auto make_resolver(const controller& control, abi_serializer::yield_function_t yield) { - return [&control, yield{std::move(yield)}](const account_name &name) -> std::optional { - const auto* accnt = control.db().template find(name); - if (accnt != nullptr) { - if (abi_def abi; abi_serializer::to_abi(accnt->abi, abi)) { - return abi_serializer(std::move(abi), yield); - } - } - return {}; - }; - } - protected: struct send_transaction_params_t { bool return_failure_trace = true; diff --git a/plugins/chain_plugin/trx_retry_db.cpp b/plugins/chain_plugin/trx_retry_db.cpp index 2fb1d91360..1907c55fc3 100644 --- a/plugins/chain_plugin/trx_retry_db.cpp +++ b/plugins/chain_plugin/trx_retry_db.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -145,7 +146,10 @@ struct trx_retry_db_impl { // Convert to variant with abi here and now because abi could change in very next transaction. // Alternatively, we could store off all the abis needed and do the conversion later, but as this is designed // to run on an API node, probably the best trade off to perform the abi serialization during block processing. - tt.trx_trace_v = control.to_variant_with_abi( *trace, abi_serializer::create_yield_function( abi_max_time ) ); + auto abi_cache = abi_serializer_cache_builder(make_resolver(control, abi_max_time, throw_on_yield::no)).add_serializers(trace).get(); + auto resolver = abi_resolver(std::move(abi_cache)); + tt.trx_trace_v.clear(); + abi_serializer::to_variant(*trace, tt.trx_trace_v, resolver, abi_max_time); } catch( chain::abi_exception& ) { tt.trx_trace_v = *trace; } diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index fcb835ab16..472b273f40 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -514,7 +514,7 @@ fc::variant push_transaction( signed_transaction& trx, const std::vectorfirst_block < 1) ? 1 : opt->first_block; signed_block_ptr next; fc::variant pretty_output; - const fc::microseconds deadline = fc::seconds(10); auto print_block = [&](signed_block_ptr& next) { abi_serializer::to_variant( *next, pretty_output, [](account_name n) { return std::optional(); }, - abi_serializer::create_yield_function(deadline)); + fc::seconds(1)); const auto block_id = next->calculate_id(); const uint32_t ref_block_prefix = block_id._hash[1]; const auto enhanced_object = fc::mutable_variant_object("block_num", next->block_num())("id", block_id)("ref_block_prefix", ref_block_prefix)(pretty_output.get_object()); diff --git a/unittests/abi_tests.cpp b/unittests/abi_tests.cpp index cdab740d58..ddf2ca5661 100644 --- a/unittests/abi_tests.cpp +++ b/unittests/abi_tests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -46,12 +47,19 @@ fc::microseconds max_serialization_time = fc::seconds(1); // some test machines fc::variant verify_byte_round_trip_conversion( const abi_serializer& abis, const type_name& type, const fc::variant& var ) { auto bytes = abis.variant_to_binary(type, var, abi_serializer::create_yield_function( max_serialization_time )); + auto b = abis.variant_to_binary(type, var, max_serialization_time ); + BOOST_TEST( b == bytes ); auto var2 = abis.binary_to_variant(type, bytes, abi_serializer::create_yield_function( max_serialization_time )); + auto var3 = abis.binary_to_variant(type, b, max_serialization_time); - std::string r = fc::json::to_string(var2, fc::time_point::now() + max_serialization_time); + std::string r2 = fc::json::to_string(var2, fc::time_point::now() + max_serialization_time); + std::string r3 = fc::json::to_string(var3, fc::time_point::now() + max_serialization_time); + BOOST_TEST( r2 == r3 ); auto bytes2 = abis.variant_to_binary(type, var2, abi_serializer::create_yield_function( max_serialization_time )); + auto bytes3 = abis.variant_to_binary(type, var3, max_serialization_time); + BOOST_TEST( bytes2 == bytes3 ); BOOST_TEST( fc::to_hex(bytes) == fc::to_hex(bytes2) ); @@ -63,10 +71,16 @@ void verify_round_trip_conversion( const abi_serializer& abis, const type_name& auto var = fc::json::from_string(json); auto bytes = abis.variant_to_binary(type, var, abi_serializer::create_yield_function( max_serialization_time )); BOOST_REQUIRE_EQUAL(fc::to_hex(bytes), hex); + auto b = abis.variant_to_binary(type, var, max_serialization_time); + BOOST_REQUIRE_EQUAL(fc::to_hex(b), hex); auto var2 = abis.binary_to_variant(type, bytes, abi_serializer::create_yield_function( max_serialization_time )); BOOST_REQUIRE_EQUAL(fc::json::to_string(var2, fc::time_point::now() + max_serialization_time), expected_json); + auto var3 = abis.binary_to_variant(type, b, max_serialization_time ); + BOOST_REQUIRE_EQUAL(fc::json::to_string(var3, fc::time_point::now() + max_serialization_time), expected_json); auto bytes2 = abis.variant_to_binary(type, var2, abi_serializer::create_yield_function( max_serialization_time )); BOOST_REQUIRE_EQUAL(fc::to_hex(bytes2), hex); + auto b2 = abis.variant_to_binary(type, var3, max_serialization_time); + BOOST_REQUIRE_EQUAL(fc::to_hex(b2), hex); } void verify_round_trip_conversion( const abi_serializer& abis, const type_name& type, const std::string& json, const std::string& hex ) @@ -87,17 +101,28 @@ fc::variant verify_type_round_trip_conversion( const abi_serializer& abis, const { try { auto bytes = abis.variant_to_binary(type, var, abi_serializer::create_yield_function( max_serialization_time )); + auto b = abis.variant_to_binary(type, var, max_serialization_time); T obj; abi_serializer::from_variant(var, obj, get_resolver(), abi_serializer::create_yield_function( max_serialization_time )); + T obj2; + abi_serializer::from_variant(var, obj2, get_resolver(), max_serialization_time); + fc::variant var2; abi_serializer::to_variant(obj, var2, get_resolver(), abi_serializer::create_yield_function( max_serialization_time )); - std::string r = fc::json::to_string(var2, fc::time_point::now() + max_serialization_time); + fc::variant var3; + abi_serializer::to_variant(obj2, var3, get_resolver(), max_serialization_time); + std::string r2 = fc::json::to_string(var2, fc::time_point::now() + max_serialization_time); + std::string r3 = fc::json::to_string(var3, fc::time_point::now() + max_serialization_time); + BOOST_TEST( r2 == r3 ); auto bytes2 = abis.variant_to_binary(type, var2, abi_serializer::create_yield_function( max_serialization_time )); + auto b3 = abis.variant_to_binary(type, var3, max_serialization_time); + BOOST_TEST( bytes2 == b3 ); + BOOST_TEST( b == b3 ); BOOST_TEST( fc::to_hex(bytes) == fc::to_hex(bytes2) ); @@ -1572,11 +1597,16 @@ BOOST_AUTO_TEST_CASE(packed_transaction) )====="; fc::variant var; abi_serializer::to_variant(packed_txn, var, get_resolver(fc::json::from_string(packed_transaction_abi).as()), abi_serializer::create_yield_function( max_serialization_time )); + fc::variant var2; + abi_serializer::to_variant(packed_txn, var2, get_resolver(fc::json::from_string(packed_transaction_abi).as()), max_serialization_time); chain::packed_transaction packed_txn2; abi_serializer::from_variant(var, packed_txn2, get_resolver(fc::json::from_string(packed_transaction_abi).as()), abi_serializer::create_yield_function( max_serialization_time )); + chain::packed_transaction packed_txn3; + abi_serializer::from_variant(var2, packed_txn3, get_resolver(fc::json::from_string(packed_transaction_abi).as()), max_serialization_time); const auto txn2 = packed_txn2.get_transaction(); + const auto txn3 = packed_txn3.get_transaction(); BOOST_REQUIRE_EQUAL(txn.ref_block_num, txn2.ref_block_num); BOOST_REQUIRE_EQUAL(txn.ref_block_prefix, txn2.ref_block_prefix); @@ -1589,6 +1619,19 @@ BOOST_AUTO_TEST_CASE(packed_transaction) verify_action_equal(txn.actions[i], txn2.actions[i]); BOOST_REQUIRE_EQUAL(txn.max_net_usage_words.value, txn2.max_net_usage_words.value); BOOST_REQUIRE_EQUAL(txn.max_cpu_usage_ms, txn2.max_cpu_usage_ms); + + BOOST_REQUIRE_EQUAL(txn.ref_block_num, txn3.ref_block_num); + BOOST_REQUIRE_EQUAL(txn.ref_block_prefix, txn3.ref_block_prefix); + BOOST_REQUIRE(txn.expiration == txn3.expiration); + BOOST_REQUIRE_EQUAL(txn.context_free_actions.size(), txn3.context_free_actions.size()); + for (unsigned int i = 0; i < txn.context_free_actions.size(); ++i) + verify_action_equal(txn.context_free_actions[i], txn3.context_free_actions[i]); + BOOST_REQUIRE_EQUAL(txn.actions.size(), txn3.actions.size()); + for (unsigned int i = 0; i < txn.actions.size(); ++i) + verify_action_equal(txn.actions[i], txn3.actions[i]); + BOOST_REQUIRE_EQUAL(txn.max_net_usage_words.value, txn3.max_net_usage_words.value); + BOOST_REQUIRE_EQUAL(txn.max_cpu_usage_ms, txn3.max_cpu_usage_ms); + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_CASE(abi_type_repeat) @@ -2014,7 +2057,7 @@ BOOST_AUTO_TEST_CASE(abi_type_nested_in_vector) } )====="; - BOOST_CHECK_THROW( abi_serializer abis(fc::json::from_string(repeat_abi).as(), abi_serializer::create_yield_function( max_serialization_time )), fc::exception ); + BOOST_CHECK_THROW( abi_serializer abis(fc::json::from_string(repeat_abi).as(), abi_serializer::create_yield_function( max_serialization_time )), parse_error_exception ); } FC_LOG_AND_RETHROW() } @@ -2094,6 +2137,7 @@ BOOST_AUTO_TEST_CASE(abi_large_array) static_cast(0xff), static_cast(0x08)}; BOOST_CHECK_THROW( abis.binary_to_variant( "hi[]", bin, abi_serializer::create_yield_function( max_serialization_time ) );, fc::exception ); + BOOST_CHECK_THROW( abis.binary_to_variant( "hi[]", bin, max_serialization_time );, fc::exception ); } FC_LOG_AND_RETHROW() } @@ -2207,7 +2251,9 @@ BOOST_AUTO_TEST_CASE(abi_recursive_structs) abi_serializer abis(fc::json::from_string(abi_str).as(), abi_serializer::create_yield_function( max_serialization_time )); string hi_data = "{\"user\":\"eosio\"}"; auto bin = abis.variant_to_binary("hi2", fc::json::from_string(hi_data), abi_serializer::create_yield_function( max_serialization_time )); + auto bin2 = abis.variant_to_binary("hi2", fc::json::from_string(hi_data), max_serialization_time); BOOST_CHECK_THROW( abis.binary_to_variant("hi", bin, abi_serializer::create_yield_function( max_serialization_time ));, fc::exception ); + BOOST_CHECK_THROW( abis.binary_to_variant("hi", bin2, max_serialization_time);, fc::exception ); } FC_LOG_AND_RETHROW() } @@ -2219,11 +2265,12 @@ BOOST_AUTO_TEST_CASE(abi_very_deep_structs) abi_serializer abis( fc::json::from_string( large_nested_abi ).as(), abi_serializer::create_yield_function( max_serialization_time ) ); string hi_data = "{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":{\"f1\":0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"; BOOST_CHECK_THROW( abis.variant_to_binary( "s98", fc::json::from_string( hi_data ), abi_serializer::create_yield_function( max_serialization_time ) ), fc::exception ); + BOOST_CHECK_THROW( abis.variant_to_binary( "s98", fc::json::from_string( hi_data ), max_serialization_time ), fc::exception ); } FC_LOG_AND_RETHROW() } // Infinite recursion of abi_serializer in struct definitions -BOOST_AUTO_TEST_CASE(abi_very_deep_structs_1ms) +BOOST_AUTO_TEST_CASE(abi_very_deep_structs_1us) { try { BOOST_CHECK_THROW( @@ -2293,20 +2340,32 @@ BOOST_AUTO_TEST_CASE(abi_large_signature) .sig = sig }); - fc::variant var; - auto start = fc::time_point::now(); - bool check_data = true; - try { - abi_serializer::to_variant( large_act, var, get_resolver( fc::json::from_string( abi_str ).as() ), - abi_serializer::create_yield_function( fc::milliseconds( 1 ) ) ); - } catch( abi_serialization_deadline_exception& ) { - // can be thrown if check_deadline is tripped after deadline in to_base58 is tripped - check_data = false; + { + fc::variant var; + auto start = fc::time_point::now(); + bool check_data = true; + try { + abi_serializer::to_variant( large_act, var, get_resolver( fc::json::from_string( abi_str ).as() ), + abi_serializer::create_yield_function( fc::milliseconds( 1 ) ) ); + } catch( abi_serialization_deadline_exception& ) { + // can be thrown if check_deadline is tripped after deadline in to_base58 is tripped + check_data = false; + } + auto stop = fc::time_point::now(); + // Give it a leaway of 50ms + BOOST_CHECK_LE( (stop - start).count(), 51 * 1000 ); + if( check_data ) { + BOOST_CHECK( var.get_object().contains( "data" ) ); + BOOST_CHECK( var.get_object().contains( "hex_data" ) ); + } } - auto stop = fc::time_point::now(); - // Give it a leaway of 50ms - BOOST_CHECK_LE( (stop - start).count(), 51*1000 ); - if( check_data ) { + { + fc::variant var; + auto start = fc::time_point::now(); + abi_serializer::to_variant( large_act, var, get_resolver( fc::json::from_string( abi_str ).as() ), fc::milliseconds(1) ); + auto stop = fc::time_point::now(); + // Give it a leaway of 50ms + BOOST_CHECK_LE( (stop - start).count(), 51 * 1000 ); BOOST_CHECK( var.get_object().contains( "data" ) ); BOOST_CHECK( var.get_object().contains( "hex_data" ) ); } @@ -2373,9 +2432,20 @@ BOOST_AUTO_TEST_CASE(variants) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["4", 5, 6])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Expected input to be an array of two items while processing variant 'v1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(9)"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Expected input to be an array of two items while processing variant 'v1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"([4])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Expected input to be an array of two items while processing variant 'v1") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"([4, 5])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Encountered non-string as first item of input array while processing variant 'v1") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["4", 5, 6])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Expected input to be an array of two items while processing variant 'v1'") ); + // type is not valid within this variant BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["int9", 21])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Specified type 'int9' in input array is not valid within the variant 'v1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["int9", 21])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Specified type 'int9' in input array is not valid within the variant 'v1'") ); verify_round_trip_conversion(abis, "v1", R"(["int8",21])", "0015"); verify_round_trip_conversion(abis, "v1", R"(["string","abcd"])", "010461626364"); @@ -2467,10 +2537,14 @@ BOOST_AUTO_TEST_CASE(extend) // missing i1 BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"({"i0":5})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Missing field 'i1' in input object while processing struct") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"({"i0":5})"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Missing field 'i1' in input object while processing struct") ); // Unexpected 'a' BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"({"i0":5,"i1":6,"a":[8,9,10]})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Unexpected field 'a' found in input object while processing struct") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"({"i0":5,"i1":6,"a":[8,9,10]})"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Unexpected field 'a' found in input object while processing struct") ); verify_round_trip_conversion(abis, "s", R"({"i0":5,"i1":6})", "0506"); verify_round_trip_conversion(abis, "s", R"({"i0":5,"i1":6,"i2":7})", "050607"); @@ -2486,6 +2560,8 @@ BOOST_AUTO_TEST_CASE(extend) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"i0":1})"), abi_serializer::create_yield_function( max_serialization_time )), abi_exception, fc_exception_message_starts_with("Encountered field 'i2' without binary extension designation while processing struct") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"i0":1})"), max_serialization_time), + abi_exception, fc_exception_message_starts_with("Encountered field 'i2' without binary extension designation while processing struct") ); } FC_LOG_AND_RETHROW() @@ -2522,9 +2598,13 @@ BOOST_AUTO_TEST_CASE(abi_serialize_incomplete_json_array) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"([])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Early end to input array specifying the fields of struct") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"([])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Early end to input array specifying the fields of struct") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"([1,2])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Early end to input array specifying the fields of struct") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s", fc::json::from_string(R"([1,2])"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Early end to input array specifying the fields of struct") ); verify_round_trip_conversion(abis, "s", R"([1,2,3])", "010203", R"({"i0":1,"i1":2,"i2":3})"); @@ -2554,9 +2634,13 @@ BOOST_AUTO_TEST_CASE(abi_serialize_incomplete_json_object) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Missing field 'f0' in input object") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({})"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Missing field 'f0' in input object") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"f0":{"i0":1}})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_starts_with("Missing field 'i1' in input object") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"f0":{"i0":1}})"), max_serialization_time), + pack_exception, fc_exception_message_starts_with("Missing field 'i1' in input object") ); verify_round_trip_conversion(abis, "s2", R"({"f0":{"i0":1,"i1":2},"i2":3})", "010203"); @@ -2585,6 +2669,8 @@ BOOST_AUTO_TEST_CASE(abi_serialize_json_mismatching_type) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"f0":1,"i1":2})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Unexpected input encountered while processing struct 's2.f0'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2", fc::json::from_string(R"({"f0":1,"i1":2})"), max_serialization_time), + pack_exception, fc_exception_message_is("Unexpected input encountered while processing struct 's2.f0'") ); verify_round_trip_conversion(abis, "s2", R"({"f0":{"i0":1},"i1":2})", "0102"); @@ -2608,7 +2694,8 @@ BOOST_AUTO_TEST_CASE(abi_serialize_json_empty_name) try { abi_serializer abis( fc::json::from_string(abi).as(), abi_serializer::create_yield_function( max_serialization_time ) ); - auto bin = abis.variant_to_binary("s1", fc::json::from_string(R"({"":1})"), abi_serializer::create_yield_function( max_serialization_time )); + auto bin1 = abis.variant_to_binary("s1", fc::json::from_string(R"({"":1})"), abi_serializer::create_yield_function( max_serialization_time )); + auto bin2 = abis.variant_to_binary("s1", fc::json::from_string(R"({"":1})"), max_serialization_time); verify_round_trip_conversion(abis, "s1", R"({"":1})", "01"); @@ -2661,34 +2748,52 @@ BOOST_AUTO_TEST_CASE(abi_serialize_detailed_error_messages) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("bar", fc::json::from_string(R"({"f0":{"i0":1},"i2":3})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's2.f0'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("bar", fc::json::from_string(R"({"f0":{"i0":1},"i2":3})"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's2.f0'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s3", fc::json::from_string(R"({"i0":1,"i2":3})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's3'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s3", fc::json::from_string(R"({"i0":1,"i2":3})"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's3'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s3", fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["s2",{}]})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Specified type 's2' in input array is not valid within the variant 's3.f3'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s3", fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["s2",{}]})"), max_serialization_time), + pack_exception, fc_exception_message_is("Specified type 's2' in input array is not valid within the variant 's3.f3'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s3", fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11},"i2":13}]})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's3.f3..f0'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s3", fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11},"i2":13}]})"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's3.f3..f0'") ); verify_round_trip_conversion(abis, "s3", R"({"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11,"i1":12},"i2":13}]})", "010203010b0c0d"); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["s3",{"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11,"i1":12},"i2":13}],"f5":0}])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Unexpected field 'f5' found in input object while processing struct 'v1.'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["s3",{"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11,"i1":12},"i2":13}],"f5":0}])"), max_serialization_time), + pack_exception, fc_exception_message_is("Unexpected field 'f5' found in input object while processing struct 'v1.'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["s4",{"f0":[0,1],"f1":[{"i0":2,"i1":3},{"i1":5}]}])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 'v1..f1[1]'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["s4",{"f0":[0,1],"f1":[{"i0":2,"i1":3},{"i1":5}]}])"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 'v1..f1[1]'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2[]", fc::json::from_string(R"([{"f0":{"i0":1,"i1":2},"i2":3},{"f0":{"i0":4},"i2":6}])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 'ARRAY[1].f0'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2[]", fc::json::from_string(R"([{"f0":{"i0":1,"i1":2},"i2":3},{"f0":{"i0":4},"i2":6}])"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 'ARRAY[1].f0'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s5", fc::json::from_string(R"({"f0":[["bar",{"f0":{"i0":1,"i1":2},"i2":3}],["foo",{"f0":{"i0":4},"i2":6}]]})"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's5.f0[1]..f0'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s5", fc::json::from_string(R"({"f0":[["bar",{"f0":{"i0":1,"i1":2},"i2":3}],["foo",{"f0":{"i0":4},"i2":6}]]})"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's5.f0[1]..f0'") ); verify_round_trip_conversion( abis, "s1arrayarray", R"([[{"i0":1,"i1":2},{"i0":3,"i1":4}],[{"i0":5,"i1":6},{"i0":7,"i1":8},{"i0":9,"i1":10}]])", "0202010203040305060708090a"); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s1arrayarray", fc::json::from_string(R"([[{"i0":1,"i1":2},{"i0":3,"i1":4}],[{"i0":6,"i1":6},{"i0":7,"i1":8},{"i1":10}]])"), abi_serializer::create_yield_function( max_serialization_time )), pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 'ARRAY[1][2]'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s1arrayarray", fc::json::from_string(R"([[{"i0":1,"i1":2},{"i0":3,"i1":4}],[{"i0":6,"i1":6},{"i0":7,"i1":8},{"i1":10}]])"), max_serialization_time), + pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 'ARRAY[1][2]'") ); } FC_LOG_AND_RETHROW() } @@ -2738,37 +2843,62 @@ BOOST_AUTO_TEST_CASE(abi_serialize_short_error_messages) BOOST_CHECK_EXCEPTION( abis.variant_to_binary("bar", fc::json::from_string(R"({"f0":{"i0":1},"i2":3})"), abi_serializer::create_yield_function( max_serialization_time ), true), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("bar", fc::json::from_string(R"({"f0":{"i0":1},"i2":3})"), max_serialization_time, true), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "very_very_very_very_very_very_very_very_very_very_long_struct_name_s3", fc::json::from_string(R"({"i0":1,"i2":3})"), abi_serializer::create_yield_function( max_serialization_time ), true ), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 'very_very_very_very_very_very_very_very_very_very_long_...ame_s3'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "very_very_very_very_very_very_very_very_very_very_long_struct_name_s3", + fc::json::from_string(R"({"i0":1,"i2":3})"), max_serialization_time, true ), + pack_exception, + fc_exception_message_is("Missing field 'i1' in input object while processing struct 'very_very_very_very_very_very_very_very_very_very_long_...ame_s3'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "very_very_very_very_very_very_very_very_very_very_long_struct_name_s3", fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["s2",{}]})"), abi_serializer::create_yield_function( max_serialization_time ), true ), pack_exception, fc_exception_message_is("Specified type 's2' in input array is not valid within the variant 'v2'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "very_very_very_very_very_very_very_very_very_very_long_struct_name_s3", + fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["s2",{}]})"), max_serialization_time, true ), + pack_exception, fc_exception_message_is("Specified type 's2' in input array is not valid within the variant 'v2'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "very_very_very_very_very_very_very_very_very_very_long_struct_name_s3", fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11},"i2":13}]})"), abi_serializer::create_yield_function( max_serialization_time ), true ), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "very_very_very_very_very_very_very_very_very_very_long_struct_name_s3", + fc::json::from_string(R"({"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11},"i2":13}]})"), max_serialization_time, true ), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "v1", fc::json::from_string(R"(["very_very_very_very_very_very_very_very_very_very_long_struct_name_s3",{"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11,"i1":12},"i2":13}],"very_very_very_very_very_very_very_very_very_very_long_field_name_f5":0}])"), abi_serializer::create_yield_function( max_serialization_time ), true ), pack_exception, fc_exception_message_is("Unexpected field 'very_very_very_very_very_very_very_very_very_very_long_...ame_f5' found in input object while processing struct 'very_very_very_very_very_very_very_very_very_very_long_...ame_s3'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary( "v1", + fc::json::from_string(R"(["very_very_very_very_very_very_very_very_very_very_long_struct_name_s3",{"i0":1,"i1":2,"i2":3,"f3":["bar",{"f0":{"i0":11,"i1":12},"i2":13}],"very_very_very_very_very_very_very_very_very_very_long_field_name_f5":0}])"), + max_serialization_time, true ), + pack_exception, + fc_exception_message_is("Unexpected field 'very_very_very_very_very_very_very_very_very_very_long_...ame_f5' found in input object while processing struct 'very_very_very_very_very_very_very_very_very_very_long_...ame_s3'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["s4",{"f0":[0,1],"f1":[{"i0":2,"i1":3},{"i1":5}]}])"), abi_serializer::create_yield_function( max_serialization_time ), true), pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 's1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("v1", fc::json::from_string(R"(["s4",{"f0":[0,1],"f1":[{"i0":2,"i1":3},{"i1":5}]}])"), max_serialization_time, true), + pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 's1'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2[]", fc::json::from_string(R"([{"f0":{"i0":1,"i1":2},"i2":3},{"f0":{"i0":4},"i2":6}])"), abi_serializer::create_yield_function( max_serialization_time ), true), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s2[]", fc::json::from_string(R"([{"f0":{"i0":1,"i1":2},"i2":3},{"f0":{"i0":4},"i2":6}])"), max_serialization_time, true), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s5", fc::json::from_string(R"({"f0":[["bar",{"f0":{"i0":1,"i1":2},"i2":3}],["foo",{"f0":{"i0":4},"i2":6}]]})"), abi_serializer::create_yield_function( max_serialization_time ), true), pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s5", fc::json::from_string(R"({"f0":[["bar",{"f0":{"i0":1,"i1":2},"i2":3}],["foo",{"f0":{"i0":4},"i2":6}]]})"), max_serialization_time, true), + pack_exception, fc_exception_message_is("Missing field 'i1' in input object while processing struct 's1'") ); BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s1arrayarray", fc::json::from_string(R"([[{"i0":1,"i1":2},{"i0":3,"i1":4}],[{"i0":6,"i1":6},{"i0":7,"i1":8},{"i1":10}]])"), abi_serializer::create_yield_function( max_serialization_time ), true), pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 's1'") ); + BOOST_CHECK_EXCEPTION( abis.variant_to_binary("s1arrayarray", fc::json::from_string(R"([[{"i0":1,"i1":2},{"i0":3,"i1":4}],[{"i0":6,"i1":6},{"i0":7,"i1":8},{"i1":10}]])"), max_serialization_time, true), + pack_exception, fc_exception_message_is("Missing field 'i0' in input object while processing struct 's1'") ); } FC_LOG_AND_RETHROW() } @@ -2816,38 +2946,59 @@ BOOST_AUTO_TEST_CASE(abi_deserialize_detailed_error_messages) // Test to verify that array of optinal doesn't throw exception abi_serializer abis( fc::json::from_string(abi).as(), abi_serializer::create_yield_function( max_serialization_time ) ); BOOST_CHECK_NO_THROW( abis.binary_to_variant("s4", fc::variant("030101000103").as(), abi_serializer::create_yield_function( max_serialization_time )) ); + BOOST_CHECK_NO_THROW( abis.binary_to_variant("s4", fc::variant("030101000103").as(), max_serialization_time) ); try { BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s2", fc::variant("020102").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Stream unexpectedly ended; unable to unpack field 'f1' of struct 's2'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s2", fc::variant("020102").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Stream unexpectedly ended; unable to unpack field 'f1' of struct 's2'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s2", fc::variant("0201020103").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Stream unexpectedly ended; unable to unpack field 'i1' of struct 's2.f1[0]'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s2", fc::variant("0201020103").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Stream unexpectedly ended; unable to unpack field 'i1' of struct 's2.f1[0]'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s2", fc::variant("020102ff").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Unable to unpack size of array 's2.f1'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s2", fc::variant("020102ff").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Unable to unpack size of array 's2.f1'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s3", fc::variant("010203").as(), abi_serializer::create_yield_function( max_serialization_time )), abi_exception, fc_exception_message_is("Encountered field 'i5' without binary extension designation while processing struct 's3'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s3", fc::variant("010203").as(), max_serialization_time), + abi_exception, fc_exception_message_is("Encountered field 'i5' without binary extension designation while processing struct 's3'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s3", fc::variant("02010304").as(), abi_serializer::create_yield_function( max_serialization_time )), abi_exception, fc_exception_message_is("Encountered field 'i5' without binary extension designation while processing struct 's3'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s3", fc::variant("02010304").as(), max_serialization_time), + abi_exception, fc_exception_message_is("Encountered field 'i5' without binary extension designation while processing struct 's3'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s4", fc::variant("020101").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Unable to unpack optional of built-in type 'int8' while processing 's4.f0[1]'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s4", fc::variant("020101").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Unable to unpack optional of built-in type 'int8' while processing 's4.f0[1]'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("02010102").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Unable to unpack presence flag of optional 's5.f0[1]'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("02010102").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Unable to unpack presence flag of optional 's5.f0[1]'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("0001").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Unable to unpack tag of variant 's5.f1[0]'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("0001").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Unable to unpack tag of variant 's5.f1[0]'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("00010501").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Unpacked invalid tag (5) for variant 's5.f1[0]'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("00010501").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Unpacked invalid tag (5) for variant 's5.f1[0]'") ); BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("00010101").as(), abi_serializer::create_yield_function( max_serialization_time )), unpack_exception, fc_exception_message_is("Stream unexpectedly ended; unable to unpack field 'i1' of struct 's5.f1[0].'") ); + BOOST_CHECK_EXCEPTION( abis.binary_to_variant("s5", fc::variant("00010101").as(), max_serialization_time), + unpack_exception, fc_exception_message_is("Stream unexpectedly ended; unable to unpack field 'i1' of struct 's5.f1[0].'") ); } FC_LOG_AND_RETHROW() } @@ -2972,12 +3123,22 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__good_return_value) auto abidef = fc::json::from_string(abi).as(); abi_serializer abis(abi_def(abidef), abi_serializer::create_yield_function(max_serialization_time)); - mutable_variant_object mvo; - eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time)); - eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); - std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); + { + mutable_variant_object mvo; + eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time), fc::microseconds{}); + eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); + std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); - BOOST_CHECK_EQUAL(res, expected_json); + BOOST_CHECK_EQUAL(res, expected_json); + } + { + mutable_variant_object mvo; + eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_depth_yield_function(), max_serialization_time); + eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); + std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); + + BOOST_CHECK_EQUAL(res, expected_json); + } } BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__bad_return_value) @@ -2997,12 +3158,22 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__bad_return_value) auto abidef = fc::json::from_string(abi).as(); abi_serializer abis(abi_def(abidef), abi_serializer::create_yield_function(max_serialization_time)); - mutable_variant_object mvo; - eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time)); - eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); - std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); + { + mutable_variant_object mvo; + eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time), fc::microseconds{}); + eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); + std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); + + BOOST_CHECK_EQUAL(res, expected_json); + } + { + mutable_variant_object mvo; + eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_depth_yield_function(), max_serialization_time); + eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); + std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); - BOOST_CHECK_EQUAL(res, expected_json); + BOOST_CHECK_EQUAL(res, expected_json); + } } BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__no_return_value) @@ -3032,12 +3203,22 @@ BOOST_AUTO_TEST_CASE(abi_to_variant__add_action__no_return_value) auto abidef = fc::json::from_string(abi).as(); abi_serializer abis(abi_def(abidef), abi_serializer::create_yield_function(max_serialization_time)); - mutable_variant_object mvo; - eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time)); - eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); - std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); + { + mutable_variant_object mvo; + eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_yield_function(max_serialization_time), fc::microseconds{}); + eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); + std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); - BOOST_CHECK_EQUAL(res, expected_json); + BOOST_CHECK_EQUAL(res, expected_json); + } + { + mutable_variant_object mvo; + eosio::chain::impl::abi_traverse_context ctx(abi_serializer::create_depth_yield_function(), max_serialization_time); + eosio::chain::impl::abi_to_variant::add(mvo, "action_traces", at, get_resolver(abidef), ctx); + std::string res = fc::json::to_string(mvo, fc::time_point::now() + max_serialization_time); + + BOOST_CHECK_EQUAL(res, expected_json); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index f463b229be..b35818b7ad 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -1116,7 +1116,10 @@ BOOST_FIXTURE_TEST_CASE(eosio_abi, validating_tester) try { // verify to_variant works on eos native contract type: newaccount // see abi_serializer::to_abi() abi_serializer::to_variant(*result, pretty_output, get_resolver(), abi_serializer::create_yield_function( abi_serializer_max_time )); + BOOST_TEST(fc::json::to_string(pretty_output, fc::time_point::now() + abi_serializer_max_time).find("newaccount") != std::string::npos); + pretty_output.clear(); + abi_serializer::to_variant(*result, pretty_output, get_resolver(), abi_serializer_max_time); BOOST_TEST(fc::json::to_string(pretty_output, fc::time_point::now() + abi_serializer_max_time).find("newaccount") != std::string::npos); produce_block(); From e666cd000ea89169f3eb027a3a87477785a810d8 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 10 May 2023 17:38:34 -0500 Subject: [PATCH 21/32] GH-1062 Revert unneeded changes to yield call --- libraries/chain/abi_serializer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index 2235dbf1f7..61520aab01 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -24,12 +24,11 @@ namespace eosio { namespace chain { template inline fc::variant variant_from_stream(fc::datastream& stream, const abi_serializer::yield_function_t& yield) { - yield(0); // reset deadline - fc::yield_function_t y = [yield](){ yield(1); }; // create yield function matching fc::variant requirements, 0 for recursive depth T temp; fc::raw::unpack( stream, temp ); - y(); - return fc::variant( temp, y ); + yield(0); + // create yield function matching fc::variant requirements, 0 for recursive depth + return fc::variant( temp, [yield](){ yield(0); } ); } template From 99f93200ac798d8556600d433956670c7caf0deb Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 10 May 2023 17:44:07 -0500 Subject: [PATCH 22/32] GH-1062 Use new fc time_point::safe_add --- .../include/eosio/http_plugin/beast_http_session.hpp | 4 ++-- plugins/http_plugin/include/eosio/http_plugin/common.hpp | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp b/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp index 83148ca84c..3342b54637 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/beast_http_session.hpp @@ -422,7 +422,7 @@ class beast_http_session : public detail::abstract_conn { error_results results{static_cast(http::status::internal_server_error), "Internal Service Error", error_results::error_info( e, http_plugin::verbose_errors() )}; - err_str = fc::json::to_string( results, plugin_state_->get_max_response_deadline() ); + err_str = fc::json::to_string( results, fc::time_point::now().safe_add(plugin_state_->max_response_time) ); } } catch(std::exception& e) { err_str = e.what(); @@ -432,7 +432,7 @@ class beast_http_session : public detail::abstract_conn { "Internal Service Error", error_results::error_info( fc::exception( FC_LOG_MESSAGE( error, err_str ) ), http_plugin::verbose_errors() )}; - err_str = fc::json::to_string( results, plugin_state_->get_max_response_deadline() ); + err_str = fc::json::to_string( results, fc::time_point::now().safe_add(plugin_state_->max_response_time) ); } } catch(...) { err_str = "Unknown exception"; diff --git a/plugins/http_plugin/include/eosio/http_plugin/common.hpp b/plugins/http_plugin/include/eosio/http_plugin/common.hpp index dd2db95611..6691750120 100644 --- a/plugins/http_plugin/include/eosio/http_plugin/common.hpp +++ b/plugins/http_plugin/include/eosio/http_plugin/common.hpp @@ -136,11 +136,6 @@ struct http_plugin_state { explicit http_plugin_state(fc::logger& log) : logger(log) {} - - fc::time_point get_max_response_deadline() const { - return max_response_time == fc::microseconds::maximum() ? fc::time_point::maximum() - : fc::time_point::now() + max_response_time; - } }; /** From c7bf139eba9fc2348aa77822db345ced80fcc428 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 12 May 2023 13:36:04 -0500 Subject: [PATCH 23/32] GH-1062 Refactor start_action_serialization into constructor of context --- .../include/eosio/chain/abi_serializer.hpp | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/libraries/chain/include/eosio/chain/abi_serializer.hpp b/libraries/chain/include/eosio/chain/abi_serializer.hpp index 6467dc52e8..9ff9b27fde 100644 --- a/libraries/chain/include/eosio/chain/abi_serializer.hpp +++ b/libraries/chain/include/eosio/chain/abi_serializer.hpp @@ -24,6 +24,7 @@ namespace impl { struct abi_traverse_context_with_path; struct binary_to_variant_context; struct variant_to_binary_context; + struct action_data_to_variant_context; } /** @@ -183,17 +184,6 @@ namespace impl { void check_deadline()const { yield( recursion_depth ); } abi_serializer::yield_function_t get_yield_function() { return yield; } - void start_action_serialization() { - if (max_action_serialization_time.count() > 0) { - fc::time_point deadline = fc::time_point::now().safe_add(max_action_serialization_time); - yield = [deadline, y=yield, max=max_action_serialization_time](size_t depth) { - y(depth); // call provided yield that might include an overall time limit or not - EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, - "serialization action data time limit ${t}us exceeded", ("t", max) ); - }; - } - } - fc::scoped_exit> enter_scope(); protected: @@ -276,6 +266,22 @@ namespace impl { using abi_traverse_context_with_path::abi_traverse_context_with_path; }; + struct action_data_to_variant_context : public binary_to_variant_context { + action_data_to_variant_context( const abi_serializer& abis, const abi_traverse_context& ctx, const std::string_view& type ) + : binary_to_variant_context(abis, ctx, type) + { + short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place + if (max_action_serialization_time.count() > 0) { + fc::time_point deadline = fc::time_point::now().safe_add(max_action_serialization_time); + yield = [deadline, y=yield, max=max_action_serialization_time](size_t depth) { + y(depth); // call provided yield that might include an overall time limit or not + EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, + "serialization action data time limit ${t}us exceeded", ("t", max) ); + }; + } + } + }; + struct variant_to_binary_context : public abi_traverse_context_with_path { using abi_traverse_context_with_path::abi_traverse_context_with_path; @@ -504,9 +510,7 @@ namespace impl { auto type = abi.get_action_type(act.name); if (!type.empty()) { try { - binary_to_variant_context _ctx(abi, ctx, type); - _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place - _ctx.start_action_serialization(); + action_data_to_variant_context _ctx(abi, ctx, type); mvo( "data", abi._binary_to_variant( type, act.data, _ctx )); } catch(...) { // any failure to serialize data, then leave as not serialized @@ -566,9 +570,7 @@ namespace impl { const abi_serializer& abi = *abi_optional; auto type = abi.get_action_result_type(act.name); if (!type.empty()) { - binary_to_variant_context _ctx(abi, ctx, type); - _ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place - _ctx.start_action_serialization(); + action_data_to_variant_context _ctx(abi, ctx, type); mvo( "return_value_data", abi._binary_to_variant( type, act_trace.return_value, _ctx )); } } From af766dc80e1ba2c3a1348efef995169f112b9660 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 12 May 2023 14:19:37 -0500 Subject: [PATCH 24/32] GH-1062 Simplify recursion_depth lambda --- libraries/chain/abi_serializer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/chain/abi_serializer.cpp b/libraries/chain/abi_serializer.cpp index 61520aab01..c06c6a4fb4 100644 --- a/libraries/chain/abi_serializer.cpp +++ b/libraries/chain/abi_serializer.cpp @@ -659,9 +659,7 @@ namespace eosio { namespace chain { namespace impl { fc::scoped_exit> abi_traverse_context::enter_scope() { - std::function callback = [old_recursion_depth=recursion_depth, this](){ - recursion_depth = old_recursion_depth; - }; + std::function callback = [this](){ --recursion_depth; }; ++recursion_depth; yield( recursion_depth ); From 07d5a7476a785e0281b02ec67a056adf909d3141 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 12 May 2023 19:43:11 -0500 Subject: [PATCH 25/32] GH-1062 assert on exclusive ownership of variant_object --- libraries/libfc/include/fc/variant_object.hpp | 2 ++ libraries/libfc/src/variant_object.cpp | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/libfc/include/fc/variant_object.hpp b/libraries/libfc/include/fc/variant_object.hpp index e687a5a50c..bd03279d2f 100644 --- a/libraries/libfc/include/fc/variant_object.hpp +++ b/libraries/libfc/include/fc/variant_object.hpp @@ -238,6 +238,7 @@ namespace fc explicit mutable_variant_object( const variant_object& ); /* * Use with care as the internal shared state of variant_object is moved. + * asserts on exclusive ownership of variant_object shared state. Not thread safe. */ explicit mutable_variant_object( variant_object&& ); @@ -246,6 +247,7 @@ namespace fc mutable_variant_object& operator=( const variant_object& ); /** * Use with care as the internal shared state of variant_object is moved. + * asserts on exclusive ownership of variant_object shared state. Not thread safe. */ mutable_variant_object& operator=( variant_object&& ); diff --git a/libraries/libfc/src/variant_object.cpp b/libraries/libfc/src/variant_object.cpp index de08d2a79f..61954b595f 100644 --- a/libraries/libfc/src/variant_object.cpp +++ b/libraries/libfc/src/variant_object.cpp @@ -287,8 +287,13 @@ namespace fc } mutable_variant_object::mutable_variant_object( variant_object&& obj ) - : _key_value( new std::vector(std::move(*obj._key_value)) ) + : _key_value( new std::vector() ) { + assert(obj._key_value.use_count() == 1); // should only be used if data not shared + if (obj._key_value.use_count() == 1) + *_key_value = std::move(*obj._key_value); + else + *_key_value = *obj._key_value; } mutable_variant_object::mutable_variant_object( const mutable_variant_object& obj ) @@ -309,7 +314,11 @@ namespace fc mutable_variant_object& mutable_variant_object::operator=( variant_object&& obj ) { - *_key_value = std::move(*obj._key_value); + assert(obj._key_value.use_count() == 1); // should only be used if data not shared + if (obj._key_value.use_count() == 1) + *_key_value = std::move(*obj._key_value); + else + *_key_value = *obj._key_value; return *this; } From 8ade32783d0befbe11cc8b060adc2eaa60e18a04 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 09:35:00 -0500 Subject: [PATCH 26/32] GH-1062 Fix merge issues --- plugins/http_plugin/tests/unit_tests.cpp | 14 +++++++------- .../producer_api_plugin/producer_api_plugin.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/http_plugin/tests/unit_tests.cpp b/plugins/http_plugin/tests/unit_tests.cpp index acdbef656c..b0c5e13748 100644 --- a/plugins/http_plugin/tests/unit_tests.cpp +++ b/plugins/http_plugin/tests/unit_tests.cpp @@ -484,31 +484,31 @@ BOOST_FIXTURE_TEST_CASE(valid_category_addresses, http_plugin_test_fixture) { http_plugin->add_api({{std::string("/v1/node/hello"), api_category::node, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}, {std::string("/v1/chain_ro/hello"), api_category::chain_ro, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}, {std::string("/v1/chain_rw/hello"), api_category::chain_rw, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}, {std::string("/v1/net_ro/hello"), api_category::net_ro, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}, {std::string("/v1/net_rw/hello"), api_category::net_rw, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}, {std::string("/v1/producer_ro/hello"), api_category::producer_ro, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}, {std::string("/v1/producer_rw/hello"), api_category::producer_rw, [&](string&&, string&& body, url_response_callback&& cb) { - cb(200, fc::time_point::maximum(), fc::variant("world!")); + cb(200, fc::variant("world!")); }}}, appbase::exec_queue::read_write); diff --git a/plugins/producer_api_plugin/producer_api_plugin.cpp b/plugins/producer_api_plugin/producer_api_plugin.cpp index 67751f5d77..0b825a10fd 100644 --- a/plugins/producer_api_plugin/producer_api_plugin.cpp +++ b/plugins/producer_api_plugin/producer_api_plugin.cpp @@ -23,7 +23,7 @@ using namespace eosio; #define CALL_WITH_400(api_name, category, api_handle, call_name, INVOKE, http_response_code) \ {std::string("/v1/" #api_name "/" #call_name), \ api_category::category, \ - [&api_handle](string&&, string&& body, url_response_callback&& cb) mutable { \ + [&](string&&, string&& body, url_response_callback&& cb) mutable { \ try { \ INVOKE \ cb(http_response_code, fc::variant(result)); \ From 6d0a06b00ffcdf4e51f3eedadfeeb9c10def27c2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 09:42:13 -0500 Subject: [PATCH 27/32] GH-1062 Add back check for correct table --- plugins/chain_plugin/chain_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 1992cd4526..6ae6c46781 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1762,7 +1762,7 @@ read_only::get_producers( const read_only::get_producers_params& params, const f if (fc::time_point::now() >= params_deadline) break; } - if( it != secondary_index_by_secondary.end() ) { + if( it != secondary_index_by_secondary.end() && it->t_id == secondary_table_id->id ) { result.more = name{it->primary_key}.to_string(); } From 87e598a4ad0925a07051e45970bc3492995cc6cc Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 09:46:08 -0500 Subject: [PATCH 28/32] GH-1062 Simplify deadline calculation. --- .../chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index d6f81ff6ea..5f4ed08f2e 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -157,8 +157,7 @@ class read_only : public api_base { // return deadline for call fc::time_point start() const { validate(); - return http_max_response_time == fc::microseconds::maximum() ? fc::time_point::maximum() - : fc::time_point::now() + http_max_response_time; + return fc::time_point::now().safe_add(http_max_response_time); } void set_shorten_abi_errors( bool f ) { shorten_abi_errors = f; } From 3dc18415b5868cd98bf3dee5896d1cc2d934a526 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 09:56:42 -0500 Subject: [PATCH 29/32] GH-1062 Make get_activated_protocol_features atomic --- plugins/chain_plugin/chain_plugin.cpp | 12 ++---------- .../include/eosio/chain_plugin/chain_plugin.hpp | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 6ae6c46781..aac9abb3ba 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1326,9 +1326,6 @@ read_only::get_activated_protocol_features( const read_only::get_activated_proto if( upper_bound_value < lower_bound_value ) return result; - fc::microseconds params_time_limit = params.time_limit_ms ? fc::milliseconds(*params.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); - auto walk_range = [&]( auto itr, auto end_itr, auto&& convert_iterator ) { fc::mutable_variant_object mvo; mvo( "activation_ordinal", 0 ); @@ -1337,18 +1334,13 @@ read_only::get_activated_protocol_features( const read_only::get_activated_proto auto& activation_ordinal_value = mvo["activation_ordinal"]; auto& activation_block_num_value = mvo["activation_block_num"]; - // activated protocol features unlikely to ever reach max_return_items - for( unsigned int count = 0; count < params.limit && itr != end_itr; ++itr, ++count ) { + // activated protocol features are naturally limited and unlikely to ever reach max_return_items + for( ; itr != end_itr; ++itr ) { const auto& conv_itr = convert_iterator( itr ); activation_ordinal_value = conv_itr.activation_ordinal(); activation_block_num_value = conv_itr.activation_block_num(); result.activated_protocol_features.emplace_back( conv_itr->to_variant( false, &mvo ) ); - if (fc::time_point::now() >= params_deadline) - break; - } - if( itr != end_itr ) { - result.more = convert_iterator( itr ).activation_ordinal() ; } }; diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index 5f4ed08f2e..eefbabcff7 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -217,10 +217,10 @@ class read_only : public api_base { struct get_activated_protocol_features_params { std::optional lower_bound; std::optional upper_bound; - uint32_t limit = 10; + uint32_t limit = std::numeric_limits::max(); // ignored bool search_by_block_num = false; bool reverse = false; - std::optional time_limit_ms; // defaults to 10ms + std::optional time_limit_ms; // ignored }; struct get_activated_protocol_features_results { From 2df745999fa1618163f61c54990a4319ecbd2135 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 10:39:13 -0500 Subject: [PATCH 30/32] GH-1062 Update test to reflect get_activate_protocol_features ignored limit and time limit --- tests/plugin_http_api_test.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/tests/plugin_http_api_test.py b/tests/plugin_http_api_test.py index 278b0d8a5b..285e6bb398 100755 --- a/tests/plugin_http_api_test.py +++ b/tests/plugin_http_api_test.py @@ -143,15 +143,9 @@ def test_ChainApi(self) : allFeatureCodenames.append(s['specification'][0]['value']) self.assertEqual(len(allFeatureDigests), len(allFeatureCodenames)) - # Default limit set in get_activated_protocol_features_params - ACT_FEATURE_DEFAULT_LIMIT = 10 if len(allFeatureCodenames) > 10 else len(allFeatureCodenames) - # Actual expected activated features total ACT_FEATURE_CURRENT_EXPECTED_TOTAL = len(allFeatureCodenames) - # Extemely high value to attempt to always get full list of activated features - ACT_FEATURE_EXTREME = 10000 - # get_consensus_parameters without parameter command = "get_consensus_parameters" ret_json = self.nodeos.processUrllibRequest(resource, command, endpoint=endpoint) @@ -169,14 +163,14 @@ def test_ChainApi(self) : command = "get_activated_protocol_features" ret_json = self.nodeos.processUrllibRequest(resource, command, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_DEFAULT_LIMIT) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) # get_activated_protocol_features with empty content parameter ret_json = self.nodeos.processUrllibRequest(resource, command, self.empty_content_dict, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_DEFAULT_LIMIT) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) for index, _ in enumerate(ret_json["payload"]["activated_protocol_features"]): @@ -191,7 +185,7 @@ def test_ChainApi(self) : payload = {"lower_bound":1} ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_DEFAULT_LIMIT) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL-1) # -1 since lower_bound=1 for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) @@ -199,7 +193,7 @@ def test_ChainApi(self) : payload = {"upper_bound":1000} ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_DEFAULT_LIMIT) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) @@ -213,15 +207,15 @@ def test_ChainApi(self) : self.assertTrue(dict_feature['activation_ordinal'] <= upper_bound_param) # get_activated_protocol_features with 3rd param - payload = {"limit":1} + payload = {"limit":1} # ignored by nodeos ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), 1) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) # get_activated_protocol_features with 3rd param to get expected full list of activated features - payload = {"limit":ACT_FEATURE_CURRENT_EXPECTED_TOTAL} + payload = {"limit":ACT_FEATURE_CURRENT_EXPECTED_TOTAL} # ignored by nodeos ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) @@ -232,9 +226,8 @@ def test_ChainApi(self) : for digest in allFeatureDigests: assert digest in str(ret_json["payload"]["activated_protocol_features"]), f"ERROR: Expected active feature \'{feature}\' not found in returned list." - # get_activated_protocol_features with 3rd param set extremely high to attempt to catch the - # addition of new features and fail and cause this test to be updated. - payload = {"limit":ACT_FEATURE_EXTREME} + # get_activated_protocol_features with 3rd param set extremely high + payload = {"limit":999999} # ignored by nodeos ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) @@ -245,7 +238,7 @@ def test_ChainApi(self) : payload = {"search_by_block_num":"true"} ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_DEFAULT_LIMIT) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) @@ -253,7 +246,7 @@ def test_ChainApi(self) : payload = {"reverse":"true"} ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(type(ret_json["payload"]["activated_protocol_features"]), list) - self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_DEFAULT_LIMIT) + self.assertEqual(len(ret_json["payload"]["activated_protocol_features"]), ACT_FEATURE_CURRENT_EXPECTED_TOTAL) for dict_feature in ret_json["payload"]["activated_protocol_features"]: self.assertTrue(dict_feature['feature_digest'] in allFeatureDigests) for index, _ in enumerate(ret_json["payload"]["activated_protocol_features"]): From bd896c7a81dd04468147922207bc99f6ed8dc702 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 12:08:17 -0500 Subject: [PATCH 31/32] GH-1062 Change default http-max-response-time-ms from 30 to 15 --- plugins/http_plugin/http_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/http_plugin/http_plugin.cpp b/plugins/http_plugin/http_plugin.cpp index b881b934b0..9b224604db 100644 --- a/plugins/http_plugin/http_plugin.cpp +++ b/plugins/http_plugin/http_plugin.cpp @@ -408,7 +408,7 @@ namespace eosio { "Maximum size in megabytes http_plugin should use for processing http requests. -1 for unlimited. 429 error response when exceeded." ) ("http-max-in-flight-requests", bpo::value()->default_value(-1), "Maximum number of requests http_plugin should use for processing http requests. 429 error response when exceeded." ) - ("http-max-response-time-ms", bpo::value()->default_value(30), + ("http-max-response-time-ms", bpo::value()->default_value(15), "Maximum time on main thread for processing a request, -1 for unlimited") ("verbose-http-errors", bpo::bool_switch()->default_value(false), "Append the error log to HTTP responses") From 0465384e31164f5491f13f95a4cbe0ed05e299b6 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 May 2023 12:09:26 -0500 Subject: [PATCH 32/32] GH-1062 If time_limit_ms not specified use http-max-response-time-ms instead of 10ms --- plugins/chain_plugin/chain_plugin.cpp | 9 +++------ .../include/eosio/chain_plugin/chain_plugin.hpp | 14 ++++++-------- .../producer_api_plugin/producer.swagger.yaml | 2 +- .../eosio/producer_plugin/producer_plugin.hpp | 2 +- plugins/producer_plugin/producer_plugin.cpp | 3 +-- programs/cleos/main.cpp | 16 ++++++++-------- 6 files changed, 20 insertions(+), 26 deletions(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index aac9abb3ba..598d73911e 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1585,8 +1585,7 @@ read_only::get_table_rows( const read_only::get_table_rows_params& p, const fc:: read_only::get_table_by_scope_result read_only::get_table_by_scope( const read_only::get_table_by_scope_params& p, const fc::time_point& deadline )const { - fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); + fc::time_point params_deadline = p.time_limit_ms ? std::min(fc::time_point::now().safe_add(fc::milliseconds(*p.time_limit_ms)), deadline) : deadline; read_only::get_table_by_scope_result result; const auto& d = db.db(); @@ -1739,8 +1738,7 @@ read_only::get_producers( const read_only::get_producers_params& params, const f boost::make_tuple(secondary_table_id->id, lower.to_uint64_t()))); }(); - fc::microseconds params_time_limit = params.time_limit_ms ? fc::milliseconds(*params.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); + fc::time_point params_deadline = params.time_limit_ms ? std::min(fc::time_point::now().safe_add(fc::milliseconds(*params.time_limit_ms)), deadline) : deadline; uint32_t limit = params.limit; if (deadline != fc::time_point::maximum() && limit > max_return_items) limit = max_return_items; @@ -1799,8 +1797,7 @@ read_only::get_producer_schedule_result read_only::get_producer_schedule( const read_only::get_scheduled_transactions_result read_only::get_scheduled_transactions( const read_only::get_scheduled_transactions_params& p, const fc::time_point& deadline ) const { - fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); + fc::time_point params_deadline = p.time_limit_ms ? std::min(fc::time_point::now().safe_add(fc::milliseconds(*p.time_limit_ms)), deadline) : deadline; const auto& d = db.db(); diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index eefbabcff7..e98bc1520f 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -422,7 +422,7 @@ class read_only : public api_base { string encode_type{"dec"}; //dec, hex , default=dec std::optional reverse; std::optional show_payer; // show RAM payer - std::optional time_limit_ms; // defaults to 10ms + std::optional time_limit_ms; // defaults to http-max-response-time-ms }; struct get_table_rows_result { @@ -442,7 +442,7 @@ class read_only : public api_base { string upper_bound; // upper bound of scope, optional uint32_t limit = 10; std::optional reverse; - std::optional time_limit_ms; // defaults to 10ms + std::optional time_limit_ms; // defaults to http-max-response-time-ms }; struct get_table_by_scope_result_row { name code; @@ -484,7 +484,7 @@ class read_only : public api_base { bool json = false; string lower_bound; uint32_t limit = 50; - std::optional time_limit_ms; // defaults to 10ms + std::optional time_limit_ms; // defaults to http-max-response-time-ms }; struct get_producers_result { @@ -510,7 +510,7 @@ class read_only : public api_base { bool json = false; string lower_bound; /// timestamp OR transaction ID uint32_t limit = 50; - std::optional time_limit_ms; // defaults to 10ms + std::optional time_limit_ms; // defaults to http-max-response-time-ms }; struct get_scheduled_transactions_result { @@ -572,8 +572,7 @@ class read_only : public api_base { const fc::time_point& deadline, ConvFn conv ) const { - fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); + fc::time_point params_deadline = p.time_limit_ms ? std::min(fc::time_point::now().safe_add(fc::milliseconds(*p.time_limit_ms)), deadline) : deadline; struct http_params_t { name table; @@ -705,8 +704,7 @@ class read_only : public api_base { abi_def&& abi, const fc::time_point& deadline ) const { - fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now().safe_add(params_time_limit), deadline); + fc::time_point params_deadline = p.time_limit_ms ? std::min(fc::time_point::now().safe_add(fc::milliseconds(*p.time_limit_ms)), deadline) : deadline; struct http_params_t { name table; diff --git a/plugins/producer_api_plugin/producer.swagger.yaml b/plugins/producer_api_plugin/producer.swagger.yaml index 2b53598319..25c7b70e90 100644 --- a/plugins/producer_api_plugin/producer.swagger.yaml +++ b/plugins/producer_api_plugin/producer.swagger.yaml @@ -735,7 +735,7 @@ paths: $ref: "https://docs.eosnetwork.com/openapi/v2.0/Sha256.yaml" time_limit_ms: type: integer - default: 10 + default: http-max-response-time-ms example: 10 responses: "200": diff --git a/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp b/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp index 74b8c02f95..1587a3d29b 100644 --- a/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp +++ b/plugins/producer_plugin/include/eosio/producer_plugin/producer_plugin.hpp @@ -112,7 +112,7 @@ class producer_plugin : public appbase::plugin { struct get_unapplied_transactions_params { string lower_bound; /// transaction id std::optional limit = 100; - std::optional time_limit_ms; // defaults to 10ms + std::optional time_limit_ms; // defaults to http-max-response-time-ms }; struct unapplied_trx { diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 7eb9837484..833a0bb992 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1552,8 +1552,7 @@ producer_plugin::get_account_ram_corrections( const get_account_ram_corrections_ producer_plugin::get_unapplied_transactions_result producer_plugin::get_unapplied_transactions( const get_unapplied_transactions_params& p, const fc::time_point& deadline ) const { - fc::microseconds params_time_limit = p.time_limit_ms ? fc::milliseconds(*p.time_limit_ms) : fc::milliseconds(10); - fc::time_point params_deadline = std::min(fc::time_point::now() + params_time_limit, deadline); + fc::time_point params_deadline = p.time_limit_ms ? std::min(fc::time_point::now().safe_add(fc::milliseconds(*p.time_limit_ms)), deadline) : deadline; auto& ua = my->_unapplied_transactions; diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 472b273f40..31bbc81532 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -1458,7 +1458,7 @@ struct unapprove_producer_subcommand { struct list_producers_subcommand { bool print_json = false; uint32_t limit = 50; - uint32_t time_limit_ms = 10; + uint32_t time_limit_ms = 0; std::string lower; list_producers_subcommand(CLI::App* actionRoot) { @@ -1466,11 +1466,11 @@ struct list_producers_subcommand { list_producers->add_flag("--json,-j", print_json, localized("Output in JSON format")); list_producers->add_option("-l,--limit", limit, localized("The maximum number of rows to return")); list_producers->add_option("-L,--lower", lower, localized("Lower bound value of key, defaults to first")); - list_producers->add_option("--time-limit", time_limit_ms, localized("Limit time of execution in milliseconds, defaults to 10ms")); + list_producers->add_option("--time-limit", time_limit_ms, localized("Limit time of execution in milliseconds")); list_producers->callback([this] { fc::mutable_variant_object mo; mo("json", true)("lower_bound", lower)("limit", limit); - if( time_limit_ms != 10 ) mo("time_limit_ms", time_limit_ms); + if( time_limit_ms != 0 ) mo("time_limit_ms", time_limit_ms); auto rawResult = call(get_producers_func, mo); if ( print_json ) { std::cout << fc::json::to_pretty_string(rawResult) << std::endl; @@ -3142,7 +3142,7 @@ int main( int argc, char** argv ) { string encode_type{"dec"}; bool binary = false; uint32_t limit = 10; - uint32_t time_limit_ms = 10; + uint32_t time_limit_ms = 0; string index_position; bool reverse = false; bool show_payer = false; @@ -3151,7 +3151,7 @@ int main( int argc, char** argv ) { getTable->add_option( "scope", scope, localized("The scope within the contract in which the table is found") )->required(); getTable->add_option( "table", table, localized("The name of the table as specified by the contract abi") )->required(); getTable->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") ); - getTable->add_option( "--time-limit", time_limit_ms, localized("Limit time of execution in milliseconds, defaults to 10ms")); + getTable->add_option( "--time-limit", time_limit_ms, localized("Limit time of execution in milliseconds")); getTable->add_option( "-k,--key", table_key, localized("Deprecated") ); getTable->add_option( "-L,--lower", lower, localized("JSON representation of lower bound value of key, defaults to first") ); getTable->add_option( "-U,--upper", upper, localized("JSON representation of upper bound value of key, defaults to last") ); @@ -3184,7 +3184,7 @@ int main( int argc, char** argv ) { ( "encode_type", encode_type ) ( "reverse", reverse ) ( "show_payer", show_payer ); - if( time_limit_ms != 10 ) mo( "time_limit_ms", time_limit_ms ); + if( time_limit_ms != 0 ) mo( "time_limit_ms", time_limit_ms ); auto result = call( get_table_func, mo ); std::cout << fc::json::to_pretty_string(result) @@ -3195,7 +3195,7 @@ int main( int argc, char** argv ) { getScope->add_option( "contract", code, localized("The contract who owns the table") )->required(); getScope->add_option( "-t,--table", table, localized("The name of the table as filter") ); getScope->add_option( "-l,--limit", limit, localized("The maximum number of rows to return") ); - getScope->add_option( "--time-limit", time_limit_ms, localized("Limit time of execution in milliseconds, defaults to 10ms")); + getScope->add_option( "--time-limit", time_limit_ms, localized("Limit time of execution in milliseconds")); getScope->add_option( "-L,--lower", lower, localized("Lower bound of scope") ); getScope->add_option( "-U,--upper", upper, localized("Upper bound of scope") ); getScope->add_flag("-r,--reverse", reverse, localized("Iterate in reverse order")); @@ -3207,7 +3207,7 @@ int main( int argc, char** argv ) { ( "upper_bound", upper ) ( "limit", limit ) ( "reverse", reverse ); - if( time_limit_ms != 10 ) mo( "time_limit_ms", time_limit_ms ); + if( time_limit_ms != 0 ) mo( "time_limit_ms", time_limit_ms ); auto result = call( get_table_by_scope_func, mo ); std::cout << fc::json::to_pretty_string(result)