From 00f7cbdedfe385cb5ac8032399f36c36b012b217 Mon Sep 17 00:00:00 2001 From: Reverse Flash Date: Tue, 13 Sep 2016 11:25:11 -0400 Subject: [PATCH 1/3] add extra data to discussion to capture reblog state --- libraries/app/database_api.cpp | 2 ++ libraries/app/include/steemit/app/state.hpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index f840175587..b8402e4d74 100755 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1573,6 +1573,8 @@ state database_api::get_state( string path )const eacnt.feed->push_back( link ); _state.content[ link ] = my->_db.get_comment( f.author, f.permlink ); set_pending_payout( _state.content[ link ] ); + if( f.reblog_by.size() ) + _state.content[link].first_reblogged_by = f.reblog_by; } } } diff --git a/libraries/app/include/steemit/app/state.hpp b/libraries/app/include/steemit/app/state.hpp index bac68b8928..d79dbf5546 100644 --- a/libraries/app/include/steemit/app/state.hpp +++ b/libraries/app/include/steemit/app/state.hpp @@ -67,6 +67,7 @@ namespace steemit { namespace app { vector replies; ///< author/slug mapping share_type author_reputation = 0; asset promoted = asset(0, SBD_SYMBOL); + optional first_reblogged_by; }; /** @@ -176,7 +177,7 @@ FC_REFLECT( steemit::app::account_vote, (authorperm)(weight)(rshares)(percent)(t FC_REFLECT( steemit::app::discussion_index, (category)(trending)(trending30)(updated)(created)(responses)(active)(votes)(maturing)(best)(hot)(promoted)(cashout) ) FC_REFLECT( steemit::app::category_index, (trending)(active)(recent)(best) ) -FC_REFLECT_DERIVED( steemit::app::discussion, (steemit::chain::comment_object), (url)(root_title)(pending_payout_value)(total_pending_payout_value)(active_votes)(replies)(author_reputation)(promoted) ) +FC_REFLECT_DERIVED( steemit::app::discussion, (steemit::chain::comment_object), (url)(root_title)(pending_payout_value)(total_pending_payout_value)(active_votes)(replies)(author_reputation)(promoted)(first_reblogged_by) ) FC_REFLECT( steemit::app::state, (current_route)(props)(category_idx)(categories)(content)(accounts)(pow_queue)(witnesses)(discussion_idx)(witness_schedule)(feed_price)(error)(market_data) ) From edadec5e77da3307c4753f28f6369099a3ceef80 Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Fri, 16 Sep 2016 12:10:32 -0400 Subject: [PATCH 2/3] Update get_state to use follow plugin blog --- libraries/app/database_api.cpp | 74 +++++++++++++++---- .../app/include/steemit/app/database_api.hpp | 2 + libraries/plugins/follow/follow_plugin.cpp | 1 + 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index b8402e4d74..d6780a60c3 100755 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1269,6 +1269,49 @@ vector database_api::get_discussions_by_feed( const discussion_query result.reserve( query.limit ); while( result.size() < query.limit && feed_itr != f_idx.end() ) + { + if( feed_itr->account != account.id ) + break; + try + { + result.push_back( get_discussion( feed_itr->comment ) ); + if( feed_itr->first_reblogged_by != account_id_type() ) + result.back().first_reblogged_by = feed_itr->first_reblogged_by( my->_db ).name; + } + catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + } + + ++feed_itr; + } + return result; +} + +vector database_api::get_discussions_by_blog( const discussion_query& query )const +{ + query.validate(); + FC_ASSERT( my->_follow_api, "Node is not running the follow plugin" ); + auto start_author = query.start_author ? *( query.start_author ) : ""; + auto start_permlink = query.start_permlink ? *( query.start_permlink ) : ""; + + const auto& account = my->_db.get_account( query.tag ); + + const auto& c_idx = my->_db.get_index_type< follow::blog_index >().indices().get< follow::by_comment >(); + const auto& b_idx = my->_db.get_index_type< follow::blog_index >().indices().get< follow::by_blog >(); + auto feed_itr = b_idx.lower_bound( account.id ); + + if( start_author.size() || start_permlink.size() ) + { + auto start_c = c_idx.find( boost::make_tuple( my->_db.get_comment( start_author, start_permlink ).id, account.id ) ); + FC_ASSERT( start_c != c_idx.end(), "Comment is not in account's feed" ); + feed_itr = b_idx.iterator_to( *start_c ); + } + + vector< discussion > result; + result.reserve( query.limit ); + + while( result.size() < query.limit && feed_itr != b_idx.end() ) { if( feed_itr->account != account.id ) break; @@ -1548,19 +1591,24 @@ state database_api::get_state( string path )const ++itr; ++count; } - } else if( part[1].size() == 0 || part[1] == "blog" ) { - int count = 0; - const auto& pidx = my->_db.get_index_type().indices().get(); - auto itr = pidx.lower_bound( boost::make_tuple(acnt, std::string(""), time_point_sec::maximum() ) ); - eacnt.blog = vector(); - while( itr != pidx.end() && itr->author == acnt && count < 20 && !itr->parent_author.size() ) { - eacnt.blog->push_back(itr->permlink); - _state.content[acnt+"/"+itr->permlink] = *itr; - set_pending_payout( _state.content[acnt+"/"+itr->permlink] ); - ++itr; - ++count; - } - } else if( part[1].size() == 0 || part[1] == "feed" ) + } + else if( part[1].size() == 0 || part[1] == "blog" ) + { + if( my->_follow_api ) + { + auto blog = my->_follow_api->get_blog_entries( eacnt.name, 0, 20 ); + eacnt.blog = vector< string >(); + + for( auto b: blog ) + { + const auto link = b.author + "/" + b.permlink; + eacnt.blog->push_back( link ); + _state.content[ link ] = my->_db.get_comment( b.author, b.permlink ); + set_pending_payout( _state.content[ link ] ); + } + } + } + else if( part[1].size() == 0 || part[1] == "feed" ) { if( my->_follow_api ) { diff --git a/libraries/app/include/steemit/app/database_api.hpp b/libraries/app/include/steemit/app/database_api.hpp index 2605ff6e7f..3190dd4dea 100755 --- a/libraries/app/include/steemit/app/database_api.hpp +++ b/libraries/app/include/steemit/app/database_api.hpp @@ -332,6 +332,7 @@ class database_api vector get_discussions_by_children( const discussion_query& query )const; vector get_discussions_by_hot( const discussion_query& query )const; vector get_discussions_by_feed( const discussion_query& query )const; + vector get_discussions_by_blog( const discussion_query& query )const; vector get_discussions_by_promoted( const discussion_query& query )const; @@ -445,6 +446,7 @@ FC_API(steemit::app::database_api, (get_discussions_by_children) (get_discussions_by_hot) (get_discussions_by_feed) + (get_discussions_by_blog) (get_discussions_by_promoted) // Blocks and transactions diff --git a/libraries/plugins/follow/follow_plugin.cpp b/libraries/plugins/follow/follow_plugin.cpp index 54d48972b6..ea6e668008 100644 --- a/libraries/plugins/follow/follow_plugin.cpp +++ b/libraries/plugins/follow/follow_plugin.cpp @@ -101,6 +101,7 @@ struct pre_operation_visitor const auto* comment = db.find_comment( op.author, op.permlink ); if( comment == nullptr ) return; + if( comment->parent_author.size() ) return; const auto& feed_idx = db.get_index_type< feed_index >().indices().get< by_comment >(); auto itr = feed_idx.lower_bound( comment->id ); From 1f4f6b47e0474b4a0067ab32504932adf922ac57 Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Fri, 16 Sep 2016 16:18:58 -0400 Subject: [PATCH 3/3] Adds pagination for an account's comments. Custom ops can fail fast on producing nodes. Updated the follow plugin to throw exceptions on bad ops (handled by custom op code). --- libraries/app/database_api.cpp | 90 +++++++++++++++---- .../app/include/steemit/app/database_api.hpp | 3 +- libraries/chain/steem_evaluator.cpp | 6 +- .../plugins/follow/follow_evaluators.cpp | 16 ++-- 4 files changed, 84 insertions(+), 31 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index d6780a60c3..a66b7e8ac6 100755 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -1299,32 +1299,76 @@ vector database_api::get_discussions_by_blog( const discussion_query const auto& c_idx = my->_db.get_index_type< follow::blog_index >().indices().get< follow::by_comment >(); const auto& b_idx = my->_db.get_index_type< follow::blog_index >().indices().get< follow::by_blog >(); - auto feed_itr = b_idx.lower_bound( account.id ); + auto blog_itr = b_idx.lower_bound( account.id ); if( start_author.size() || start_permlink.size() ) { auto start_c = c_idx.find( boost::make_tuple( my->_db.get_comment( start_author, start_permlink ).id, account.id ) ); - FC_ASSERT( start_c != c_idx.end(), "Comment is not in account's feed" ); - feed_itr = b_idx.iterator_to( *start_c ); + FC_ASSERT( start_c != c_idx.end(), "Comment is not in account's blog" ); + blog_itr = b_idx.iterator_to( *start_c ); } vector< discussion > result; result.reserve( query.limit ); - while( result.size() < query.limit && feed_itr != b_idx.end() ) + while( result.size() < query.limit && blog_itr != b_idx.end() ) { - if( feed_itr->account != account.id ) + if( blog_itr->account != account.id ) break; try { - result.push_back( get_discussion( feed_itr->comment ) ); + result.push_back( get_discussion( blog_itr->comment ) ); } catch ( const fc::exception& e ) { edump((e.to_detail_string())); } - ++feed_itr; + ++blog_itr; + } + return result; +} + +vector database_api::get_discussions_by_comments( const discussion_query& query )const +{ + query.validate(); + FC_ASSERT( query.start_author, "Must get comments for a specific author" ); + auto start_author = *( query.start_author ); + auto start_permlink = query.start_permlink ? *( query.start_permlink ) : ""; + + const auto& account = my->_db.get_account( start_author ); + + const auto& c_idx = my->_db.get_index_type< comment_index >().indices().get< by_permlink >(); + const auto& t_idx = my->_db.get_index_type< comment_index >().indices().get< by_author_last_update >(); + auto comment_itr = t_idx.lower_bound( start_author ); + + if( start_permlink.size() ) + { + auto start_c = c_idx.find( boost::make_tuple( start_author, start_permlink ) ); + FC_ASSERT( start_c != c_idx.end(), "Comment is not in account's comments" ); + comment_itr = t_idx.iterator_to( *start_c ); + } + + vector< discussion > result; + result.reserve( query.limit ); + + while( result.size() < query.limit && comment_itr != t_idx.end() ) + { + if( comment_itr->author != start_author ) + break; + if( comment_itr->parent_author.size() > 0 ) + { + try + { + result.push_back( get_discussion( comment_itr->id ) ); + } + catch( const fc::exception& e ) + { + edump( (e.to_detail_string() ) ); + } + } + + ++comment_itr; } return result; } @@ -1579,18 +1623,26 @@ state database_api::get_state( string path )const } eacnt.recent_replies->push_back( reply_ref ); } - } else if( part[1] == "posts" ) { - int count = 0; - const auto& pidx = my->_db.get_index_type().indices().get(); - auto itr = pidx.lower_bound( boost::make_tuple(acnt, time_point_sec::maximum() ) ); - eacnt.posts = vector(); - while( itr != pidx.end() && itr->author == acnt && count < 20 ) { - eacnt.posts->push_back(itr->permlink); - _state.content[acnt+"/"+itr->permlink] = *itr; - set_pending_payout( _state.content[acnt+"/"+itr->permlink] ); - ++itr; - ++count; - } + } + else if( part[1] == "posts" || part[1] == "comments" ) + { + int count = 0; + const auto& pidx = my->_db.get_index_type().indices().get(); + auto itr = pidx.lower_bound( acnt ); + eacnt.posts = vector(); + + while( itr != pidx.end() && itr->author == acnt && count < 20 ) + { + if( itr->parent_author.size() ) + { + eacnt.posts->push_back(itr->permlink); + _state.content[acnt+"/"+itr->permlink] = *itr; + set_pending_payout( _state.content[acnt+"/"+itr->permlink] ); + ++count; + } + + ++itr; + } } else if( part[1].size() == 0 || part[1] == "blog" ) { diff --git a/libraries/app/include/steemit/app/database_api.hpp b/libraries/app/include/steemit/app/database_api.hpp index 3190dd4dea..df11a25085 100755 --- a/libraries/app/include/steemit/app/database_api.hpp +++ b/libraries/app/include/steemit/app/database_api.hpp @@ -333,9 +333,9 @@ class database_api vector get_discussions_by_hot( const discussion_query& query )const; vector get_discussions_by_feed( const discussion_query& query )const; vector get_discussions_by_blog( const discussion_query& query )const; + vector get_discussions_by_comments( const discussion_query& query )const; vector get_discussions_by_promoted( const discussion_query& query )const; - ///@} /** @@ -447,6 +447,7 @@ FC_API(steemit::app::database_api, (get_discussions_by_hot) (get_discussions_by_feed) (get_discussions_by_blog) + (get_discussions_by_comments) (get_discussions_by_promoted) // Blocks and transactions diff --git a/libraries/chain/steem_evaluator.cpp b/libraries/chain/steem_evaluator.cpp index 5d81c349c6..bf2d4c92e7 100644 --- a/libraries/chain/steem_evaluator.cpp +++ b/libraries/chain/steem_evaluator.cpp @@ -1268,7 +1268,8 @@ void custom_json_evaluator::do_apply( const custom_json_operation& o ) } catch( const fc::exception& e ) { - //elog( "Caught exception processing custom_json_operation:\n${e}", ("e", e.to_detail_string()) ); + if( d.is_producing() ) + throw e; } catch(...) { @@ -1292,7 +1293,8 @@ void custom_binary_evaluator::do_apply( const custom_binary_operation& o ) } catch( const fc::exception& e ) { - //elog( "Caught exception processing custom_json_operation:\n${e}", ("e", e.to_detail_string()) ); + if( d.is_producing() ) + throw e; } catch(...) { diff --git a/libraries/plugins/follow/follow_evaluators.cpp b/libraries/plugins/follow/follow_evaluators.cpp index 5cf5e0b045..fdff759580 100644 --- a/libraries/plugins/follow/follow_evaluators.cpp +++ b/libraries/plugins/follow/follow_evaluators.cpp @@ -66,7 +66,7 @@ void reblog_evaluator::do_apply( const reblog_operation& o ) { auto& db = _plugin->database(); const auto& c = db.get_comment( o.author, o.permlink ); - if( c.parent_author.size() > 0 ) return; + FC_ASSERT( c.parent_author.size() == 0, "Only top level posts can be reblogged" ); const auto& reblog_account = db.get_account( o.account ); const auto& blog_idx = db.get_index_type< blog_index >().indices().get< by_blog >(); @@ -82,15 +82,13 @@ void reblog_evaluator::do_apply( const reblog_operation& o ) auto blog_itr = blog_comment_idx.find( boost::make_tuple( c.id, reblog_account.id ) ); - if( blog_itr == blog_comment_idx.end() ) + FC_ASSERT( blog_itr == blog_comment_idx.end(), "Account has already reblogged this post" ); + db.create< blog_object >( [&]( blog_object& b ) { - db.create< blog_object >( [&]( blog_object& b ) - { - b.account = reblog_account.id; - b.comment = c.id; - b.blog_feed_id = next_blog_id; - }); - } + b.account = reblog_account.id; + b.comment = c.id; + b.blog_feed_id = next_blog_id; + }); const auto& feed_idx = db.get_index_type< feed_index >().indices().get< by_feed >(); const auto& comment_idx = db.get_index_type< feed_index >().indices().get< by_comment >();