diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 991868c1ce..06a0012023 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -211,6 +211,7 @@ namespace detail { application_impl(application* self) : _self(self), + _pending_trx_db(std::make_shared()), _chain_db(std::make_shared()) { } @@ -232,6 +233,7 @@ namespace detail { { try { bool clean = !fc::exists(_data_dir / "blockchain/dblock"); fc::create_directories(_data_dir / "blockchain/dblock"); + fc::create_directories(_data_dir / "node/transaction_history"); register_builtin_apis(); @@ -299,6 +301,7 @@ namespace detail { wlog("Detected unclean shutdown. Replaying blockchain..."); _chain_db->reindex(_data_dir / "blockchain" ); } + _pending_trx_db->open(_data_dir / "node/transaction_history" ); if( _options->count("force-validate") ) { @@ -787,8 +790,9 @@ namespace detail { const bpo::variables_map* _options = nullptr; api_access _apiaccess; - std::shared_ptr _chain_db; - std::shared_ptr _p2p_network; + std::shared_ptr _pending_trx_db; + std::shared_ptr _chain_db; + std::shared_ptr _p2p_network; std::shared_ptr _websocket_server; std::shared_ptr _websocket_tls_server; @@ -818,6 +822,10 @@ application::~application() { my->_chain_db->close(); } + if( my->_pending_trx_db ) + { + my->_pending_trx_db->close(); + } } void application::set_program_options(boost::program_options::options_description& command_line_options, @@ -890,6 +898,10 @@ std::shared_ptr application::chain_database() const { return my->_chain_db; } +std::shared_ptr application::pending_trx_database() const +{ + return my->_pending_trx_db; +} void application::set_block_production(bool producing_blocks) { @@ -933,6 +945,8 @@ void application::shutdown() my->_p2p_network->close(); if( my->_chain_db ) my->_chain_db->close(); + if( my->_pending_trx_db ) + my->_pending_trx_db->close(); } void application::register_abstract_plugin( std::shared_ptr< abstract_plugin > plug ) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 81cbe2a0aa..07acb437a0 100755 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -417,6 +417,35 @@ uint64_t database_api_impl::get_account_count()const return _db.get_index_type().indices().size(); } +vector< owner_authority_history_object > database_api::get_owner_history( string account )const +{ + vector< owner_authority_history_object > results; + + const auto& hist_idx = my->_db.get_index_type< owner_authority_history_index >().indices().get< by_account >(); + auto itr = hist_idx.lower_bound( account ); + + while( itr != hist_idx.end() && itr->account == account ) + { + results.push_back( *itr ); + ++itr; + } + + return results; +} + +optional< account_recovery_request_object > database_api::get_recovery_request( string account )const +{ + optional< account_recovery_request_object > result; + + const auto& rec_idx = my->_db.get_index_type< account_recovery_request_index >().indices().get< by_account >(); + auto req = rec_idx.find( account ); + + if( req != rec_idx.end() ) + result = *req; + + return result; +} + ////////////////////////////////////////////////////////////////////// // // // Witnesses // @@ -1237,16 +1266,7 @@ state database_api::get_state( string path )const auto acnt = part[0].substr(1); _state.accounts[acnt] = my->_db.get_account(acnt); auto& eacnt = _state.accounts[acnt]; - if( part[1] == "recommended" ) { - auto discussions = get_recommended_for( acnt, 50 ); - eacnt.recommended = vector(); - for( const auto& d : discussions ) { - auto ref = d.author+"/"+d.permlink; - _state.content[ ref ] = d; - eacnt.recommended->push_back( ref ); - } - } - else if( part[1] == "transfers" ) { + if( part[1] == "transfers" ) { auto history = get_account_history( acnt, uint64_t(-1), 1000 ); for( auto& item : history ) { switch( item.second.op.which() ) { @@ -1317,7 +1337,7 @@ state database_api::get_state( string path )const } else if( part[1].size() == 0 || part[1] == "feed" ) { const auto& fidxs = my->_db.get_index_type().indices(); const auto& fidx = fidxs.get(); - + auto itr = fidx.lower_bound( eacnt.id ); int count = 0; while( itr != fidx.end() && itr->account == eacnt.id && count < 100 ) { @@ -1474,54 +1494,5 @@ annotated_signed_transaction database_api::get_transaction( transaction_id_type FC_ASSERT( false, "Unknown Transaction ${t}", ("t",id)); } -vector database_api::get_recommended_for( const string& u, uint32_t limit )const { - FC_ASSERT( limit <= 1000 ); - auto start_time = fc::time_point::now(); - - const auto& user = my->_db.get_account(u); - const auto& rank_idx = my->_db.get_index_type().indices().get(); - const auto& vote_idx = my->_db.get_index_type().indices().get(); - - map stage; - - auto max_age = my->_db.head_block_time() - fc::days(1); - - auto itr = rank_idx.lower_bound( boost::make_tuple( user.get_id(), std::numeric_limits::max() ) ); - while( itr != rank_idx.end() && itr->voter == user.id && stage.size() < 1000 && itr->rank > 0 ) { - auto vitr = vote_idx.lower_bound( boost::make_tuple( itr->peer, fc::time_point_sec::maximum() ) ); - int32_t max_votes_per_peer = 50; - while( max_votes_per_peer && vitr->voter == itr->peer && vitr->last_update >= max_age ) { - const auto& c = vitr->comment(my->_db); - if( c.parent_author.size() == 0 && c.created > max_age && c.author != u && c.net_rshares > 0 ) - { - auto agediscount = float((c.created - max_age).count())/fc::days(1).count(); - stage[vitr->comment] += agediscount * itr->rank * vitr->vote_percent; - max_votes_per_peer--; - } - ++vitr; - } - ++itr; - } - auto vitr = vote_idx.lower_bound( boost::make_tuple( user.get_id(), fc::time_point_sec::maximum() ) ); - while( vitr != vote_idx.end() && vitr->voter == user.get_id() && vitr->last_update > max_age ) { - stage.erase( vitr->comment ); - ++vitr; - } - - vector< pair > result; - result.reserve(stage.size()); - for( const auto& item : stage ) result.push_back({item.second,item.first}); - std::sort( result.begin(), result.end(), std::greater>() ); - - vector dresult; dresult.reserve(limit); - for( const auto& item : result ) { - dresult.push_back( get_discussion( item.second ) ); - if( dresult.size() == limit ) break; - } - auto end_time = fc::time_point::now(); - idump((start_time-end_time)); - - return dresult; -} } } // steemit::app diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 58c7dd243b..313b64426a 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -171,6 +171,21 @@ struct get_impacted_account_visitor _impacted.insert( s ); } + void operator()( const request_account_recovery_operation& op ) + { + _impacted.insert( op.account_to_recover ); + } + + void operator()( const recover_account_operation& op ) + { + _impacted.insert( op.account_to_recover ); + } + + void operator()( const change_recovery_account_operation& op ) + { + _impacted.insert( op.account_to_recover ); + } + //void operator()( const operation& op ){} }; diff --git a/libraries/app/include/steemit/app/application.hpp b/libraries/app/include/steemit/app/application.hpp index 80a04aa609..18966687e5 100644 --- a/libraries/app/include/steemit/app/application.hpp +++ b/libraries/app/include/steemit/app/application.hpp @@ -79,6 +79,7 @@ namespace steemit { namespace app { graphene::net::node_ptr p2p_node(); std::shared_ptr chain_database()const; + std::shared_ptr pending_trx_database() const; void set_block_production(bool producing_blocks); fc::optional< api_access_info > get_api_access_info( const string& username )const; diff --git a/libraries/app/include/steemit/app/database_api.hpp b/libraries/app/include/steemit/app/database_api.hpp index 70fecb0a40..460fcf5f66 100755 --- a/libraries/app/include/steemit/app/database_api.hpp +++ b/libraries/app/include/steemit/app/database_api.hpp @@ -55,7 +55,6 @@ struct liquidity_balance fc::uint128_t weight; }; - class database_api_impl; /** @@ -196,6 +195,10 @@ class database_api */ uint64_t get_account_count()const; + vector< owner_authority_history_object > get_owner_history( string account )const; + + optional< account_recovery_request_object > get_recovery_request( string account ) const; + /////////////// // Witnesses // /////////////// @@ -307,7 +310,6 @@ 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_recommended_for( const string& user, uint32_t limit )const; ///@} @@ -406,7 +408,6 @@ FC_API(steemit::app::database_api, (get_discussions_by_votes) (get_discussions_by_children) (get_discussions_by_hot) - (get_recommended_for) // Blocks and transactions (get_block_header) @@ -438,6 +439,8 @@ FC_API(steemit::app::database_api, (get_account_count) (get_conversion_requests) (get_account_history) + (get_owner_history) + (get_recovery_request) // Market (get_order_book) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 03ac0e44a5..c77244e988 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -1796,7 +1796,7 @@ void database::cashout_comment_helper( const comment_object& comment ) if( comment.net_rshares > 0 ) { - uint128_t reward_tokens = uint128_t( claim_rshare_reward( comment.net_rshares, to_steem( comment.max_accepted_payout ) ).value ); + uint128_t reward_tokens = uint128_t( claim_rshare_reward( comment.net_rshares, comment.reward_weight, to_steem( comment.max_accepted_payout ) ).value ); asset total_payout; if( reward_tokens > 0 ) @@ -1871,8 +1871,15 @@ void database::cashout_comment_helper( const comment_object& comment ) c.abs_rshares = 0; c.vote_rshares = 0; c.total_vote_weight = 0; - c.cashout_time = fc::time_point_sec::maximum(); c.max_cashout_time = fc::time_point_sec::maximum(); + + if( c.parent_author.size() == 0 ) + { + if( has_hardfork( STEEMIT_HARDFORK_0_12__177 ) && c.last_payout == fc::time_point_sec::min() ) + c.cashout_time = head_block_time() + STEEMIT_SECOND_CASHOUT_WINDOW; + else + c.cashout_time = fc::time_point_sec::maximum(); + } c.last_payout = head_block_time(); } ); @@ -1882,10 +1889,17 @@ void database::cashout_comment_helper( const comment_object& comment ) { const auto& cur_vote = *vote_itr; ++vote_itr; - modify( cur_vote, [&]( comment_vote_object& cvo ) + if( !has_hardfork( STEEMIT_HARDFORK_0_12__177 ) || calculate_discussion_payout_time( comment ) != fc::time_point_sec::maximum() ) { - cvo.num_changes = -1; - }); + modify( cur_vote, [&]( comment_vote_object& cvo ) + { + cvo.num_changes = -1; + }); + } + else + { + remove( cur_vote ); + } } } FC_CAPTURE_AND_RETHROW( (comment) ) } @@ -2006,6 +2020,9 @@ void database::update_account_activity( const account_object& account ) { asset database::get_liquidity_reward()const { + if( has_hardfork( STEEMIT_HARDFORK_0_12__178 ) ) + return asset( 0, STEEM_SYMBOL ); + const auto& props = get_dynamic_global_properties(); static_assert( STEEMIT_LIQUIDITY_REWARD_PERIOD_SEC == 60*60, "this code assumes a 1 hour time interval" ); asset percent( calc_percent_reward_per_hour< STEEMIT_LIQUIDITY_APR_PERCENT >( props.virtual_supply.amount ), STEEM_SYMBOL ); @@ -2076,11 +2093,15 @@ void database::pay_liquidity_reward() if( (head_block_num() % STEEMIT_LIQUIDITY_REWARD_BLOCKS) == 0 ) { + auto reward = get_liquidity_reward(); + + if( reward.amount == 0 ) + return; + const auto& ridx = get_index_type().indices().get(); auto itr = ridx.begin(); if( itr != ridx.end() && itr->volume_weight() > 0 ) { - auto reward = get_liquidity_reward(); adjust_supply( reward, true ); adjust_balance( itr->owner(*this), reward ); modify( *itr, [&]( liquidity_reward_balance_object& obj ) @@ -2197,7 +2218,7 @@ asset database::to_steem( const asset& sbd )const * This method reduces the rshare^2 supply and returns the number of tokens are * redeemed. */ -share_type database::claim_rshare_reward( share_type rshares, asset max_steem ) +share_type database::claim_rshare_reward( share_type rshares, uint16_t reward_weight, asset max_steem ) { try { @@ -2210,6 +2231,7 @@ share_type database::claim_rshare_reward( share_type rshares, asset max_steem ) u256 total_rshares2 = to256( props.total_reward_shares2 ); u256 rs2 = to256( calculate_vshares( rshares.value ) ); + rs2 = ( rs2 * reward_weight ) / STEEMIT_100_PERCENT; u256 payout_u256 = ( rf * rs2 ) / total_rshares2; FC_ASSERT( payout_u256 <= u256( uint64_t( std::numeric_limits::max() ) ) ); @@ -2562,7 +2584,6 @@ void database::_apply_block( const signed_block& next_block ) for( const auto& trx : next_block.transactions ) { - _current_trx_id = trx.id(); /* We do not need to push the undo state for each transaction * because they either all apply and are valid or the * entire block fails to apply. We only need an "undo" state @@ -2699,6 +2720,7 @@ void database::apply_transaction(const signed_transaction& trx, uint32_t skip) void database::_apply_transaction(const signed_transaction& trx) { try { + _current_trx_id = trx.id(); uint32_t skip = get_node_properties().skip_flags; if( !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */ @@ -2781,7 +2803,7 @@ void database::_apply_transaction(const signed_transaction& trx) ++_current_op_in_trx; } FC_CAPTURE_AND_RETHROW( (op) ); } - + _current_trx_id = transaction_id_type(); } FC_CAPTURE_AND_RETHROW( (trx) ) } @@ -2892,7 +2914,8 @@ void database::update_global_dynamic_data( const signed_block& b ) */ if( dgp.head_block_number % 20 == 0 ) { - if( dgp.average_block_size > dgp.maximum_block_size/2 ) + if( ( !has_hardfork( STEEMIT_HARDFORK_0_12__179 ) && dgp.average_block_size > dgp.maximum_block_size / 2 ) || + ( has_hardfork( STEEMIT_HARDFORK_0_12__179 ) && dgp.average_block_size > dgp.maximum_block_size / 4 ) ) { dgp.current_reserve_ratio /= 2; /// exponential back up } @@ -3050,9 +3073,9 @@ int database::match( const limit_order_object& new_order, const limit_order_obje old_order_pays == old_order.amount_for_sale() ); auto age = head_block_time() - old_order.created; - if( (age >= STEEMIT_MIN_LIQUIDITY_REWARD_PERIOD_SEC && !has_hardfork( STEEMIT_HARDFORK_0_10__149)) || - (age >= STEEMIT_MIN_LIQUIDITY_REWARD_PERIOD_SEC_HF10 && has_hardfork( STEEMIT_HARDFORK_0_10__149) ) - ) + if( !has_hardfork( STEEMIT_HARDFORK_0_12__178 ) && + ( (age >= STEEMIT_MIN_LIQUIDITY_REWARD_PERIOD_SEC && !has_hardfork( STEEMIT_HARDFORK_0_10__149)) || + (age >= STEEMIT_MIN_LIQUIDITY_REWARD_PERIOD_SEC_HF10 && has_hardfork( STEEMIT_HARDFORK_0_10__149) ) ) ) { if( old_order_receives.symbol == STEEM_SYMBOL ) { @@ -3309,6 +3332,9 @@ void database::init_hardforks() FC_ASSERT( STEEMIT_HARDFORK_0_11 == 11, "Invalid hardfork configuration" ); _hardfork_times[ STEEMIT_HARDFORK_0_11 ] = fc::time_point_sec( STEEMIT_HARDFORK_0_11_TIME ); _hardfork_versions[ STEEMIT_HARDFORK_0_11 ] = STEEMIT_HARDFORK_0_11_VERSION; + FC_ASSERT( STEEMIT_HARDFORK_0_12 == 12, "Invalid hardfork configuration" ); + _hardfork_times[ STEEMIT_HARDFORK_0_12 ] = fc::time_point_sec( STEEMIT_HARDFORK_0_12_TIME ); + _hardfork_versions[ STEEMIT_HARDFORK_0_12 ] = STEEMIT_HARDFORK_0_12_VERSION; const auto& hardforks = hardfork_property_id_type()( *this ); FC_ASSERT( hardforks.last_hardfork <= STEEMIT_NUM_HARDFORKS, "Chain knows of more hardforks than configuration", ("hardforks.last_hardfork",hardforks.last_hardfork)("STEEMIT_NUM_HARDFORKS",STEEMIT_NUM_HARDFORKS) ); @@ -3488,6 +3514,58 @@ void database::apply_hardfork( uint32_t hardfork ) elog( "HARDFORK 11" ); #endif break; + case STEEMIT_HARDFORK_0_12: +#ifndef IS_TEST_NET + elog( "HARDFORK 12" ); +#endif + { + const auto& comment_idx = get_index_type< comment_index >().indices(); + + for( auto itr = comment_idx.begin(); itr != comment_idx.end(); ++itr ) + { + // At the hardfork time, all new posts with no votes get their cashout time set to +12 hrs from head block time. + // All posts with a payout get their cashout time set to +30 days. This hardfork takes place within 30 days + // initial payout so we don't have to handle the case of posts that should be frozen that aren't + if( itr->parent_author.size() == 0 ) + { + // Post has not been paid out and has no votes (cashout_time == 0 === net_rshares == 0, under current semmantics) + if( itr->last_payout == fc::time_point_sec::min() && itr->cashout_time == fc::time_point_sec::maximum() ) + { + modify( *itr, [&]( comment_object & c ) + { + c.cashout_time = head_block_time() + STEEMIT_CASHOUT_WINDOW_SECONDS; + }); + } + // Has been paid out, needs to be on second cashout window + else if( itr->last_payout > fc::time_point_sec() ) + { + modify( *itr, [&]( comment_object& c ) + { + c.cashout_time = c.last_payout + STEEMIT_SECOND_CASHOUT_WINDOW; + }); + } + } + } + + modify( get_account( STEEMIT_MINER_ACCOUNT ), [&]( account_object& a ) + { + a.posting = authority(); + a.posting.weight_threshold = 1; + }); + + modify( get_account( STEEMIT_NULL_ACCOUNT ), [&]( account_object& a ) + { + a.posting = authority(); + a.posting.weight_threshold = 1; + }); + + modify( get_account( STEEMIT_TEMP_ACCOUNT ), [&]( account_object& a ) + { + a.posting = authority(); + a.posting.weight_threshold = 1; + }); + } + break; default: break; } diff --git a/libraries/chain/hardfork.d/0-preamble.hf b/libraries/chain/hardfork.d/0-preamble.hf index 615d38a1ee..47a5d3449e 100644 --- a/libraries/chain/hardfork.d/0-preamble.hf +++ b/libraries/chain/hardfork.d/0-preamble.hf @@ -33,5 +33,5 @@ FC_REFLECT_DERIVED( steemit::chain::hardfork_property_object, (graphene::db::obj (processed_hardforks)(last_hardfork)(current_hardfork_version) (next_hardfork)(next_hardfork_time) ) -#define STEEMIT_NUM_HARDFORKS 11 +#define STEEMIT_NUM_HARDFORKS 12 diff --git a/libraries/chain/hardfork.d/0_12.hf b/libraries/chain/hardfork.d/0_12.hf new file mode 100644 index 0000000000..21d43ebb6b --- /dev/null +++ b/libraries/chain/hardfork.d/0_12.hf @@ -0,0 +1,11 @@ +#ifndef STEEMIT_HARDFORK_0_12 +#define STEEMIT_HARDFORK_0_12 12 +#define STEEMIT_HARDFORK_0_12__176 (STEEMIT_HARDFORK_0_12) +#define STEEMIT_HARDFORK_0_12__177 (STEEMIT_HARDFORK_0_12) +#define STEEMIT_HARDFORK_0_12__178 (STEEMIT_HARDFORK_0_12) +#define STEEMIT_HARDFORK_0_12__179 (STEEMIT_HARDFORK_0_12) + +#define STEEMIT_HARDFORK_0_12_TIME 1469545200 // 2016-07-26T15:00:00 UTC (11:00:00 EDT) +#define STEEMIT_HARDFORK_0_12_VERSION hardfork_version( 0, 12 ) + +#endif diff --git a/libraries/chain/include/steemit/chain/account_object.hpp b/libraries/chain/include/steemit/chain/account_object.hpp index 5b8e2462ad..17e6f5ec81 100644 --- a/libraries/chain/include/steemit/chain/account_object.hpp +++ b/libraries/chain/include/steemit/chain/account_object.hpp @@ -36,6 +36,7 @@ namespace steemit { namespace chain { time_point_sec last_owner_proved = time_point_sec::min(); time_point_sec last_active_proved = time_point_sec::min(); string recovery_account = ""; + time_point_sec last_account_recovery; uint32_t comment_count = 0; uint32_t lifetime_vote_count = 0; uint32_t post_count = 0; @@ -96,6 +97,8 @@ namespace steemit { namespace chain { uint64_t average_market_bandwidth = 0; time_point_sec last_market_bandwidth_update; time_point_sec last_post; + time_point_sec last_root_post = fc::time_point_sec::min(); + uint32_t post_bandwidth = 0; /** * Used to track activity rewards, updated on every post and comment @@ -345,7 +348,7 @@ namespace steemit { namespace chain { FC_REFLECT_DERIVED( steemit::chain::account_object, (graphene::db::object), (name)(owner)(active)(posting)(memo_key)(json_metadata)(proxy)(last_owner_update) (created)(mined) - (owner_challenged)(active_challenged)(last_owner_proved)(last_active_proved)(recovery_account) + (owner_challenged)(active_challenged)(last_owner_proved)(last_active_proved)(recovery_account)(last_account_recovery) (comment_count)(lifetime_vote_count)(post_count)(voting_power)(last_vote_time) (balance) (sbd_balance)(sbd_seconds)(sbd_seconds_last_update)(sbd_last_interest_payment) @@ -355,7 +358,7 @@ FC_REFLECT_DERIVED( steemit::chain::account_object, (graphene::db::object), (proxied_vsf_votes)(witnesses_voted_for) (average_bandwidth)(lifetime_bandwidth)(last_bandwidth_update) (average_market_bandwidth)(last_market_bandwidth_update) - (last_post) + (last_post)(last_root_post)(post_bandwidth) (last_active)(activity_shares)(last_activity_payout) ) diff --git a/libraries/chain/include/steemit/chain/comment_object.hpp b/libraries/chain/include/steemit/chain/comment_object.hpp index 1d0e92a3f5..ede06db564 100644 --- a/libraries/chain/include/steemit/chain/comment_object.hpp +++ b/libraries/chain/include/steemit/chain/comment_object.hpp @@ -104,6 +104,8 @@ namespace steemit { namespace chain { time_point_sec max_cashout_time; uint64_t total_vote_weight = 0; /// the total weight of voting rewards, used to calculate pro-rata share of curation payouts + uint16_t reward_weight; + /** tracks the total payout this comment has received over time, measured in SBD */ asset total_payout_value = asset(0, SBD_SYMBOL); asset curator_payout_value = asset(0, SBD_SYMBOL); @@ -337,7 +339,7 @@ FC_REFLECT_DERIVED( steemit::chain::comment_object, (graphene::db::object), (depth)(children)(children_rshares2) (net_rshares)(abs_rshares)(vote_rshares) (children_abs_rshares)(cashout_time)(max_cashout_time) - (total_vote_weight)(total_payout_value)(curator_payout_value)(author_rewards)(net_votes)(root_comment) + (total_vote_weight)(reward_weight)(total_payout_value)(curator_payout_value)(author_rewards)(net_votes)(root_comment) (max_accepted_payout)(percent_steem_dollars)(allow_replies)(allow_votes)(allow_curation_rewards) ) FC_REFLECT_DERIVED( steemit::chain::comment_vote_object, (graphene::db::object), diff --git a/libraries/chain/include/steemit/chain/config.hpp b/libraries/chain/include/steemit/chain/config.hpp index 113f7cf144..8df9230176 100644 --- a/libraries/chain/include/steemit/chain/config.hpp +++ b/libraries/chain/include/steemit/chain/config.hpp @@ -3,7 +3,7 @@ */ #pragma once -#define STEEMIT_BLOCKCHAIN_VERSION ( version(0, 11, 0) ) +#define STEEMIT_BLOCKCHAIN_VERSION ( version(0, 12, 0) ) #define STEEMIT_BLOCKCHAIN_HARDFORK_VERSION ( hardfork_version( STEEMIT_BLOCKCHAIN_VERSION ) ) #ifdef IS_TEST_NET @@ -22,7 +22,9 @@ #define STEEMIT_GENESIS_TIME (fc::time_point_sec(1451606400)) #define STEEMIT_MINING_TIME (fc::time_point_sec(1451606400)) #define STEEMIT_CASHOUT_WINDOW_SECONDS (60*60) /// 1 hr -#define STEEMIT_MAX_CASHOUT_WINDOW_SECONDS (60*60*24*14) /// 2 weeks +#define STEEMIT_CASHOUT_WINDOW_SECONDS_PRE_HF12 (STEEMIT_CASHOUT_WINDOW_SECONDS) +#define STEEMIT_SECOND_CASHOUT_WINDOW (60*60*24*3) /// 3 days +#define STEEMIT_MAX_CASHOUT_WINDOW_SECONDS (60*60*24) /// 1 day #define STEEMIT_VOTE_CHANGE_LOCKOUT_PERIOD (60*10) /// 10 minutes @@ -46,7 +48,9 @@ #define STEEMIT_GENESIS_TIME (fc::time_point_sec(1458835200)) #define STEEMIT_MINING_TIME (fc::time_point_sec(1458838800)) -#define STEEMIT_CASHOUT_WINDOW_SECONDS (60*60*24) /// 1 day +#define STEEMIT_CASHOUT_WINDOW_SECONDS_PRE_HF12 (60*60*24) /// 1 day +#define STEEMIT_CASHOUT_WINDOW_SECONDS (60*60*12) /// 12 hours +#define STEEMIT_SECOND_CASHOUT_WINDOW (60*60*24*30) /// 30 days #define STEEMIT_MAX_CASHOUT_WINDOW_SECONDS (60*60*24*14) /// 2 weeks #define STEEMIT_VOTE_CHANGE_LOCKOUT_PERIOD (60*60*2) /// 2 hours @@ -86,6 +90,12 @@ #define STEEMIT_REVERSE_AUCTION_WINDOW_SECONDS (60*30) /// 30 minutes #define STEEMIT_MIN_VOTE_INTERVAL_SEC 3 +#define STEEMIT_MIN_ROOT_COMMENT_INTERVAL (fc::seconds(60*5)) // 5 minutes +#define STEEMIT_MIN_REPLY_INTERVAL (fc::seconds(20)) // 20 seconds +#define STEEMIT_POST_AVERAGE_WINDOW (60*60*24u) // 1 day +#define STEEMIT_POST_MAX_BANDWIDTH (4*STEEMIT_100_PERCENT) // 2 posts per 1 days, average 1 every 12 hours +#define STEEMIT_POST_WEIGHT_CONSTANT (uint64_t(STEEMIT_POST_MAX_BANDWIDTH) * STEEMIT_POST_MAX_BANDWIDTH) + #define STEEMIT_MAX_ACCOUNT_WITNESS_VOTES 30 #define STEEMIT_100_PERCENT 10000 @@ -175,7 +185,7 @@ #define STEEMIT_SECONDS_PER_YEAR (uint64_t(60*60*24*365ll)) #define STEEMIT_SBD_INTEREST_COMPOUND_INTERVAL_SEC (60*60*24*30) -#define STEEMIT_MAX_TRANSACTION_SIZE (1024*128) +#define STEEMIT_MAX_TRANSACTION_SIZE (1024*64) #define STEEMIT_MIN_BLOCK_SIZE_LIMIT (STEEMIT_MAX_TRANSACTION_SIZE) #define STEEMIT_MAX_BLOCK_SIZE (STEEMIT_MAX_TRANSACTION_SIZE*STEEMIT_BLOCK_INTERVAL*2000) #define STEEMIT_BLOCKS_PER_HOUR (60*60/STEEMIT_BLOCK_INTERVAL) diff --git a/libraries/chain/include/steemit/chain/database.hpp b/libraries/chain/include/steemit/chain/database.hpp index 351cf4d7f4..c6defd29b6 100644 --- a/libraries/chain/include/steemit/chain/database.hpp +++ b/libraries/chain/include/steemit/chain/database.hpp @@ -278,7 +278,7 @@ namespace steemit { namespace chain { void process_conversions(); void account_recovery_processing(); void update_median_feed(); - share_type claim_rshare_reward( share_type rshares, asset max_steem ); + share_type claim_rshare_reward( share_type rshares, uint16_t reward_weight, asset max_steem ); asset get_liquidity_reward()const; asset get_content_reward()const; diff --git a/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp b/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp index f074974f10..9c9959c90b 100644 --- a/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp +++ b/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp @@ -366,7 +366,7 @@ namespace steemit { namespace chain { * This witnesses vote for the maximum_block_size which is used by the network * to tune rate limiting and capacity */ - uint32_t maximum_block_size = STEEMIT_MIN_BLOCK_SIZE_LIMIT; + uint32_t maximum_block_size = STEEMIT_MIN_BLOCK_SIZE_LIMIT * 2; uint16_t sbd_interest_rate = STEEMIT_DEFAULT_SBD_INTEREST_RATE; void validate()const { @@ -611,24 +611,95 @@ namespace steemit { namespace chain { void validate()const; }; + /** + * All account recovery requests come from a listed recovery account. This + * is secure based on the assumption that only a trusted account should be + * a recovery account. It is the responsibility of the recovery account to + * verify the identity of the account holder of the account to recover by + * whichever means they have agreed upon. The blockchain assumes identity + * has been verified when this operation is broadcast. + * + * This operation creates an account recovery request which the account to + * recover has 24 hours to respond to before the request expires and is + * invalidated. + * + * There can only be one active recovery request per account at any one time. + * Pushing this operation for an account to recover when it already has + * an active request will either update the request to a new new owner authority + * and extend the request expiration to 24 hours from the current head block + * time or it will delete the request. To cancel a request, simply set the + * weight threshold of the new owner authority to 0, making it an open authority. + * + * Additionally, the new owner authority must be satisfiable. In other words, + * the sum of the key weights must be greater than or equal to the weight + * threshold. + * + * This operation only needs to be signed by the the recovery account. + * The account to recover confirms its identity to the blockchain in + * the recover account operation. + */ struct request_account_recovery_operation : public base_operation { - string recovery_account; - string account_to_recover; - authority new_owner_authority; - extensions_type extensions; + string recovery_account; ///< The recovery account is listed as the recovery account on the account to recover. + + string account_to_recover; ///< The account to recover. This is likely due to a compromised owner authority. + + authority new_owner_authority; ///< The new owner authority the account to recover wishes to have. This is secret + ///< known by the account to recover and will be confirmed in a recover_account_operation + + extensions_type extensions; ///< Extensions. Not currently used. void get_required_active_authorities( flat_set& a )const{ a.insert( recovery_account ); } void validate() const; }; + /** + * Recover an account to a new authority using a previous authority and verification + * of the recovery account as proof of identity. This operation can only succeed + * if there was a recovery request sent by the account's recover account. + * + * In order to recover the account, the account holder must provide proof + * of past ownership and proof of identity to the recovery account. Being able + * to satisfy an owner authority that was used in the past 30 days is sufficient + * to prove past ownership. The get_owner_history function in the database API + * returns past owner authorities that are valid for account recovery. + * + * Proving identity is an off chain contract between the account holder and + * the recovery account. The recovery request contains a new authority which + * must be satisfied by the account holder to regain control. The actual process + * of verifying authority may become complicated, but that is an application + * level concern, not a blockchain concern. + * + * This operation requires both the past and future owner authorities in the + * operation because neither of them can be derived from the current chain state. + * The operation must be signed by keys that satisfy both the new owner authority + * and the recent owner authority. Failing either fails the operation entirely. + * + * If a recovery request was made inadvertantly, the account holder should + * contact the recovery account to have the request deleted. + * + * The two setp combination of the account recovery request and recover is + * safe because the recovery account never has access to secrets of the account + * to recover. They simply act as an on chain endorsement of off chain identity. + * In other systems, a fork would be required to enforce such off chain state. + * Additionally, an account cannot be permanently recovered to the wrong account. + * While any owner authority from the past 30 days can be used, including a compromised + * authority, the account can be continually recovered until the recovery account + * is confident a combination of uncompromised authorities were used to + * recover the account. The actual process of verifying authority may become + * complicated, but that is an application level concern, not the blockchain's + * concern. + */ struct recover_account_operation : public base_operation { - string account_to_recover; - authority new_owner_authority; - authority recent_owner_authority; - extensions_type extensions; + string account_to_recover; ///< The account to be recovered + + authority new_owner_authority; ///< The new owner authority as specified in the request account recovery operation. + + authority recent_owner_authority; ///< A previous owner authority that the account holder will use to prove past ownership of the account to be recovered. + + extensions_type extensions; ///< Extensions. Not currently used. void get_required_authorities( vector& a )const { @@ -639,11 +710,31 @@ namespace steemit { namespace chain { void validate() const; }; + /** + * Each account lists another account as their recovery account. + * The recovery account has the ability to create account_recovery_requests + * for the account to recover. An account can change their recovery account + * at any time with a 30 day delay. This delay is to prevent + * an attacker from changing the recovery account to a malicious account + * during an attack. These 30 days match the 30 days that an + * owner authority is valid for recovery purposes. + * + * On account creation the recovery account is set either to the creator of + * the account (The account that pays the creation fee and is a signer on the transaction) + * or to the empty string if the account was mined. An account with no recovery + * has the top voted witness as a recovery account, at the time the recover + * request is created. Note: This does mean the effective recovery account + * of an account with no listed recovery account can change at any time as + * witness vote weights. The top voted witness is explicitly the most trusted + * witness according to stake. + */ struct change_recovery_account_operation : public base_operation { - string account_to_recover; - string new_recovery_account; - extensions_type extensions; + string account_to_recover; ///< The account that would be recovered in case of compromise + + string new_recovery_account; ///< The account that creates the recover request + + extensions_type extensions; ///< Extensions. Not currently used. void get_required_owner_authorities( flat_set& a )const{ a.insert( account_to_recover ); } diff --git a/libraries/chain/steem_evaluator.cpp b/libraries/chain/steem_evaluator.cpp index 53008d5111..7fcbdba9b4 100644 --- a/libraries/chain/steem_evaluator.cpp +++ b/libraries/chain/steem_evaluator.cpp @@ -56,6 +56,9 @@ void witness_update_evaluator::do_apply( const witness_update_operation& o ) if ( db().has_hardfork( STEEMIT_HARDFORK_0_1 ) ) FC_ASSERT( o.url.size() <= STEEMIT_MAX_WITNESS_URL_LENGTH ); + if( !db().has_hardfork( STEEMIT_HARDFORK_0_12__179) ) + FC_ASSERT( o.props.maximum_block_size >= STEEMIT_MIN_BLOCK_SIZE_LIMIT * 2 ); + const auto& by_witness_name_idx = db().get_index_type< witness_index >().indices().get< by_name >(); auto wit_itr = by_witness_name_idx.find( o.owner ); if( wit_itr != by_witness_name_idx.end() ) @@ -272,24 +275,49 @@ void comment_evaluator::do_apply( const comment_operation& o ) if ( itr == by_permlink_idx.end() ) { - if( db().is_producing() && o.parent_author.size() == 0 ) // TODO: Remove after hardfork - { - FC_ASSERT( (now - auth.last_post) > fc::seconds(60*5), "You may only post once every 5 minutes", ("now",now)("auth.last_post",auth.last_post) ); - } - if( o.parent_author.size() != 0 ) + { FC_ASSERT( parent->root_comment( db() ).allow_replies, "Comment has disabled replies." ); + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__177) ) + FC_ASSERT( db().calculate_discussion_payout_time( *parent ) != fc::time_point_sec::maximum() ); + } - if( db().has_hardfork( STEEMIT_HARDFORK_0_6__113 ) ) { + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__176 ) ) + { if( o.parent_author.size() == 0 ) - FC_ASSERT( (now - auth.last_post) > fc::seconds(60*5), "You may only post once every 5 minutes", ("now",now)("auth.last_post",auth.last_post) ); + FC_ASSERT( (now - auth.last_root_post) > STEEMIT_MIN_ROOT_COMMENT_INTERVAL, "You may only post once every 5 minutes", ("now",now)("auth.last_root_post",auth.last_root_post) ); else - FC_ASSERT( (now - auth.last_post) > fc::seconds(20), "You may only comment once every 20 seconds", ("now",now)("auth.last_post",auth.last_post) ); - } else { + FC_ASSERT( (now - auth.last_post) > STEEMIT_MIN_REPLY_INTERVAL, "You may only comment once every 20 seconds", ("now",now)("auth.last_post",auth.last_post) ); + } + else if( db().has_hardfork( STEEMIT_HARDFORK_0_6__113 ) ) + { + if( o.parent_author.size() == 0 ) + FC_ASSERT( (now - auth.last_post) > STEEMIT_MIN_ROOT_COMMENT_INTERVAL, "You may only post once every 5 minutes", ("now",now)("auth.last_post",auth.last_post) ); + else + FC_ASSERT( (now - auth.last_post) > STEEMIT_MIN_REPLY_INTERVAL, "You may only comment once every 20 seconds", ("now",now)("auth.last_post",auth.last_post) ); + } + else + { FC_ASSERT( (now - auth.last_post) > fc::seconds(60), "You may only post once per minute", ("now",now)("auth.last_post",auth.last_post) ); } + uint16_t reward_weight = STEEMIT_100_PERCENT; + uint64_t post_bandwidth = auth.post_bandwidth; + + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__176 ) && o.parent_author.size() == 0 ) + { + uint64_t post_delta_time = std::min( db().head_block_time().sec_since_epoch() - auth.last_root_post.sec_since_epoch(), STEEMIT_POST_AVERAGE_WINDOW ); + uint32_t old_weight = uint32_t( ( post_bandwidth * ( STEEMIT_POST_AVERAGE_WINDOW - post_delta_time ) ) / STEEMIT_POST_AVERAGE_WINDOW ); + post_bandwidth = ( old_weight + STEEMIT_100_PERCENT ); + reward_weight = uint16_t( std::min( ( STEEMIT_POST_WEIGHT_CONSTANT * STEEMIT_100_PERCENT ) / ( post_bandwidth * post_bandwidth ), uint64_t( STEEMIT_100_PERCENT ) ) ); + } + db().modify( auth, [&]( account_object& a ) { + if( o.parent_author.size() == 0 ) + { + a.last_root_post = now; + a.post_bandwidth = uint32_t( post_bandwidth ); + } a.last_post = now; a.post_count++; }); @@ -308,8 +336,8 @@ void comment_evaluator::do_apply( const comment_operation& o ) com.created = com.last_update; com.active = com.last_update; com.last_payout = fc::time_point_sec::min(); - com.cashout_time = fc::time_point_sec::maximum(); com.max_cashout_time = fc::time_point_sec::maximum(); + com.reward_weight = reward_weight; if ( o.parent_author.size() == 0 ) { @@ -317,6 +345,9 @@ void comment_evaluator::do_apply( const comment_operation& o ) com.parent_permlink = o.parent_permlink; com.category = o.parent_permlink; com.root_comment = com.id; + com.cashout_time = db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) ? + db().head_block_time() + STEEMIT_CASHOUT_WINDOW_SECONDS : + fc::time_point_sec::maximum(); } else { @@ -325,6 +356,7 @@ void comment_evaluator::do_apply( const comment_operation& o ) com.depth = parent->depth + 1; com.category = parent->category; com.root_comment = parent->root_comment; + com.cashout_time = fc::time_point_sec::maximum(); } #ifndef IS_LOW_MEM @@ -371,7 +403,7 @@ void comment_evaluator::do_apply( const comment_operation& o ) { const auto& comment = *itr; - if( db().is_producing() || db().has_hardfork( STEEMIT_HARDFORK_0_10 ) ) // TODO Remove is_producing after hardfork + if( db().has_hardfork( STEEMIT_HARDFORK_0_10 ) ) FC_ASSERT( comment.last_payout == fc::time_point_sec::min() ); db().modify( comment, [&]( comment_object& com ) @@ -802,12 +834,15 @@ void vote_evaluator::do_apply( const vote_operation& o ) if( o.weight > 0 ) FC_ASSERT( comment.allow_votes ); + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) && db().calculate_discussion_payout_time( comment ) == fc::time_point_sec::maximum() ) + return; + const auto& comment_vote_idx = db().get_index_type< comment_vote_index >().indices().get< by_comment_voter >(); auto itr = comment_vote_idx.find( std::make_tuple( comment.id, voter.id ) ); auto elapsed_seconds = (db().head_block_time() - voter.last_vote_time).to_seconds(); - if( db().has_hardfork( STEEMIT_HARDFORK_0_11 ) ) + if( db().has_hardfork( STEEMIT_HARDFORK_0_11 ) ) FC_ASSERT( elapsed_seconds >= STEEMIT_MIN_VOTE_INTERVAL_SEC ); auto regenerated_power = (STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS; @@ -825,7 +860,7 @@ void vote_evaluator::do_apply( const vote_operation& o ) // Lazily delete vote if( itr != comment_vote_idx.end() && itr->num_changes == -1 ) { - if( db().is_producing() ) + if( db().is_producing() || db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) ) FC_ASSERT( false, "Cannot vote again on a comment after payout" ); db().remove( *itr ); @@ -858,7 +893,12 @@ void vote_evaluator::do_apply( const vote_operation& o ) auto old_root_abs_rshares = root.children_abs_rshares.value; fc::uint128_t cur_cashout_time_sec = db().calculate_discussion_payout_time( comment ).sec_since_epoch(); - fc::uint128_t new_cashout_time_sec = db().head_block_time().sec_since_epoch() + STEEMIT_CASHOUT_WINDOW_SECONDS; + fc::uint128_t new_cashout_time_sec; + + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) ) + new_cashout_time_sec = db().head_block_time().sec_since_epoch() + STEEMIT_CASHOUT_WINDOW_SECONDS; + else + new_cashout_time_sec = db().head_block_time().sec_since_epoch() + STEEMIT_CASHOUT_WINDOW_SECONDS_PRE_HF12; auto avg_cashout_sec = ( cur_cashout_time_sec * old_root_abs_rshares + new_cashout_time_sec * abs_rshares ) / ( old_root_abs_rshares + abs_rshares ); FC_ASSERT( abs_rshares > 0 ); @@ -880,7 +920,10 @@ void vote_evaluator::do_apply( const vote_operation& o ) db().modify( root, [&]( comment_object& c ) { c.children_abs_rshares += abs_rshares; - c.cashout_time = fc::time_point_sec( std::min( uint32_t( avg_cashout_sec.to_uint64() ), c.max_cashout_time.sec_since_epoch() ) ); + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) && c.last_payout > fc::time_point_sec::min() ) + c.cashout_time = c.last_payout + STEEMIT_SECOND_CASHOUT_WINDOW; + else + c.cashout_time = fc::time_point_sec( std::min( uint32_t( avg_cashout_sec.to_uint64() ), c.max_cashout_time.sec_since_epoch() ) ); if( c.max_cashout_time == fc::time_point_sec::maximum() ) c.max_cashout_time = db().head_block_time() + fc::seconds( STEEMIT_MAX_CASHOUT_WINDOW_SECONDS ); @@ -1010,7 +1053,12 @@ void vote_evaluator::do_apply( const vote_operation& o ) auto old_root_abs_rshares = root.children_abs_rshares.value; fc::uint128_t cur_cashout_time_sec = db().calculate_discussion_payout_time( comment ).sec_since_epoch(); - fc::uint128_t new_cashout_time_sec = db().head_block_time().sec_since_epoch() + STEEMIT_CASHOUT_WINDOW_SECONDS; + fc::uint128_t new_cashout_time_sec; + + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) ) + new_cashout_time_sec = db().head_block_time().sec_since_epoch() + STEEMIT_CASHOUT_WINDOW_SECONDS; + else + new_cashout_time_sec = db().head_block_time().sec_since_epoch() + STEEMIT_CASHOUT_WINDOW_SECONDS_PRE_HF12; auto avg_cashout_sec = (cur_cashout_time_sec * old_root_abs_rshares + new_cashout_time_sec * abs_rshares ) / ( old_root_abs_rshares + abs_rshares ); db().modify( comment, [&]( comment_object& c ) @@ -1029,7 +1077,10 @@ void vote_evaluator::do_apply( const vote_operation& o ) db().modify( root, [&]( comment_object& c ) { c.children_abs_rshares += abs_rshares; - c.cashout_time = fc::time_point_sec( std::min( uint32_t( avg_cashout_sec.to_uint64() ), c.max_cashout_time.sec_since_epoch() ) ); + if( db().has_hardfork( STEEMIT_HARDFORK_0_12__177 ) && c.last_payout > fc::time_point_sec::min() ) + c.cashout_time = c.last_payout + STEEMIT_SECOND_CASHOUT_WINDOW; + else + c.cashout_time = fc::time_point_sec( std::min( uint32_t( avg_cashout_sec.to_uint64() ), c.max_cashout_time.sec_since_epoch() ) ); if( c.max_cashout_time == fc::time_point_sec::maximum() ) c.max_cashout_time = db().head_block_time() + fc::seconds( STEEMIT_MAX_CASHOUT_WINDOW_SECONDS ); @@ -1090,6 +1141,9 @@ void pow_evaluator::do_apply( const pow_operation& o ) } } + if( !db().has_hardfork( STEEMIT_HARDFORK_0_12__179 ) ) + FC_ASSERT( o.props.maximum_block_size >= STEEMIT_MIN_BLOCK_SIZE_LIMIT * 2 ); + const auto& accounts_by_name = db().get_index_type().indices().get(); auto itr = accounts_by_name.find(o.worker_account); if(itr == accounts_by_name.end()) { @@ -1372,6 +1426,11 @@ void recover_account_evaluator::do_apply( const recover_account_operation& o ) { FC_ASSERT( db().has_hardfork( STEEMIT_HARDFORK_0_11__169 ) ); + const auto& account = db().get_account( o.account_to_recover ); + + if( db().has_hardfork( STEEMIT_HARDFORK_0_12 ) ) + FC_ASSERT( db().head_block_time() - account.last_account_recovery > STEEMIT_OWNER_UPDATE_LIMIT ); + const auto& recovery_request_idx = db().get_index_type< account_recovery_request_index >().indices().get< by_account >(); auto request = recovery_request_idx.find( o.account_to_recover ); @@ -1392,7 +1451,11 @@ void recover_account_evaluator::do_apply( const recover_account_operation& o ) FC_ASSERT( found, "Recent authority not found in authority history" ); db().remove( *request ); // Remove first, update_owner_authority may invalidate iterator - db().update_owner_authority( db().get_account( o.account_to_recover ), o.new_owner_authority ); + db().update_owner_authority( account, o.new_owner_authority ); + db().modify( account, [&]( account_object& a ) + { + a.last_account_recovery = db().head_block_time(); + }); } void change_recovery_account_evaluator::do_apply( const change_recovery_account_operation& o ) diff --git a/libraries/fc b/libraries/fc index c109dbecf3..c672e3b4b4 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit c109dbecf3bf72b464f994e1870d93af34095e9c +Subproject commit c672e3b4b4676ee90a63dbee4179207bbcd14e56 diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 22dee62b93..ed581181ed 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -62,25 +62,38 @@ account_history_plugin_impl::~account_history_plugin_impl() return; } -void account_history_plugin_impl::on_operation( const operation_object& op_obj ) { - flat_set impacted; - steemit::chain::database& db = database(); - - const auto& hist_idx = db.get_index_type().indices().get(); - const operation_object* new_obj = nullptr; - app::operation_get_impacted_accounts( op_obj.op, impacted ); - - for( const auto& item : impacted ) { - auto itr = _tracked_accounts.lower_bound( item ); - if( !_tracked_accounts.size() || (itr != _tracked_accounts.end() && itr->first <= item && itr->second < item) ) { +struct operation_visitor { + operation_visitor( database& db, const operation_object& op, const operation_object*& n, string i ):_db(db),op_obj(op),new_obj(n),item(i){}; + typedef void result_type; + + database& _db; + const operation_object& op_obj; + const operation_object*& new_obj; + string item; + + /// ignore these ops + /* + void operator()( const comment_operation& ) {} + void operator()( const vote_operation& ) {} + void operator()( const delete_comment_operation& ){} + void operator()( const custom_json_operation& ) {} + void operator()( const custom_operation& ) {} + void operator()( const curate_reward_operation& ) {} + */ + + + template + void operator()( Op&& )const{ + + const auto& hist_idx = _db.get_index_type().indices().get(); if( !new_obj ) { - new_obj = &db.create( [&]( operation_object& obj ){ + new_obj = &_db.create( [&]( operation_object& obj ){ obj.trx_id = op_obj.trx_id; obj.block = op_obj.block; obj.trx_in_block = op_obj.trx_in_block; obj.op_in_trx = op_obj.op_in_trx; obj.virtual_op = op_obj.virtual_op; - obj.timestamp = db.head_block_time(); + obj.timestamp = _db.head_block_time(); obj.op = op_obj.op; }); } @@ -90,11 +103,26 @@ void account_history_plugin_impl::on_operation( const operation_object& op_obj ) if( hist_itr != hist_idx.end() && hist_itr->account == item ) sequence = hist_itr->sequence + 1; - const auto& ahist = db.create( [&]( account_history_object& ahist ){ + /*const auto& ahist = */_db.create( [&]( account_history_object& ahist ){ ahist.account = item; ahist.sequence = sequence; ahist.op = new_obj->id; }); + } +}; + +void account_history_plugin_impl::on_operation( const operation_object& op_obj ) { + flat_set impacted; + steemit::chain::database& db = database(); + + const auto& hist_idx = db.get_index_type().indices().get(); + const operation_object* new_obj = nullptr; + app::operation_get_impacted_accounts( op_obj.op, impacted ); + + for( const auto& item : impacted ) { + auto itr = _tracked_accounts.lower_bound( item ); + if( !_tracked_accounts.size() || (itr != _tracked_accounts.end() && itr->first <= item && itr->second < item) ) { + op_obj.op.visit( operation_visitor(db, op_obj, new_obj, item) ); } } } diff --git a/libraries/plugins/auth_util/CMakeLists.txt b/libraries/plugins/auth_util/CMakeLists.txt new file mode 100644 index 0000000000..28f2452fb9 --- /dev/null +++ b/libraries/plugins/auth_util/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB HEADERS "include/steemit/plugins/auth_util/*.hpp") + +add_library( steemit_auth_util + ${HEADERS} + auth_util_plugin.cpp + auth_util_api.cpp + ) + +target_link_libraries( steemit_auth_util steemit_app steemit_chain fc graphene_db ) +target_include_directories( steemit_auth_util + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/auth_util/auth_util_api.cpp b/libraries/plugins/auth_util/auth_util_api.cpp new file mode 100644 index 0000000000..a8ee741314 --- /dev/null +++ b/libraries/plugins/auth_util/auth_util_api.cpp @@ -0,0 +1,96 @@ + +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +namespace steemit { namespace plugin { namespace auth_util { + +using boost::container::flat_set; + +namespace detail { + +class auth_util_api_impl +{ + public: + auth_util_api_impl( steemit::app::application& _app ); + void check_authority_signature( const check_authority_signature_params& args, check_authority_signature_result& result ); + + std::shared_ptr< steemit::plugin::auth_util::auth_util_plugin > get_plugin(); + + steemit::app::application& app; +}; + +auth_util_api_impl::auth_util_api_impl( steemit::app::application& _app ) : app( _app ) +{} + +std::shared_ptr< steemit::plugin::auth_util::auth_util_plugin > auth_util_api_impl::get_plugin() +{ + return app.get_plugin< auth_util_plugin >( "auth_util" ); +} + +void auth_util_api_impl::check_authority_signature( const check_authority_signature_params& args, check_authority_signature_result& result ) +{ + std::shared_ptr< chain::database > db = app.chain_database(); + const chain::account_object& acct = db->get_account( args.account_name ); + const chain::authority* auth = nullptr; + if( (args.level == "posting") || (args.level == "p") ) + { + auth = &acct.posting; + } + else if( (args.level == "active") || (args.level == "a") || (args.level == "") ) + { + auth = &acct.active; + } + else if( (args.level == "owner") || (args.level == "o") ) + { + auth = &acct.owner; + } + else + { + FC_ASSERT( false, "invalid level specified" ); + } + flat_set< chain::public_key_type > signing_keys; + for( const chain::signature_type& sig : args.sigs ) + { + result.keys.emplace_back( fc::ecc::public_key( sig, args.dig, true ) ); + signing_keys.insert( result.keys.back() ); + } + + chain::sign_state ss( signing_keys, [&db]( const std::string& account_name ) -> const chain::authority* + { + return &db->get_account( account_name ).active; + } ); + + bool has_authority = ss.check_authority( auth ); + FC_ASSERT( has_authority ); + + return; +} + +} // detail + +auth_util_api::auth_util_api( const steemit::app::api_context& ctx ) +{ + my = std::make_shared< detail::auth_util_api_impl >(ctx.app); +} + +void auth_util_api::on_api_startup() { } + +check_authority_signature_result auth_util_api::check_authority_signature( check_authority_signature_params args ) +{ + check_authority_signature_result result; + my->check_authority_signature( args, result ); + return result; +} + +} } } // steemit::plugin::auth_util diff --git a/libraries/plugins/auth_util/auth_util_plugin.cpp b/libraries/plugins/auth_util/auth_util_plugin.cpp new file mode 100644 index 0000000000..57f351f061 --- /dev/null +++ b/libraries/plugins/auth_util/auth_util_plugin.cpp @@ -0,0 +1,35 @@ + + +#include +#include + +#include + +namespace steemit { namespace plugin { namespace auth_util { + +auth_util_plugin::auth_util_plugin() {} +auth_util_plugin::~auth_util_plugin() {} + +std::string auth_util_plugin::plugin_name()const +{ + return "auth_util"; +} + +void auth_util_plugin::plugin_initialize( const boost::program_options::variables_map& options ) +{ +} + +void auth_util_plugin::plugin_startup() +{ + chain::database& db = database(); + + app().register_api_factory< auth_util_api >( "auth_util_api" ); +} + +void auth_util_plugin::plugin_shutdown() +{ +} + +} } } // steemit::plugin::auth_util + +STEEMIT_DEFINE_PLUGIN( auth_util, steemit::plugin::auth_util::auth_util_plugin ) diff --git a/libraries/plugins/auth_util/include/steemit/plugins/auth_util/auth_util_api.hpp b/libraries/plugins/auth_util/include/steemit/plugins/auth_util/auth_util_api.hpp new file mode 100644 index 0000000000..248d5ccf66 --- /dev/null +++ b/libraries/plugins/auth_util/include/steemit/plugins/auth_util/auth_util_api.hpp @@ -0,0 +1,61 @@ + +#pragma once + +#include +#include + +#include + +#include + +namespace steemit { namespace app { + struct api_context; +} } + +namespace steemit { namespace plugin { namespace auth_util { + +namespace detail { +class auth_util_api_impl; +} + +struct check_authority_signature_params +{ + std::string account_name; + std::string level; + fc::sha256 dig; + std::vector< chain::signature_type > sigs; +}; + +struct check_authority_signature_result +{ + std::vector< chain::public_key_type > keys; +}; + +class auth_util_api +{ + public: + auth_util_api( const steemit::app::api_context& ctx ); + + void on_api_startup(); + + check_authority_signature_result check_authority_signature( check_authority_signature_params args ); + + private: + std::shared_ptr< detail::auth_util_api_impl > my; +}; + +} } } + +FC_REFLECT( steemit::plugin::auth_util::check_authority_signature_params, + (account_name) + (level) + (dig) + (sigs) + ) +FC_REFLECT( steemit::plugin::auth_util::check_authority_signature_result, + (keys) + ) + +FC_API( steemit::plugin::auth_util::auth_util_api, + (check_authority_signature) + ) diff --git a/libraries/plugins/auth_util/include/steemit/plugins/auth_util/auth_util_plugin.hpp b/libraries/plugins/auth_util/include/steemit/plugins/auth_util/auth_util_plugin.hpp new file mode 100644 index 0000000000..07963f0b75 --- /dev/null +++ b/libraries/plugins/auth_util/include/steemit/plugins/auth_util/auth_util_plugin.hpp @@ -0,0 +1,20 @@ + +#pragma once + +#include + +namespace steemit { namespace plugin { namespace auth_util { + +class auth_util_plugin : public steemit::app::plugin +{ + public: + auth_util_plugin(); + virtual ~auth_util_plugin(); + + virtual std::string plugin_name()const override; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; + virtual void plugin_startup() override; + virtual void plugin_shutdown() override; +}; + +} } } diff --git a/libraries/plugins/block_info/CMakeLists.txt b/libraries/plugins/block_info/CMakeLists.txt new file mode 100644 index 0000000000..397e5b3b1c --- /dev/null +++ b/libraries/plugins/block_info/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB HEADERS "include/steemit/plugins/block_info/*.hpp") + +add_library( steemit_block_info + ${HEADERS} + block_info_plugin.cpp + block_info_api.cpp + ) + +target_link_libraries( steemit_block_info steemit_app steemit_chain fc graphene_db ) +target_include_directories( steemit_block_info + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/block_info/block_info_api.cpp b/libraries/plugins/block_info/block_info_api.cpp new file mode 100644 index 0000000000..563357404b --- /dev/null +++ b/libraries/plugins/block_info/block_info_api.cpp @@ -0,0 +1,85 @@ + +#include +#include + +#include +#include + +namespace steemit { namespace plugin { namespace block_info { + +namespace detail { + +class block_info_api_impl +{ + public: + block_info_api_impl( steemit::app::application& _app ); + + std::shared_ptr< steemit::plugin::block_info::block_info_plugin > get_plugin(); + + void get_block_info( const get_block_info_args& args, std::vector< block_info >& result ); + void get_blocks_with_info( const get_block_info_args& args, std::vector< block_with_info >& result ); + + steemit::app::application& app; +}; + +block_info_api_impl::block_info_api_impl( steemit::app::application& _app ) : app( _app ) +{} + +std::shared_ptr< steemit::plugin::block_info::block_info_plugin > block_info_api_impl::get_plugin() +{ + return app.get_plugin< block_info_plugin >( "block_info" ); +} + +void block_info_api_impl::get_block_info( const get_block_info_args& args, std::vector< block_info >& result ) +{ + const std::vector< block_info >& _block_info = get_plugin()->_block_info; + + FC_ASSERT( args.start_block_num > 0 ); + FC_ASSERT( args.count <= 10000 ); + uint32_t n = std::min( uint32_t( _block_info.size() ), args.start_block_num + args.count ); + for( uint32_t block_num=args.start_block_num; block_num& result ) +{ + const std::vector< block_info >& _block_info = get_plugin()->_block_info; + const chain::database& db = get_plugin()->database(); + + FC_ASSERT( args.start_block_num > 0 ); + FC_ASSERT( args.count <= 10000 ); + uint32_t n = std::min( uint32_t( _block_info.size() ), args.start_block_num + args.count ); + for( uint32_t block_num=args.start_block_num; block_num(ctx.app); +} + +std::vector< block_info > block_info_api::get_block_info( get_block_info_args args ) +{ + std::vector< block_info > result; + my->get_block_info( args, result ); + return result; +} + +std::vector< block_with_info > block_info_api::get_blocks_with_info( get_block_info_args args ) +{ + std::vector< block_with_info > result; + my->get_blocks_with_info( args, result ); + return result; +} + +void block_info_api::on_api_startup() { } + +} } } // steemit::plugin::block_info diff --git a/libraries/plugins/block_info/block_info_plugin.cpp b/libraries/plugins/block_info/block_info_plugin.cpp new file mode 100644 index 0000000000..02d58f82e4 --- /dev/null +++ b/libraries/plugins/block_info/block_info_plugin.cpp @@ -0,0 +1,59 @@ + +#include +#include + +#include +#include +#include + +#include + +namespace steemit { namespace plugin { namespace block_info { + +block_info_plugin::block_info_plugin() {} +block_info_plugin::~block_info_plugin() {} + +std::string block_info_plugin::plugin_name()const +{ + return "block_info"; +} + +void block_info_plugin::plugin_initialize( const boost::program_options::variables_map& options ) +{ + chain::database& db = database(); + + _applied_block_conn = db.applied_block.connect([this](const chain::signed_block& b){ on_applied_block(b); }); +} + +void block_info_plugin::plugin_startup() +{ + app().register_api_factory< block_info_api >( "block_info_api" ); +} + +void block_info_plugin::plugin_shutdown() +{ +} + +void block_info_plugin::on_applied_block( const chain::signed_block& b ) +{ + uint32_t block_num = b.block_num(); + const chain::database& db = database(); + + while( block_num >= _block_info.size() ) + _block_info.emplace_back(); + + block_info& info = _block_info[block_num]; + const chain::dynamic_global_property_object& dgpo = db.get_dynamic_global_properties(); + + info.block_id = b.id(); + info.block_size = fc::raw::pack_size( b ); + info.average_block_size = dgpo.average_block_size; + info.aslot = dgpo.current_aslot; + info.last_irreversible_block_num = dgpo.last_irreversible_block_num; + info.num_pow_witnesses = dgpo.num_pow_witnesses; + return; +} + +} } } // steemit::plugin::block_info + +STEEMIT_DEFINE_PLUGIN( block_info, steemit::plugin::block_info::block_info_plugin ) diff --git a/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info.hpp b/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info.hpp new file mode 100644 index 0000000000..004a94019d --- /dev/null +++ b/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info.hpp @@ -0,0 +1,38 @@ + +#pragma once + +#include + +namespace steemit { namespace plugin { namespace block_info { + +struct block_info +{ + chain::block_id_type block_id; + uint32_t block_size = 0; + uint32_t average_block_size = 0; + uint64_t aslot = 0; + uint32_t last_irreversible_block_num = 0; + uint32_t num_pow_witnesses = 0; +}; + +struct block_with_info +{ + chain::signed_block block; + block_info info; +}; + +} } } + +FC_REFLECT( steemit::plugin::block_info::block_info, + (block_id) + (block_size) + (average_block_size) + (aslot) + (last_irreversible_block_num) + (num_pow_witnesses) + ) + +FC_REFLECT( steemit::plugin::block_info::block_with_info, + (block) + (info) + ) diff --git a/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info_api.hpp b/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info_api.hpp new file mode 100644 index 0000000000..22fb9042ea --- /dev/null +++ b/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info_api.hpp @@ -0,0 +1,48 @@ + +#pragma once + +#include + +#include + +namespace steemit { namespace app { + struct api_context; +} } + +namespace steemit { namespace plugin { namespace block_info { + +namespace detail { +class block_info_api_impl; +} + +struct get_block_info_args +{ + uint32_t start_block_num = 0; + uint32_t count = 1000; +}; + +class block_info_api +{ + public: + block_info_api( const steemit::app::api_context& ctx ); + + void on_api_startup(); + + std::vector< block_info > get_block_info( get_block_info_args args ); + std::vector< block_with_info > get_blocks_with_info( get_block_info_args args ); + + private: + std::shared_ptr< detail::block_info_api_impl > my; +}; + +} } } + +FC_REFLECT( steemit::plugin::block_info::get_block_info_args, + (start_block_num) + (count) + ) + +FC_API( steemit::plugin::block_info::block_info_api, + (get_block_info) + (get_blocks_with_info) + ) diff --git a/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info_plugin.hpp b/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info_plugin.hpp new file mode 100644 index 0000000000..508547a3b9 --- /dev/null +++ b/libraries/plugins/block_info/include/steemit/plugins/block_info/block_info_plugin.hpp @@ -0,0 +1,34 @@ + +#pragma once + +#include +#include + +#include +#include + +namespace steemit { namespace chain { +struct signed_block; +} } + +namespace steemit { namespace plugin { namespace block_info { + +class block_info_plugin : public steemit::app::plugin +{ + public: + block_info_plugin(); + virtual ~block_info_plugin(); + + virtual std::string plugin_name()const override; + virtual void plugin_initialize( const boost::program_options::variables_map& options ) override; + virtual void plugin_startup() override; + virtual void plugin_shutdown() override; + + void on_applied_block( const chain::signed_block& b ); + + std::vector< block_info > _block_info; + + boost::signals2::scoped_connection _applied_block_conn; +}; + +} } } diff --git a/libraries/plugins/tags/tags_plugin.cpp b/libraries/plugins/tags/tags_plugin.cpp index b4cc650d30..147f5b0a72 100644 --- a/libraries/plugins/tags/tags_plugin.cpp +++ b/libraries/plugins/tags/tags_plugin.cpp @@ -152,8 +152,16 @@ struct operation_visitor { comment_metadata meta; - if( c.json_metadata.size() ){ - meta = fc::json::from_string( c.json_metadata ).as(); + if( c.json_metadata.size() ) + { + try + { + meta = fc::json::from_string( c.json_metadata ).as(); + } + catch( const fc::exception& e ) + { + // Do nothing on malformed json_metadata + } } set lower_tags; @@ -260,10 +268,12 @@ struct operation_visitor { void operator()( const vote_operation& op )const { update_tags( _db.get_comment( op.author, op.permlink ) ); + /* update_peer_stats( _db.get_account(op.voter), _db.get_account(op.author), _db.get_comment(op.author, op.permlink), op.weight ); + */ } void operator()( const delete_comment_operation& op )const { diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 1977017851..2f57b66f1b 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -146,7 +146,7 @@ void witness_plugin::plugin_initialize(const boost::program_options::variables_m const uint32_t maximum_block_size = options["miner-maximum-block-size"].as(); if( maximum_block_size < STEEMIT_MIN_BLOCK_SIZE_LIMIT ) - wlog( "miner-maximum-block-size is below the minimum block size limit, using minimum of 128 KB instead" ); + wlog( "miner-maximum-block-size is below the minimum block size limit, using default of 128 KB instead" ); else if ( maximum_block_size > STEEMIT_MAX_BLOCK_SIZE ) { wlog( "miner-maximum-block-size is above the maximum block size limit, using maximum of 750 MB instead" ); @@ -420,7 +420,7 @@ void witness_plugin::on_applied_block( const chain::signed_block& b ) auto minutes = uint64_t(seconds / 60.0); - if( uint64_t(hps) > 0 ) + if( _total_hashes > 0 ) ilog( "hash rate: ${x} hps target: ${t} queue: ${l} estimated time to produce: ${m} minutes", ("x",uint64_t(hps)) ("t",bits) ("m", minutes ) ("l",dgp.num_pow_witnesses) ); diff --git a/libraries/wallet/include/steemit/wallet/wallet.hpp b/libraries/wallet/include/steemit/wallet/wallet.hpp index 3e6d74cb05..b46a022d58 100644 --- a/libraries/wallet/include/steemit/wallet/wallet.hpp +++ b/libraries/wallet/include/steemit/wallet/wallet.hpp @@ -707,6 +707,11 @@ class wallet_api */ annotated_signed_transaction vote( string voter, string author, string permlink, int16_t weight, bool broadcast ); + /** + * Sets the amount of time in the future until a transaction expires. + */ + void set_transaction_expiration(uint32_t seconds); + /** * Challenge a user's authority. The challenger pays a fee to the challenged which is depositted as * Steem Power. Until the challenged proves their active key, all posting rights are revoked. @@ -717,10 +722,42 @@ class wallet_api */ annotated_signed_transaction challenge( string challenger, string challenged, bool broadcast ); + /** + * Create an account recovery request as a recover account. The syntax for this command contains a serialized authority object + * so there is an example below on how to pass in the authority. + * + * request_account_recovery "your_account" "account_to_recover" {"weight_threshold": 1,"account_auths": [], "key_auths": [["new_public_key",1]]} true + * + * @param recovery_account The name of your account + * @param account_to_recover The name of the account you are trying to recover + * @param new_authority The new owner authority for the recovered account. This should be given to you by the holder of the compromised or lost account. + * @param broadcast true if you wish to broadcast the transaction + */ annotated_signed_transaction request_account_recovery( string recovery_account, string account_to_recover, authority new_authority, bool broadcast ); + + /** + * Recover your account using a recovery request created by your recovery account. The syntax for this commain contains a serialized + * authority object, so there is an example below on how to pass in the authority. + * + * recover_account "your_account" {"weight_threshold": 1,"account_auths": [], "key_auths": [["old_public_key",1]]} {"weight_threshold": 1,"account_auths": [], "key_auths": [["new_public_key",1]]} true + * + * @param account_to_recover The name of your account + * @param recent_authority A recent owner authority on your account + * @param new_authority The new authority that your recovery account used in the account recover request. + * @param broadcast true if you wish to broadcast the transaction + */ annotated_signed_transaction recover_account( string account_to_recover, authority recent_authority, authority new_authority, bool broadcast ); + + /** + * Change your recovery account after a 30 day delay. + * + * @param owner The name of your account + * @param new_recovery_account The name of the recovery account you wish to have + * @param broadcast true if you wish to broadcast the transaction + */ annotated_signed_transaction change_recovery_account( string owner, string new_recovery_account, bool broadcast ); + vector< owner_authority_history_object > get_owner_history( string account )const; /** * Prove an account's active authority, fulfilling a challenge, restoring posting rights, and making @@ -833,12 +870,13 @@ FC_API( steemit::wallet::wallet_api, (cancel_order) (post_comment) (vote) + (set_transaction_expiration) (challenge) (prove) (request_account_recovery) (recover_account) (change_recovery_account) - + (get_owner_history) // private message api (send_private_message) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index d611658842..697e221348 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -525,6 +525,12 @@ class wallet_api_impl return _remote_db->get_witness_by_account( owner_account ); } + void set_transaction_expiration( uint32_t tx_expiration_seconds ) + { + FC_ASSERT( tx_expiration_seconds < STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + _tx_expiration_seconds = tx_expiration_seconds; + } + annotated_signed_transaction sign_transaction(signed_transaction tx, bool broadcast = false) { flat_set< string > req_active_approvals; @@ -633,7 +639,7 @@ class wallet_api_impl auto dyn_props = _remote_db->get_dynamic_global_properties(); tx.set_reference_block( dyn_props.head_block_id ); - tx.set_expiration( dyn_props.time + fc::seconds(30) ); + tx.set_expiration( dyn_props.time + fc::seconds(_tx_expiration_seconds) ); tx.signatures.clear(); //idump((_keys)); @@ -901,6 +907,7 @@ class wallet_api_impl optional< fc::api > _remote_net_node; optional< fc::api > _remote_message_api; optional< fc::api > _remote_follow_api; + uint32_t _tx_expiration_seconds = 30; flat_map _prototype_ops; @@ -1289,6 +1296,11 @@ annotated_signed_transaction wallet_api::change_recovery_account( string owner, return my->sign_transaction( tx, broadcast ); } +vector< owner_authority_history_object > wallet_api::get_owner_history( string account )const +{ + return my->_remote_db->get_owner_history( account ); +} + annotated_signed_transaction wallet_api::update_account( string account_name, string json_meta, @@ -1297,21 +1309,27 @@ annotated_signed_transaction wallet_api::update_account( public_key_type posting, public_key_type memo, bool broadcast )const -{ try { - account_update_operation op; - op.account = account_name; - op.owner = authority( 1, owner, 1 ); - op.active = authority( 1, active, 1); - op.posting = authority( 1, posting, 1); - op.memo_key = memo; - op.json_metadata = json_meta; +{ + try + { + FC_ASSERT( !is_locked() ); - signed_transaction tx; - tx.operations.push_back(op); - tx.validate(); + account_update_operation op; + op.account = account_name; + op.owner = authority( 1, owner, 1 ); + op.active = authority( 1, active, 1); + op.posting = authority( 1, posting, 1); + op.memo_key = memo; + op.json_metadata = json_meta; - return my->sign_transaction( tx, broadcast ); -} FC_CAPTURE_AND_RETHROW( (account_name)(json_meta)(owner)(active)(memo)(broadcast) ) } + signed_transaction tx; + tx.operations.push_back(op); + tx.validate(); + + return my->sign_transaction( tx, broadcast ); + } + FC_CAPTURE_AND_RETHROW( (account_name)(json_meta)(owner)(active)(memo)(broadcast) ) +} annotated_signed_transaction wallet_api::update_account_auth_key( string account_name, authority_type type, public_key_type key, weight_type weight, bool broadcast ) { @@ -1840,6 +1858,11 @@ annotated_signed_transaction wallet_api::vote( string voter, string author, stri return my->sign_transaction( tx, broadcast ); } +void wallet_api::set_transaction_expiration(uint32_t seconds) +{ + my->set_transaction_expiration(seconds); +} + annotated_signed_transaction wallet_api::challenge( string challenger, string challenged, bool broadcast ) { FC_ASSERT( !is_locked() ); diff --git a/programs/util/CMakeLists.txt b/programs/util/CMakeLists.txt index a5f6268ffc..cb4f3efc5b 100644 --- a/programs/util/CMakeLists.txt +++ b/programs/util/CMakeLists.txt @@ -11,6 +11,32 @@ install( TARGETS ARCHIVE DESTINATION lib ) +add_executable( sign_digest sign_digest.cpp ) + +target_link_libraries( sign_digest + PRIVATE steemit_app steemit_chain graphene_utilities fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + +install( TARGETS + sign_digest + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) + +add_executable( sign_transaction sign_transaction.cpp ) + +target_link_libraries( sign_transaction + PRIVATE steemit_app steemit_chain graphene_utilities fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + +install( TARGETS + sign_transaction + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) + add_executable( inflation_model inflation_model.cpp ) target_link_libraries( inflation_model PRIVATE steemit_chain fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/programs/util/sign_digest.cpp b/programs/util/sign_digest.cpp new file mode 100644 index 0000000000..b3533aca34 --- /dev/null +++ b/programs/util/sign_digest.cpp @@ -0,0 +1,54 @@ + +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +struct signing_request +{ + fc::sha256 dig; + std::string wif; +}; + +struct signing_result +{ + fc::sha256 dig; + steemit::chain::public_key_type key; + steemit::chain::signature_type sig; +}; + +FC_REFLECT( signing_request, (dig)(wif) ) +FC_REFLECT( signing_result, (dig)(key)(sig) ) + +int main(int argc, char** argv, char** envp) +{ + // hash key pairs on stdin + std::string chain_id, hash, wif; + while( std::cin ) + { + std::string line; + std::getline( std::cin, line ); + boost::trim(line); + if( line == "" ) + continue; + + fc::variant v = fc::json::from_string( line, fc::json::strict_parser ); + signing_request sreq; + fc::from_variant( v, sreq ); + signing_result sres; + sres.dig = sreq.dig; + fc::ecc::private_key priv_key = *graphene::utilities::wif_to_key( sreq.wif ); + sres.sig = priv_key.sign_compact( sreq.dig ); + sres.key = steemit::chain::public_key_type( priv_key.get_public_key() ); + std::cout << fc::json::to_string( sres ) << std::endl; + } + return 0; +} diff --git a/programs/util/sign_transaction.cpp b/programs/util/sign_transaction.cpp new file mode 100644 index 0000000000..4039566035 --- /dev/null +++ b/programs/util/sign_transaction.cpp @@ -0,0 +1,60 @@ + +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +struct tx_signing_request +{ + steemit::chain::transaction tx; + std::string wif; +}; + +struct tx_signing_result +{ + steemit::chain::transaction tx; + fc::sha256 digest; + fc::sha256 sig_digest; + steemit::chain::public_key_type key; + steemit::chain::signature_type sig; +}; + +FC_REFLECT( tx_signing_request, (tx)(wif) ) +FC_REFLECT( tx_signing_result, (digest)(sig_digest)(key)(sig) ) + +int main(int argc, char** argv, char** envp) +{ + // hash key pairs on stdin + std::string chain_id, hash, wif; + while( std::cin ) + { + std::string line; + std::getline( std::cin, line ); + boost::trim(line); + if( line == "" ) + continue; + + fc::variant v = fc::json::from_string( line, fc::json::strict_parser ); + tx_signing_request sreq; + fc::from_variant( v, sreq ); + tx_signing_result sres; + sres.tx = sreq.tx; + sres.digest = sreq.tx.digest(); + sres.sig_digest = sreq.tx.sig_digest(STEEMIT_CHAIN_ID); + + fc::ecc::private_key priv_key = *graphene::utilities::wif_to_key( sreq.wif ); + sres.sig = priv_key.sign_compact( sres.sig_digest ); + sres.key = steemit::chain::public_key_type( priv_key.get_public_key() ); + std::cout << fc::json::to_string( sres ) << std::endl; + } + return 0; +} diff --git a/tests/tests/operation_time_tests.cpp b/tests/tests/operation_time_tests.cpp index b171f845c6..6e164124b1 100644 --- a/tests/tests/operation_time_tests.cpp +++ b/tests/tests/operation_time_tests.cpp @@ -1629,18 +1629,18 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) const auto& limit_order_idx = db.get_index_type< limit_order_index >().indices().get< by_account >(); auto reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ auto fill_order_op = ops[0].get< fill_order_operation >(); @@ -1728,25 +1728,25 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) BOOST_CHECK_EQUAL( fill_order_op.current_pays.amount.value, asset( alice_sbd.amount.value / 20, SBD_SYMBOL ).amount.value ); reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ BOOST_TEST_MESSAGE( "Testing a partial fill before minimum time and full fill after minimum time" ); @@ -1788,25 +1788,25 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) BOOST_CHECK_EQUAL( fill_order_op.current_pays.amount.value, asset( alice_sbd.amount.value / 20, STEEM_SYMBOL ).amount.value ); reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ generate_blocks( db.head_block_time() + STEEMIT_MIN_LIQUIDITY_REWARD_PERIOD_SEC_HF10, true ); @@ -1836,25 +1836,25 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) BOOST_CHECK_EQUAL( fill_order_op.current_pays.amount.value, asset( alice_sbd.amount.value / 20, STEEM_SYMBOL ).amount.value ); reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ BOOST_TEST_MESSAGE( "Trading to give Alice and Bob positive volumes to receive rewards" ); @@ -1909,32 +1909,32 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) BOOST_CHECK_EQUAL( fill_order_op.current_pays.amount.value, 7 * ( alice_sbd.amount.value / 20 ) ); reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "dave" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "dave" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "dave" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, dave_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, dave_steem_volume ); - BOOST_CHECK( reward->last_update == dave_reward_last_update ); + BOOST_CHECK( reward->last_update == dave_reward_last_update );*/ op.owner = "bob"; op.amount_to_sell.amount = alice_sbd.amount / 20; @@ -1962,32 +1962,32 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) BOOST_CHECK_EQUAL( fill_order_op.current_pays.amount.value, alice_sbd.amount.value / 20 ); reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "dave" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "dave" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "dave" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, dave_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, dave_steem_volume ); - BOOST_CHECK( reward->last_update == dave_reward_last_update ); + BOOST_CHECK( reward->last_update == dave_reward_last_update );*/ transfer.to = "bob"; transfer.from = "alice"; @@ -2037,32 +2037,32 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) BOOST_CHECK_EQUAL( fill_order_op.current_pays.amount.value, 3 * ( alice_sbd.amount.value / 40 ) ); reward = liquidity_idx.find( db.get_account( "alice" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "alice" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, alice_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, alice_steem_volume ); - BOOST_CHECK( reward->last_update == alice_reward_last_update ); + BOOST_CHECK( reward->last_update == alice_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "bob" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "bob" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, bob_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, bob_steem_volume ); - BOOST_CHECK( reward->last_update == bob_reward_last_update ); + BOOST_CHECK( reward->last_update == bob_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ reward = liquidity_idx.find( db.get_account( "dave" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); - BOOST_CHECK_EQUAL( reward->owner, db.get_account( "dave" ).id ); + BOOST_REQUIRE( reward == liquidity_idx.end() ); + /*BOOST_CHECK_EQUAL( reward->owner, db.get_account( "dave" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, dave_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, dave_steem_volume ); - BOOST_CHECK( reward->last_update == dave_reward_last_update ); + BOOST_CHECK( reward->last_update == dave_reward_last_update );*/ auto dave_last_order_time = db.head_block_time(); @@ -2084,7 +2084,7 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) generate_block(); - alice_balance += STEEMIT_MIN_LIQUIDITY_REWARD; + //alice_balance += STEEMIT_MIN_LIQUIDITY_REWARD; BOOST_REQUIRE_EQUAL( db.get_account( "alice" ).balance.amount.value, alice_balance.amount.value ); BOOST_REQUIRE_EQUAL( db.get_account( "bob" ).balance.amount.value, bob_balance.amount.value ); @@ -2093,12 +2093,12 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) ops = get_last_operations( 1 ); - BOOST_REQUIRE_EQUAL( ops[0].get< liquidity_reward_operation>().owner, "alice" ); - BOOST_REQUIRE_EQUAL( ops[0].get< liquidity_reward_operation>().payout.amount.value, STEEMIT_MIN_LIQUIDITY_REWARD.amount.value ); + STEEMIT_REQUIRE_THROW( ops[0].get< liquidity_reward_operation>(), fc::assert_exception ); + //BOOST_REQUIRE_EQUAL( ops[0].get< liquidity_reward_operation>().payout.amount.value, STEEMIT_MIN_LIQUIDITY_REWARD.amount.value ); generate_blocks( STEEMIT_LIQUIDITY_REWARD_BLOCKS ); - bob_balance += STEEMIT_MIN_LIQUIDITY_REWARD; + //bob_balance += STEEMIT_MIN_LIQUIDITY_REWARD; BOOST_REQUIRE_EQUAL( db.get_account( "alice" ).balance.amount.value, alice_balance.amount.value ); BOOST_REQUIRE_EQUAL( db.get_account( "bob" ).balance.amount.value, bob_balance.amount.value ); @@ -2107,8 +2107,8 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) ops = get_last_operations( 1 ); - BOOST_REQUIRE_EQUAL( ops[0].get< liquidity_reward_operation>().owner, "bob" ); - BOOST_REQUIRE_EQUAL( ops[0].get< liquidity_reward_operation>().payout.amount.value, STEEMIT_MIN_LIQUIDITY_REWARD.amount.value ); + STEEMIT_REQUIRE_THROW( ops[0].get< liquidity_reward_operation>(), fc::assert_exception ); + //BOOST_REQUIRE_EQUAL( ops[0].get< liquidity_reward_operation>().payout.amount.value, STEEMIT_MIN_LIQUIDITY_REWARD.amount.value ); alice_steem_volume = 0; alice_sbd_volume = 0; @@ -2133,11 +2133,11 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) generate_blocks( db.head_block_time() + ( STEEMIT_BLOCK_INTERVAL / 2 ) + STEEMIT_LIQUIDITY_TIMEOUT_SEC, true ); reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); + /*BOOST_REQUIRE( reward == liquidity_idx.end() ); BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ generate_block(); @@ -2157,14 +2157,262 @@ BOOST_AUTO_TEST_CASE( liquidity_rewards ) sam_reward_last_update = db.head_block_time(); reward = liquidity_idx.find( db.get_account( "sam" ).id ); - BOOST_REQUIRE( reward != liquidity_idx.end() ); + /*BOOST_REQUIRE( reward == liquidity_idx.end() ); BOOST_CHECK_EQUAL( reward->owner, db.get_account( "sam" ).id ); BOOST_CHECK_EQUAL( reward->sbd_volume, sam_sbd_volume ); BOOST_CHECK_EQUAL( reward->steem_volume, sam_steem_volume ); - BOOST_CHECK( reward->last_update == sam_reward_last_update ); + BOOST_CHECK( reward->last_update == sam_reward_last_update );*/ } FC_LOG_AND_RETHROW(); } +BOOST_AUTO_TEST_CASE( post_rate_limit ) +{ + try + { + ACTORS( (alice) ) + + fund( "alice", 10000 ); + vest( "alice", 10000 ); + + comment_operation op; + op.author = "alice"; + op.permlink = "test1"; + op.parent_author = ""; + op.parent_permlink = "test"; + op.body = "test"; + + signed_transaction tx; + + tx.operations.push_back( op ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + uint64_t alice_post_bandwidth = STEEMIT_100_PERCENT; + + BOOST_REQUIRE( alice.post_bandwidth == alice_post_bandwidth ); + BOOST_REQUIRE( db.get_comment( "alice", "test1" ).reward_weight == STEEMIT_100_PERCENT ); + + tx.operations.clear(); + tx.signatures.clear(); + + generate_blocks( db.head_block_time() + STEEMIT_MIN_ROOT_COMMENT_INTERVAL + fc::seconds( STEEMIT_BLOCK_INTERVAL ), true ); + + op.permlink = "test2"; + + tx.operations.push_back( op ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + alice_post_bandwidth = STEEMIT_100_PERCENT + ( alice_post_bandwidth * ( STEEMIT_POST_AVERAGE_WINDOW - STEEMIT_MIN_ROOT_COMMENT_INTERVAL.to_seconds() - STEEMIT_BLOCK_INTERVAL ) / STEEMIT_POST_AVERAGE_WINDOW ); + + BOOST_REQUIRE( db.get_account( "alice" ).post_bandwidth == alice_post_bandwidth ); + BOOST_REQUIRE( db.get_comment( "alice", "test2" ).reward_weight == STEEMIT_100_PERCENT ); + + generate_blocks( db.head_block_time() + STEEMIT_MIN_ROOT_COMMENT_INTERVAL + fc::seconds( STEEMIT_BLOCK_INTERVAL ), true ); + + tx.operations.clear(); + tx.signatures.clear(); + + op.permlink = "test3"; + + tx.operations.push_back( op ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + alice_post_bandwidth = STEEMIT_100_PERCENT + ( alice_post_bandwidth * ( STEEMIT_POST_AVERAGE_WINDOW - STEEMIT_MIN_ROOT_COMMENT_INTERVAL.to_seconds() - STEEMIT_BLOCK_INTERVAL ) / STEEMIT_POST_AVERAGE_WINDOW ); + + BOOST_REQUIRE( db.get_account( "alice" ).post_bandwidth == alice_post_bandwidth ); + BOOST_REQUIRE( db.get_comment( "alice", "test3" ).reward_weight == STEEMIT_100_PERCENT ); + + generate_blocks( db.head_block_time() + STEEMIT_MIN_ROOT_COMMENT_INTERVAL + fc::seconds( STEEMIT_BLOCK_INTERVAL ), true ); + + tx.operations.clear(); + tx.signatures.clear(); + + op.permlink = "test4"; + + tx.operations.push_back( op ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + alice_post_bandwidth = STEEMIT_100_PERCENT + ( alice_post_bandwidth * ( STEEMIT_POST_AVERAGE_WINDOW - STEEMIT_MIN_ROOT_COMMENT_INTERVAL.to_seconds() - STEEMIT_BLOCK_INTERVAL ) / STEEMIT_POST_AVERAGE_WINDOW ); + + BOOST_REQUIRE( db.get_account( "alice" ).post_bandwidth == alice_post_bandwidth ); + BOOST_REQUIRE( db.get_comment( "alice", "test4" ).reward_weight == STEEMIT_100_PERCENT ); + + generate_blocks( db.head_block_time() + STEEMIT_MIN_ROOT_COMMENT_INTERVAL + fc::seconds( STEEMIT_BLOCK_INTERVAL ), true ); + + tx.operations.clear(); + tx.signatures.clear(); + + op.permlink = "test5"; + + tx.operations.push_back( op ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + alice_post_bandwidth = STEEMIT_100_PERCENT + ( alice_post_bandwidth * ( STEEMIT_POST_AVERAGE_WINDOW - STEEMIT_MIN_ROOT_COMMENT_INTERVAL.to_seconds() - STEEMIT_BLOCK_INTERVAL ) / STEEMIT_POST_AVERAGE_WINDOW ); + auto reward_weight = ( STEEMIT_POST_WEIGHT_CONSTANT * STEEMIT_100_PERCENT ) / ( alice_post_bandwidth * alice_post_bandwidth ); + + BOOST_REQUIRE( db.get_account( "alice" ).post_bandwidth == alice_post_bandwidth ); + BOOST_REQUIRE( db.get_comment( "alice", "test5" ).reward_weight == reward_weight ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( comment_freeze ) +{ + try + { + ACTORS( (alice)(bob)(sam)(dave) ) + fund( "alice", 10000 ); + fund( "bob", 10000 ); + fund( "sam", 10000 ); + fund( "dave", 10000 ); + + vest( "alice", 10000 ); + vest( "bob", 10000 ); + vest( "sam", 10000 ); + vest( "dave", 10000 ); + + auto exchange_rate = price( ASSET( "1.250 TESTS" ), ASSET( "1.000 TBD" ) ); + set_price_feed( exchange_rate ); + + signed_transaction tx; + + comment_operation comment; + comment.author = "alice"; + comment.parent_author = ""; + comment.permlink = "test"; + comment.parent_permlink = "test"; + comment.body = "test"; + + tx.operations.push_back( comment ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + comment.body = "test2"; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( comment ); + tx.sign( alice_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + vote_operation vote; + vote.weight = STEEMIT_100_PERCENT; + vote.voter = "bob"; + vote.author = "alice"; + vote.permlink = "test"; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( vote ); + tx.sign( bob_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + BOOST_REQUIRE( db.get_comment( "alice", "test" ).last_payout == fc::time_point_sec::min() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time != fc::time_point_sec::min() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time != fc::time_point_sec::maximum() ); + + generate_blocks( db.get_comment( "alice", "test" ).cashout_time, true ); + + BOOST_REQUIRE( db.get_comment( "alice", "test" ).last_payout == db.head_block_time() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time == db.head_block_time() + STEEMIT_SECOND_CASHOUT_WINDOW ); + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( vote ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( bob_private_key, db.get_chain_id() ); + STEEMIT_REQUIRE_THROW( db.push_transaction( tx, 0 ), fc::assert_exception ); + + vote.voter = "sam"; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( vote ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( sam_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + comment.body = "test3"; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( comment ); + tx.sign( alice_private_key, db.get_chain_id() ); + STEEMIT_REQUIRE_THROW( db.push_transaction( tx, 0 ), fc::assert_exception ); + + generate_blocks( db.get_comment( "alice", "test" ).cashout_time, true ); + + BOOST_REQUIRE( db.get_comment( "alice", "test" ).last_payout == db.head_block_time() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time == fc::time_point_sec::maximum() ); + + vote.voter = "sam"; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( vote ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( sam_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time == fc::time_point_sec::maximum() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).net_rshares.value == 0 ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).abs_rshares.value == 0 ); + + vote.voter = "bob"; + vote.weight = STEEMIT_100_PERCENT * -1; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( vote ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( bob_private_key, db.get_chain_id() ); + db.push_transaction( tx, 0 ); + + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time == fc::time_point_sec::maximum() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).net_rshares.value == 0 ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).abs_rshares.value == 0 ); + + vote.voter = "dave"; + vote.weight = 0; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( vote ); + tx.set_expiration( db.head_block_time() + STEEMIT_MAX_TIME_UNTIL_EXPIRATION ); + tx.sign( dave_private_key, db.get_chain_id() ); + + db.push_transaction( tx, 0 ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).cashout_time == fc::time_point_sec::maximum() ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).net_rshares.value == 0 ); + BOOST_REQUIRE( db.get_comment( "alice", "test" ).abs_rshares.value == 0 ); + + comment.body = "test4"; + + tx.operations.clear(); + tx.signatures.clear(); + + tx.operations.push_back( comment ); + tx.sign( alice_private_key, db.get_chain_id() ); + STEEMIT_REQUIRE_THROW( db.push_transaction( tx, 0 ), fc::assert_exception ); + } + FC_LOG_AND_RETHROW() +} + + BOOST_AUTO_TEST_SUITE_END() #endif