Skip to content

Commit

Permalink
GH-677 Refactor get_block into multiple methods for testing. Also fix…
Browse files Browse the repository at this point in the history
… issue with invalid abi should not throw exception.
  • Loading branch information
heifner committed Feb 7, 2023
1 parent 38f3988 commit b7fdd1c
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 39 deletions.
41 changes: 4 additions & 37 deletions plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ void chain_api_plugin::plugin_startup() {

_http_plugin.add_api({
{ std::string("/v1/chain/get_block"),
[ro_api, &chain, &_http_plugin, max_time=std::min(chain.get_abi_serializer_max_time(),max_response_time)]
[ro_api, &_http_plugin, max_time=std::min(chain.get_abi_serializer_max_time(),max_response_time)]
( string, string body, url_response_callback cb ) mutable {
auto deadline = ro_api.start();
try {
Expand All @@ -161,50 +161,17 @@ void chain_api_plugin::plugin_startup() {
FC_CHECK_DEADLINE( deadline );
chain::signed_block_ptr block = ro_api.get_block( params, deadline );

auto yield = abi_serializer::create_yield_function( max_time );
std::unordered_map<account_name, std::optional<abi_serializer> > abi_cache;
auto add_to_cache = [&]( const chain::action& a ) {
auto it = abi_cache.find( a.account );
if( it == abi_cache.end() )
abi_cache.emplace_hint( it, a.account, chain.chain().get_abi_serializer( a.account, yield ) );
};
for( const auto& receipt: block->transactions ) {
if( std::holds_alternative<chain::packed_transaction>( receipt.trx ) ) {
const auto& pt = std::get<chain::packed_transaction>( receipt.trx );
const auto& t = pt.get_transaction();
for( const auto& a: t.actions )
add_to_cache( a );
for( const auto& a: t.context_free_actions )
add_to_cache( a );
}
}
auto abi_cache = ro_api.get_block_serializers( block, max_time );
FC_CHECK_DEADLINE( deadline );

auto post_time = fc::time_point::now();
auto remaining_time = max_time - (post_time - start);
_http_plugin.post_http_thread_pool(
[cb, deadline, post_time, remaining_time, abi_cache{std::move(abi_cache)}, block{std::move( block )}]() {
[ro_api, cb, deadline, post_time, remaining_time, abi_cache{std::move(abi_cache)}, block{std::move( block )}]() {
try {
auto new_deadline = deadline + (fc::time_point::now() - post_time);

auto abi_serializer_resolver = [&abi_cache](const account_name& account) -> std::optional<abi_serializer> {
auto it = abi_cache.find( account );
if( it != abi_cache.end() )
return it->second;
return {};
};

fc::variant pretty_output;
abi_serializer::to_variant( *block, pretty_output, abi_serializer_resolver,
abi_serializer::create_yield_function( remaining_time ) );

const auto block_id = block->calculate_id();
uint32_t ref_block_prefix = block_id._hash[1];

fc::variant result = fc::mutable_variant_object( pretty_output.get_object() )
( "id", block_id )
( "block_num", block->block_num() )
( "ref_block_prefix", ref_block_prefix );
fc::variant result = ro_api.convert_block( block, abi_cache, remaining_time );

cb( 200, new_deadline, std::move( result ) );
} catch( ... ) {
Expand Down
52 changes: 52 additions & 0 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,58 @@ chain::signed_block_ptr read_only::get_block(const read_only::get_block_params&
return block;
}

std::unordered_map<account_name, std::optional<abi_serializer>>
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 );
auto resolver = make_resolver(db, yield );
std::unordered_map<account_name, std::optional<abi_serializer> > abi_cache;
auto add_to_cache = [&]( const chain::action& a ) {
auto it = abi_cache.find( a.account );
if( it == abi_cache.end() ) {
try {
abi_cache.emplace_hint( it, a.account, resolver( a.account ) );
} catch( ... ) {
// keep behavior of not throwing on invalid abi, will result in hex data
}
}
};
for( const auto& receipt: block->transactions ) {
if( std::holds_alternative<chain::packed_transaction>( receipt.trx ) ) {
const auto& pt = std::get<chain::packed_transaction>( receipt.trx );
const auto& t = pt.get_transaction();
for( const auto& a: t.actions )
add_to_cache( a );
for( const auto& a: t.context_free_actions )
add_to_cache( a );
}
}
return abi_cache;
}

fc::variant read_only::convert_block( const chain::signed_block_ptr& block,
std::unordered_map<account_name, std::optional<abi_serializer>> abi_cache,
const fc::microseconds& max_time ) const {

auto abi_serializer_resolver = [&abi_cache](const account_name& account) -> std::optional<abi_serializer> {
auto it = abi_cache.find( account );
if( it != abi_cache.end() )
return it->second;
return {};
};

fc::variant pretty_output;
abi_serializer::to_variant( *block, pretty_output, abi_serializer_resolver,
abi_serializer::create_yield_function( 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() )
( "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 {

signed_block_ptr block;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,13 @@ class read_only {
};

chain::signed_block_ptr get_block(const get_block_params& params, const fc::time_point& deadline) const;
// call from app() thread
std::unordered_map<account_name, std::optional<abi_serializer>>
get_block_serializers( const chain::signed_block_ptr& block, const fc::microseconds& max_time ) const;
// call from any thread
fc::variant convert_block( const chain::signed_block_ptr& block,
std::unordered_map<account_name, std::optional<abi_serializer>> abi_cache,
const fc::microseconds& max_time ) const;

struct get_block_info_params {
uint32_t block_num = 0;
Expand Down
8 changes: 6 additions & 2 deletions tests/chain_plugin_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ BOOST_FIXTURE_TEST_CASE( get_block_with_invalid_abi, TESTER ) try {
chain_apis::read_only plugin(*(this->control), {}, fc::microseconds::maximum(), fc::microseconds::maximum(), {}, {});

// block should be decoded successfully
std::string block_str = json::to_pretty_string(plugin.get_block(param, fc::time_point::maximum()));
auto block = plugin.get_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);
BOOST_TEST(block_str.find("condition") != std::string::npos);
BOOST_TEST(block_str.find("Should Not Assert!") != std::string::npos);
Expand All @@ -111,7 +113,9 @@ 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
std::string block_str2 = json::to_pretty_string(plugin.get_block(param, fc::time_point::maximum()));
auto block2 = plugin.get_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);
BOOST_TEST(block_str2.find("condition") == std::string::npos); // decode failed
BOOST_TEST(block_str2.find("Should Not Assert!") == std::string::npos); // decode failed
Expand Down

0 comments on commit b7fdd1c

Please sign in to comment.