From 75fb80b6f83ff4a404bff0495603c22e62db44f9 Mon Sep 17 00:00:00 2001 From: Huang-Ming Huang Date: Wed, 15 Mar 2023 08:27:45 -0500 Subject: [PATCH 1/3] get_raw_block api endpoint --- libraries/chain/block_log.cpp | 34 ++++++++------ libraries/chain/controller.cpp | 15 +++++++ .../chain/include/eosio/chain/block_log.hpp | 1 + .../chain/include/eosio/chain/controller.hpp | 4 ++ plugins/chain_api_plugin/chain_api_plugin.cpp | 9 +++- plugins/chain_plugin/chain_plugin.cpp | 43 +++++++++++++++++- .../eosio/chain_plugin/chain_plugin.hpp | 25 ++++++++--- programs/cleos/httpc.hpp | 2 + programs/cleos/main.cpp | 45 +++++++++++++------ tests/chain_plugin_tests.cpp | 12 ++--- tests/plugin_http_api_test.py | 37 +++++++++++++++ 11 files changed, 186 insertions(+), 41 deletions(-) diff --git a/libraries/chain/block_log.cpp b/libraries/chain/block_log.cpp index 10ce42dfbf..2d3858ce58 100644 --- a/libraries/chain/block_log.cpp +++ b/libraries/chain/block_log.cpp @@ -185,15 +185,15 @@ namespace eosio { namespace chain { } template - block_id_type read_block_id(Stream&& ds, uint32_t expect_block_num) { - block_header bh; + signed_block_header read_block_header(Stream&& ds, uint32_t expect_block_num) { + signed_block_header bh; fc::raw::unpack(ds, bh); EOS_ASSERT(bh.block_num() == expect_block_num, block_log_exception, "Wrong block header was read from block log.", ("returned", bh.block_num())("expected", expect_block_num)); - return bh.calculate_id(); + return bh; } /// Provide the read only view of the blocks.log file @@ -474,8 +474,8 @@ namespace eosio { namespace chain { virtual void reset(const chain_id_type& chain_id, uint32_t first_block_num) = 0; virtual void flush() = 0; - virtual signed_block_ptr read_block_by_num(uint32_t block_num) = 0; - virtual block_id_type read_block_id_by_num(uint32_t block_num) = 0; + virtual signed_block_ptr read_block_by_num(uint32_t block_num) = 0; + virtual std::optional read_block_header_by_num(uint32_t block_num) = 0; virtual uint32_t version() const = 0; @@ -512,7 +512,7 @@ namespace eosio { namespace chain { void flush() final {} signed_block_ptr read_block_by_num(uint32_t block_num) final { return {}; }; - block_id_type read_block_id_by_num(uint32_t block_num) final { return {}; }; + std::optional read_block_header_by_num(uint32_t block_num) final { return {}; }; uint32_t version() const final { return 0; } signed_block_ptr read_head() final { return {}; }; @@ -556,7 +556,7 @@ namespace eosio { namespace chain { virtual uint32_t working_block_file_first_block_num() { return preamble.first_block_num; } virtual void post_append(uint64_t pos) {} virtual signed_block_ptr retry_read_block_by_num(uint32_t block_num) { return {}; } - virtual block_id_type retry_read_block_id_by_num(uint32_t block_num) { return {}; } + virtual std::optional retry_read_block_header_by_num(uint32_t block_num) { return {}; } void append(const signed_block_ptr& b, const block_id_type& id, const std::vector& packed_block) override { @@ -609,14 +609,14 @@ namespace eosio { namespace chain { FC_LOG_AND_RETHROW() } - block_id_type read_block_id_by_num(uint32_t block_num) final { + std::optional read_block_header_by_num(uint32_t block_num) final { try { uint64_t pos = get_block_pos(block_num); if (pos != block_log::npos) { block_file.seek(pos); - return read_block_id(block_file, block_num); + return read_block_header(block_file, block_num); } - return retry_read_block_id_by_num(block_num); + return retry_read_block_header_by_num(block_num); } FC_LOG_AND_RETHROW() } @@ -1027,10 +1027,10 @@ namespace eosio { namespace chain { return {}; } - block_id_type retry_read_block_id_by_num(uint32_t block_num) final { + std::optional retry_read_block_header_by_num(uint32_t block_num) final { auto ds = catalog.ro_stream_for_block(block_num); if (ds) - return read_block_id(*ds, block_num); + return read_block_header(*ds, block_num); return {}; } @@ -1225,9 +1225,15 @@ namespace eosio { namespace chain { return my->read_block_by_num(block_num); } - block_id_type block_log::read_block_id_by_num(uint32_t block_num) const { + std::optional block_log::read_block_header_by_num(uint32_t block_num) const { std::lock_guard g(my->mtx); - return my->read_block_id_by_num(block_num); + return my->read_block_header_by_num(block_num); + } + + block_id_type block_log::read_block_id_by_num(uint32_t block_num) const { + auto bh = read_block_header_by_num(block_num); + if (bh) { return bh->calculate_id(); } + return {}; } uint64_t block_log::get_block_pos(uint32_t block_num) const { diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2fec7ac4e2..b1b72fe0d1 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3101,6 +3101,12 @@ signed_block_ptr controller::fetch_block_by_id( block_id_type id )const { return signed_block_ptr(); } +std::optional controller::fetch_block_header_by_id( block_id_type id )const { + auto state = my->fork_db.get_block(id); + if( state && state->block ) return state->header; + return my->blog.read_block_header_by_num( block_header::num_from_id(id) ); +} + signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { try { auto blk_state = fetch_block_state_by_number( block_num ); if( blk_state ) { @@ -3110,6 +3116,15 @@ signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { return my->blog.read_block_by_num(block_num); } FC_CAPTURE_AND_RETHROW( (block_num) ) } +std::optional controller::fetch_block_header_by_number( uint32_t block_num )const { try { + auto blk_state = fetch_block_state_by_number( block_num ); + if( blk_state ) { + return blk_state->header; + } + + return my->blog.read_block_header_by_num(block_num); +} FC_CAPTURE_AND_RETHROW( (block_num) ) } + block_state_ptr controller::fetch_block_state_by_id( block_id_type id )const { auto state = my->fork_db.get_block(id); return state; diff --git a/libraries/chain/include/eosio/chain/block_log.hpp b/libraries/chain/include/eosio/chain/block_log.hpp index a28cbc8d41..77099afe59 100644 --- a/libraries/chain/include/eosio/chain/block_log.hpp +++ b/libraries/chain/include/eosio/chain/block_log.hpp @@ -54,6 +54,7 @@ namespace eosio { namespace chain { void reset( const chain_id_type& chain_id, uint32_t first_block_num ); signed_block_ptr read_block_by_num(uint32_t block_num)const; + std::optional read_block_header_by_num(uint32_t block_num)const; block_id_type read_block_id_by_num(uint32_t block_num)const; signed_block_ptr read_block_by_id(const block_id_type& id)const { diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index fff0b8cab7..a55390b9fa 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -240,6 +240,10 @@ namespace eosio { namespace chain { signed_block_ptr fetch_block_by_number( uint32_t block_num )const; // thread-safe signed_block_ptr fetch_block_by_id( block_id_type id )const; + // thread-safe + std::optional fetch_block_header_by_number( uint32_t block_num )const; + // thread-safe + std::optional fetch_block_header_by_id( block_id_type id )const; // return block_state from forkdb, thread-safe block_state_ptr fetch_block_state_by_number( uint32_t block_num )const; // return block_state from forkdb, thread-safe diff --git a/plugins/chain_api_plugin/chain_api_plugin.cpp b/plugins/chain_api_plugin/chain_api_plugin.cpp index 4a06e0aea4..6836bcba76 100644 --- a/plugins/chain_api_plugin/chain_api_plugin.cpp +++ b/plugins/chain_api_plugin/chain_api_plugin.cpp @@ -111,6 +111,11 @@ void chain_api_plugin::plugin_startup() { }); } + _http_plugin.add_async_api({ + CHAIN_RO_CALL_WITH_400(get_raw_block, 200, http_params_types::params_required), + CHAIN_RO_CALL_WITH_400(get_block_header, 200, http_params_types::params_required) + }); + if (chain.transaction_finality_status_enabled()) { _http_plugin.add_api({ CHAIN_RO_CALL_WITH_400(get_transaction_status, 200, http_params_types::params_required), @@ -124,9 +129,9 @@ void chain_api_plugin::plugin_startup() { auto deadline = ro_api.start(); try { auto start = fc::time_point::now(); - auto params = parse_params(body); + auto params = parse_params(body); FC_CHECK_DEADLINE( deadline ); - chain::signed_block_ptr block = ro_api.get_block( params, deadline ); + chain::signed_block_ptr block = ro_api.get_raw_block( params, deadline ); auto abi_cache = ro_api.get_block_serializers( block, max_time ); FC_CHECK_DEADLINE( deadline ); diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 5d96e44055..cfbf9f511b 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1903,7 +1903,7 @@ read_only::get_scheduled_transactions( const read_only::get_scheduled_transactio return result; } -chain::signed_block_ptr read_only::get_block(const read_only::get_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& deadline) const { signed_block_ptr block; std::optional block_num; @@ -1930,6 +1930,47 @@ chain::signed_block_ptr read_only::get_block(const read_only::get_block_params& return block; } +read_only::get_block_header_result read_only::get_block_header(const read_only::get_block_header_params& params, const fc::time_point& deadline) const{ + std::optional block_num; + + EOS_ASSERT( !params.block_num_or_id.empty() && params.block_num_or_id.size() <= 64, + chain::block_id_type_exception, + "Invalid Block number or ID, must be greater than 0 and less than 64 characters" + ); + + try { + block_num = fc::to_uint64(params.block_num_or_id); + } catch( ... ) {} + + if (!params.include_extensions) { + std::optional header; + + if( block_num ) { + header = db.fetch_block_header_by_number( *block_num ); + } else { + try { + header = db.fetch_block_header_by_id( fc::variant(params.block_num_or_id).as() ); + } EOS_RETHROW_EXCEPTIONS(chain::block_id_type_exception, "Invalid block ID: ${block_num_or_id}", ("block_num_or_id", params.block_num_or_id)) + } + EOS_ASSERT( header, unknown_block_exception, "Could not find block header: ${block}", ("block", params.block_num_or_id)); + return { header->calculate_id(), fc::variant{*header}, {}}; + } else { + signed_block_ptr block; + if( block_num ) { + block = db.fetch_block_by_number( *block_num ); + } else { + try { + block = db.fetch_block_by_id( fc::variant(params.block_num_or_id).as() ); + } EOS_RETHROW_EXCEPTIONS(chain::block_id_type_exception, "Invalid block ID: ${block_num_or_id}", ("block_num_or_id", params.block_num_or_id)) + } + 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); + +} + std::unordered_map> 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 ); 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 b0a2267cea..63a49a92c4 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -325,11 +325,11 @@ class read_only { get_transaction_id_result get_transaction_id( const get_transaction_id_params& params, const fc::time_point& deadline)const; - struct get_block_params { + struct get_raw_block_params { string block_num_or_id; }; - chain::signed_block_ptr get_block(const get_block_params& params, const fc::time_point& deadline) const; + chain::signed_block_ptr get_raw_block(const get_raw_block_params& params, const fc::time_point& deadline) const; // call from app() thread std::unordered_map> get_block_serializers( const chain::signed_block_ptr& block, const fc::microseconds& max_time ) const; @@ -338,6 +338,19 @@ class read_only { std::unordered_map> abi_cache, const fc::microseconds& max_time ) const; + struct get_block_header_params { + string block_num_or_id; + bool include_extensions = false; // include block extensions (requires reading entire block off disk) + }; + + struct get_block_header_result { + chain::block_id_type id; + fc::variant signed_block_header; + std::optional block_extensions; + }; + + get_block_header_result get_block_header(const get_block_header_params& params, const fc::time_point& deadline) const; + struct get_block_info_params { uint32_t block_num = 0; }; @@ -782,7 +795,7 @@ class read_write { // which is the format used by secondary index uint8_t buffer[32]; memcpy(buffer, v.data(), 32); - fixed_bytes<32> fb(buffer); + fixed_bytes<32> fb(buffer); return chain::key256_t(fb.get_array()); }; } @@ -800,7 +813,7 @@ class read_write { // which is the format used by secondary index uint8_t buffer[20]; memcpy(buffer, v.data(), 20); - fixed_bytes<20> fb(buffer); + fixed_bytes<20> fb(buffer); return chain::key256_t(fb.get_array()); }; } @@ -895,9 +908,11 @@ FC_REFLECT(eosio::chain_apis::read_only::get_transaction_status_results, (state) (head_timestamp)(irreversible_number)(irreversible_id)(irreversible_timestamp)(earliest_tracked_block_id)(earliest_tracked_block_number) ) FC_REFLECT(eosio::chain_apis::read_only::get_activated_protocol_features_params, (lower_bound)(upper_bound)(limit)(search_by_block_num)(reverse)(time_limit_ms) ) FC_REFLECT(eosio::chain_apis::read_only::get_activated_protocol_features_results, (activated_protocol_features)(more) ) -FC_REFLECT(eosio::chain_apis::read_only::get_block_params, (block_num_or_id)) +FC_REFLECT(eosio::chain_apis::read_only::get_raw_block_params, (block_num_or_id)) FC_REFLECT(eosio::chain_apis::read_only::get_block_info_params, (block_num)) FC_REFLECT(eosio::chain_apis::read_only::get_block_header_state_params, (block_num_or_id)) +FC_REFLECT(eosio::chain_apis::read_only::get_block_header_params, (block_num_or_id)(include_extensions)) +FC_REFLECT(eosio::chain_apis::read_only::get_block_header_result, (id)(signed_block_header)(block_extensions)) FC_REFLECT( eosio::chain_apis::read_write::push_transaction_results, (transaction_id)(processed) ) FC_REFLECT( eosio::chain_apis::read_write::send_transaction2_params, (return_failure_trace)(retry_trx)(retry_trx_num_blocks)(transaction) ) diff --git a/programs/cleos/httpc.hpp b/programs/cleos/httpc.hpp index 6b4296b1ec..ad0b047cf2 100644 --- a/programs/cleos/httpc.hpp +++ b/programs/cleos/httpc.hpp @@ -30,6 +30,8 @@ namespace eosio { namespace client { namespace http { const string compute_txn_func = chain_func_base + "/compute_transaction"; const string push_txns_func = chain_func_base + "/push_transactions"; const string get_block_func = chain_func_base + "/get_block"; + const string get_raw_block_func = chain_func_base + "/get_raw_block"; + const string get_block_header_func = chain_func_base + "/get_block_header"; const string get_block_info_func = chain_func_base + "/get_block_info"; const string get_block_header_state_func = chain_func_base + "/get_block_header_state"; const string get_account_func = chain_func_base + "/get_account"; diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index cf8465ed02..a9aee44452 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -2771,6 +2771,15 @@ struct set_url_no_trailing_slash { } }; +struct get_block_params { + string blockArg; + bool get_bhs = false; + bool get_binfo = false; + bool get_braw = false; + bool get_bheader = false; + bool get_bheader_extensions = false; +}; + int main( int argc, char** argv ) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); @@ -3005,29 +3014,39 @@ int main( int argc, char** argv ) { }); // get block - string blockArg; - bool get_bhs = false; - bool get_binfo = false; + get_block_params params; auto getBlock = get->add_subcommand("block", localized("Retrieve a full block from the blockchain")); - getBlock->add_option("block", blockArg, localized("The number or ID of the block to retrieve"))->required(); - getBlock->add_flag("--header-state", get_bhs, localized("Get block header state from fork database instead") ); - getBlock->add_flag("--info", get_binfo, localized("Get block info from the blockchain by block num only") ); - getBlock->callback([&blockArg, &get_bhs, &get_binfo] { - EOSC_ASSERT( !(get_bhs && get_binfo), "ERROR: Either --header-state or --info can be set" ); - if (get_binfo) { + getBlock->add_option("block", params.blockArg, localized("The number or ID of the block to retrieve"))->required(); + getBlock->add_flag("--header-state", params.get_bhs, localized("Get block header state from fork database instead") ); + getBlock->add_flag("--info", params.get_binfo, localized("Get block info from the blockchain by block num only") ); + getBlock->add_flag("--raw", params.get_braw, localized("Get raw block from the blockchain") ); + getBlock->add_flag("--header", params.get_bheader, localized("Get block header from the blockchain") ); + getBlock->add_flag("--header-with-extensions", params.get_bheader_extensions, localized("Get block header with block exntesions from the blockchain") ); + + getBlock->callback([¶ms] { + int num_flags = params.get_bhs + params.get_binfo + params.get_braw + params.get_bheader + params.get_bheader_extensions; + EOSC_ASSERT( num_flags <= 1, "ERROR: Only one of the following flags can be set: --header-state, --info, --raw, --header, --header-with-extensions." ); + if (params.get_binfo) { std::optional block_num; try { - block_num = fc::to_int64(blockArg); + block_num = fc::to_int64(params.blockArg); } catch (...) { // error is handled in assertion below } - EOSC_ASSERT( block_num.has_value() && (*block_num > 0), "Invalid block num: ${block_num}", ("block_num", blockArg) ); + EOSC_ASSERT( block_num.has_value() && (*block_num > 0), "Invalid block num: ${block_num}", ("block_num", params.blockArg) ); const auto arg = fc::variant_object("block_num", static_cast(*block_num)); std::cout << fc::json::to_pretty_string(call(get_block_info_func, arg)) << std::endl; } else { - const auto arg = fc::variant_object("block_num_or_id", blockArg); - if (get_bhs) { + const auto arg = fc::variant_object("block_num_or_id", params.blockArg); + if (params.get_bhs) { std::cout << fc::json::to_pretty_string(call(get_block_header_state_func, arg)) << std::endl; + } else if (params.get_braw) { + std::cout << fc::json::to_pretty_string(call(get_raw_block_func, arg)) << std::endl; + } else if (params.get_bheader || params.get_bheader_extensions) { + std::cout << fc::json::to_pretty_string( + call(get_block_header_func, + fc::mutable_variant_object("block_num_or_id", params.blockArg) + ("include_extensions", params.get_bheader_extensions))) << std::endl; } else { std::cout << fc::json::to_pretty_string(call(get_block_func, arg)) << std::endl; } diff --git a/tests/chain_plugin_tests.cpp b/tests/chain_plugin_tests.cpp index 914c83c1d9..44fd1bbd00 100644 --- a/tests/chain_plugin_tests.cpp +++ b/tests/chain_plugin_tests.cpp @@ -88,11 +88,11 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, TESTER ) try { char headnumstr[20]; sprintf(headnumstr, "%d", headnum); - chain_apis::read_only::get_block_params param{headnumstr}; + chain_apis::read_only::get_raw_block_params param{headnumstr}; chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), fc::microseconds::maximum(), {}, {}); // block should be decoded successfully - auto block = plugin.get_block(param, fc::time_point::maximum()); + 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())); BOOST_TEST(block_str.find("procassert") != std::string::npos); @@ -112,7 +112,7 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, TESTER ) try { BOOST_CHECK_THROW(resolver("asserter"_n), invalid_type_inside_abi); // get the same block as string, results in decode failed(invalid abi) but not exception - auto block2 = plugin.get_block(param, fc::time_point::maximum()); + 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())); BOOST_TEST(block_str2.find("procassert") != std::string::npos); @@ -185,7 +185,7 @@ BOOST_FIXTURE_TEST_CASE( get_account, TESTER ) try { BOOST_REQUIRE_EQUAL(2, result.permissions.size()); if (result.permissions.size() > 1) { auto perm = result.permissions[0]; - BOOST_REQUIRE_EQUAL(name("active"_n), perm.perm_name); + BOOST_REQUIRE_EQUAL(name("active"_n), perm.perm_name); BOOST_REQUIRE_EQUAL(name("owner"_n), perm.parent); auto auth = perm.required_auth; BOOST_REQUIRE_EQUAL(1, auth.threshold); @@ -194,8 +194,8 @@ BOOST_FIXTURE_TEST_CASE( get_account, TESTER ) try { BOOST_REQUIRE_EQUAL(0, auth.waits.size()); perm = result.permissions[1]; - BOOST_REQUIRE_EQUAL(name("owner"_n), perm.perm_name); - BOOST_REQUIRE_EQUAL(name(""_n), perm.parent); + BOOST_REQUIRE_EQUAL(name("owner"_n), perm.perm_name); + BOOST_REQUIRE_EQUAL(name(""_n), perm.parent); auth = perm.required_auth; BOOST_REQUIRE_EQUAL(1, auth.threshold); BOOST_REQUIRE_EQUAL(1, auth.keys.size()); diff --git a/tests/plugin_http_api_test.py b/tests/plugin_http_api_test.py index 61e93f1a78..4a9f36e4eb 100755 --- a/tests/plugin_http_api_test.py +++ b/tests/plugin_http_api_test.py @@ -250,6 +250,43 @@ def test_ChainApi(self) : ret_json = self.nodeos.processUrllibRequest(resource, command, payload) self.assertEqual(ret_json["payload"]["block_num"], 1) + # get_raw_block with empty parameter + command = "get_raw_block" + ret_json = self.nodeos.processUrllibRequest(resource, command) + self.assertEqual(ret_json["code"], 400) + # get_block with empty content parameter + ret_json = self.nodeos.processUrllibRequest(resource, command, self.empty_content_dict) + self.assertEqual(ret_json["code"], 400) + # get_block with invalid parameter + ret_json = self.nodeos.processUrllibRequest(resource, command, self.http_post_invalid_param) + self.assertEqual(ret_json["code"], 400) + # get_block with valid parameter + payload = {"block_num_or_id":1} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload) + self.assertTrue("action_mroot" in ret_json["payload"]) + + # get_block_header with empty parameter + command = "get_block_header" + ret_json = self.nodeos.processUrllibRequest(resource, command) + self.assertEqual(ret_json["code"], 400) + # get_block with empty content parameter + ret_json = self.nodeos.processUrllibRequest(resource, command, self.empty_content_dict) + self.assertEqual(ret_json["code"], 400) + # get_block with invalid parameter + ret_json = self.nodeos.processUrllibRequest(resource, command, self.http_post_invalid_param) + self.assertEqual(ret_json["code"], 400) + # get_block with valid parameters + payload = {"block_num_or_id":1, "include_extensions": True} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload) + self.assertTrue("id" in ret_json["payload"]) + self.assertTrue("signed_block_header" in ret_json["payload"]) + self.assertTrue("block_extensions" in ret_json["payload"]) + payload = {"block_num_or_id":1, "include_extensions": False} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload) + self.assertTrue("id" in ret_json["payload"]) + self.assertTrue("signed_block_header" in ret_json["payload"]) + self.assertFalse("block_extensions" in ret_json["payload"]) + # get_block_info with empty parameter command = "get_block_info" ret_json = self.nodeos.processUrllibRequest(resource, command) From e6020fbec271f4843e567e78aa589f7ecbb89c71 Mon Sep 17 00:00:00 2001 From: Huang-Ming Huang Date: Wed, 15 Mar 2023 17:10:50 -0500 Subject: [PATCH 2/3] address PR comments --- libraries/chain/block_log.cpp | 3 ++- libraries/chain/controller.cpp | 8 +++++--- libraries/chain/include/eosio/chain/controller.hpp | 4 ++-- tests/chain_plugin_tests.cpp | 8 ++++++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/libraries/chain/block_log.cpp b/libraries/chain/block_log.cpp index 2d3858ce58..3b7d95ade0 100644 --- a/libraries/chain/block_log.cpp +++ b/libraries/chain/block_log.cpp @@ -474,7 +474,7 @@ namespace eosio { namespace chain { virtual void reset(const chain_id_type& chain_id, uint32_t first_block_num) = 0; virtual void flush() = 0; - virtual signed_block_ptr read_block_by_num(uint32_t block_num) = 0; + virtual signed_block_ptr read_block_by_num(uint32_t block_num) = 0; virtual std::optional read_block_header_by_num(uint32_t block_num) = 0; virtual uint32_t version() const = 0; @@ -1231,6 +1231,7 @@ namespace eosio { namespace chain { } block_id_type block_log::read_block_id_by_num(uint32_t block_num) const { + // read_block_header_by_num acquires mutex auto bh = read_block_header_by_num(block_num); if (bh) { return bh->calculate_id(); } return {}; diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index b1b72fe0d1..7ffe65c499 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3093,7 +3093,7 @@ const global_property_object& controller::get_global_properties()const { return my->db.get(); } -signed_block_ptr controller::fetch_block_by_id( block_id_type id )const { +signed_block_ptr controller::fetch_block_by_id( const block_id_type& id )const { auto state = my->fork_db.get_block(id); if( state && state->block ) return state->block; auto bptr = my->blog.read_block_by_num( block_header::num_from_id(id) ); @@ -3101,10 +3101,12 @@ signed_block_ptr controller::fetch_block_by_id( block_id_type id )const { return signed_block_ptr(); } -std::optional controller::fetch_block_header_by_id( block_id_type id )const { +std::optional controller::fetch_block_header_by_id( const block_id_type& id )const { auto state = my->fork_db.get_block(id); if( state && state->block ) return state->header; - return my->blog.read_block_header_by_num( block_header::num_from_id(id) ); + auto result = my->blog.read_block_header_by_num( block_header::num_from_id(id) ); + if( result && result->calculate_id() == id ) return result; + return {}; } signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { try { diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index a55390b9fa..64046876a0 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -239,11 +239,11 @@ namespace eosio { namespace chain { // thread-safe signed_block_ptr fetch_block_by_number( uint32_t block_num )const; // thread-safe - signed_block_ptr fetch_block_by_id( block_id_type id )const; + signed_block_ptr fetch_block_by_id( const block_id_type& id )const; // thread-safe std::optional fetch_block_header_by_number( uint32_t block_num )const; // thread-safe - std::optional fetch_block_header_by_id( block_id_type id )const; + std::optional fetch_block_header_by_id( const block_id_type& id )const; // return block_state from forkdb, thread-safe block_state_ptr fetch_block_state_by_number( uint32_t block_num )const; // return block_state from forkdb, thread-safe diff --git a/tests/chain_plugin_tests.cpp b/tests/chain_plugin_tests.cpp index 44fd1bbd00..2c81a4fc92 100644 --- a/tests/chain_plugin_tests.cpp +++ b/tests/chain_plugin_tests.cpp @@ -120,6 +120,14 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, TESTER ) try { BOOST_TEST(block_str2.find("Should Not Assert!") == std::string::npos); // decode failed BOOST_TEST(block_str2.find("011253686f756c64204e6f742041737365727421") != std::string::npos); //action data + + chain_apis::read_only::get_block_header_params bh_param{headnumstr, false}; + auto get_bh_result = plugin.get_block_header(bh_param, fc::time_point::maximum()); + + BOOST_TEST(get_bh_result.id == block->calculate_id()); + BOOST_TEST(json::to_string(get_bh_result.signed_block_header, fc::time_point::maximum()) == + json::to_string(fc::variant{static_cast(*block)}, fc::time_point::maximum())); + } FC_LOG_AND_RETHROW() /// get_block_with_invalid_abi BOOST_FIXTURE_TEST_CASE( get_consensus_parameters, TESTER ) try { From af1dcf23cf99a4236ef589f87e8c057167e0fece Mon Sep 17 00:00:00 2001 From: Huang-Ming Huang Date: Thu, 16 Mar 2023 15:35:42 -0500 Subject: [PATCH 3/3] fix assertion message --- plugins/chain_plugin/chain_plugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index cfbf9f511b..86d877b413 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1909,7 +1909,7 @@ chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_ EOS_ASSERT( !params.block_num_or_id.empty() && params.block_num_or_id.size() <= 64, chain::block_id_type_exception, - "Invalid Block number or ID, must be greater than 0 and less than 64 characters" + "Invalid Block number or ID, must be greater than 0 and less than 65 characters" ); try { @@ -1935,7 +1935,7 @@ read_only::get_block_header_result read_only::get_block_header(const read_only:: EOS_ASSERT( !params.block_num_or_id.empty() && params.block_num_or_id.size() <= 64, chain::block_id_type_exception, - "Invalid Block number or ID, must be greater than 0 and less than 64 characters" + "Invalid Block number or ID, must be greater than 0 and less than 65 characters" ); try {