diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index caef157e..b01e3bde 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -11,7 +11,7 @@ add_library( graphene_app ${EGENESIS_HEADERS} ) -target_link_libraries( graphene_app graphene_market_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities ) +target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 35cf3880..20b9a8c8 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -380,25 +380,58 @@ namespace graphene { namespace app { return result; } - vector history_api::get_account_history(account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start) const + vector history_api::get_account_history( account_id_type account, + operation_history_id_type stop, + unsigned limit, + operation_history_id_type start ) const { - FC_ASSERT(_app.chain_database()); + FC_ASSERT( _app.chain_database() ); + const auto& db = *_app.chain_database(); + FC_ASSERT( limit <= 100 ); + vector result; + const auto& stats = account(db).statistics(db); + if( stats.most_recent_op == account_transaction_history_id_type() ) return result; + const account_transaction_history_object* node = &stats.most_recent_op(db); + if( start == operation_history_id_type() ) + start = node->operation_id; + + while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit) + { + if( node->operation_id.instance.value <= start.instance.value ) + result.push_back( node->operation_id(db) ); + if( node->next == account_transaction_history_id_type() ) + node = nullptr; + else node = &node->next(db); + } + + return result; + } + + vector history_api::get_relative_account_history( account_id_type account, + uint32_t stop, + unsigned limit, + uint32_t start) const + { + FC_ASSERT( _app.chain_database() ); const auto& db = *_app.chain_database(); FC_ASSERT(limit <= 100); vector result; - const auto& stats = account(db).statistics(db); - if(stats.most_recent_op == account_transaction_history_id_type()) return result; - const account_transaction_history_object* node = &stats.most_recent_op(db); - if(start == operation_history_id_type()) - start = node->id; - while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit) + if( start == 0 ) + start = account(db).statistics(db).total_ops; + else start = min( account(db).statistics(db).total_ops, start ); + const auto& hist_idx = db.get_index_type(); + const auto& by_seq_idx = hist_idx.indices().get(); + + auto itr = by_seq_idx.upper_bound( boost::make_tuple( account, start ) ); + auto itr_stop = by_seq_idx.lower_bound( boost::make_tuple( account, stop ) ); + --itr; + + while ( itr != itr_stop && result.size() < limit ) { - if(node->id.instance() <= start.instance.value) - result.push_back(node->operation_id(db)); - if(node->next == account_transaction_history_id_type()) - node = nullptr; - else node = db.find(node->next); + result.push_back( itr->operation_id(db) ); + --itr; } + return result; } diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 0e57ef7f..734d68b2 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -31,6 +31,8 @@ #include #include +#include +#include #include @@ -1025,56 +1027,37 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q try { if( base_id > quote_id ) std::swap(base_id, quote_id); - const auto& bidx = _db.get_index_type(); - const auto& by_key_idx = bidx.indices().get(); - uint32_t bucket_size = 86400; + uint32_t day = 86400; auto now = fc::time_point_sec( fc::time_point::now() ); - - auto itr = by_key_idx.lower_bound( bucket_key( base_id, quote_id, bucket_size, - now - bucket_size ) ); - auto orders = get_order_book( base, quote, 1 ); + auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - day ), 100 ); - if( itr != by_key_idx.end() && itr->key.base == base_id && itr->key.quote == quote_id && itr->key.seconds == bucket_size ) + result.latest = trades[0].price; + + for ( market_trade t: trades ) { - auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 ); + result.base_volume += t.value; + result.quote_volume += t.amount; + } - if (assets[0]->id == base_id) - { - result.latest = trades[0].price; - result.percent_change = ( result.latest / ( price_to_real( itr->open_quote, assets[1]->precision ) / price_to_real( itr->open_base, assets[0]->precision ) ) - 1 ) * 100; - //result.lowest_ask = price_to_real( itr->low_quote, assets[1]->precision ) / price_to_real( itr->low_base, assets[0]->precision ); - //result.highest_bid = price_to_real( itr->high_quote, assets[1]->precision ) / price_to_real( itr->high_base, assets[0]->precision ); - result.lowest_ask = orders.asks[0].first; - result.highest_bid = orders.bids[0].first; - } - else - { - result.latest = trades[0].price; - result.percent_change = ( result.latest / ( price_to_real( itr->open_base, assets[1]->precision ) / price_to_real( itr->open_quote, assets[0]->precision ) ) - 1) * 100; - //result.lowest_ask = price_to_real( itr->low_base, assets[1]->precision ) / price_to_real( itr->low_quote, assets[0]->precision ); - //result.highest_bid = price_to_real( itr->high_base, assets[1]->precision ) / price_to_real( itr->high_quote, assets[0]->precision ); - result.lowest_ask = orders.bids[0].first; - result.highest_bid = orders.asks[0].first; - } + while (trades.size() == 100) + { + trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - day ), 100 ); for ( market_trade t: trades ) { - result.base_volume += t.amount; - result.quote_volume += t.value; + result.base_volume += t.value; + result.quote_volume += t.amount; } + } - while (trades.size() == 100) - { - for ( market_trade t: trades ) - { - result.base_volume += t.amount; - result.quote_volume += t.value; - } - - trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 ); - } + trades = get_trade_history( base, quote, trades.back().date, fc::time_point_sec(), 1 ); + result.percent_change = trades.size() > 0 ? ( ( result.latest / trades.back().price ) - 1 ) * 100 : 0; + //if (assets[0]->id == base_id) + { + result.lowest_ask = orders.asks[0].price; + result.highest_bid = orders.bids[0].price; } return result; @@ -1111,19 +1094,19 @@ market_volume database_api_impl::get_24_volume( const string& base, const string for ( market_trade t: trades ) { - result.base_volume += t.amount; - result.quote_volume += t.value; + result.base_volume += t.value; + result.quote_volume += t.amount; } while (trades.size() == 100) { + trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 ); + for ( market_trade t: trades ) { - result.base_volume += t.amount; - result.quote_volume += t.value; + result.base_volume += t.value; + result.quote_volume += t.amount; } - - trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 ); } return result; @@ -1137,6 +1120,7 @@ order_book database_api::get_order_book( const string& base, const string& quote order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const { + using boost::multiprecision::uint128_t; FC_ASSERT( limit <= 50 ); order_book result; @@ -1156,21 +1140,28 @@ order_book database_api_impl::get_order_book( const string& base, const string& auto price_to_real = [&]( const price& p ) { if( p.base.asset_id == base_id ) - return asset_to_real( p.quote, assets[1]->precision ) / asset_to_real( p.base, assets[0]->precision ); + return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision ); else - return asset_to_real( p.base, assets[1]->precision ) / asset_to_real( p.quote, assets[0]->precision ); + return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision ); }; - for( const auto& o : orders ) { + for( const auto& o : orders ) + { if( o.sell_price.base.asset_id == base_id ) { - result.asks.push_back( std::make_pair( price_to_real(o.sell_price), - asset_to_real(o.sell_price.base, assets[0]->precision)) ); + order ord; + ord.price = price_to_real( o.sell_price ); + ord.quote = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[1]->precision ); + ord.base = asset_to_real( o.for_sale, assets[0]->precision ); + result.bids.push_back( ord ); } else { - result.bids.push_back( std::make_pair( price_to_real(o.sell_price), - asset_to_real(o.sell_price.quote, assets[0]->precision ) ) ); + order ord; + ord.price = price_to_real( o.sell_price ); + ord.quote = asset_to_real( o.for_sale, assets[1]->precision ); + ord.base = asset_to_real( share_type( ( uint64_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[0]->precision ); + result.asks.push_back( ord ); } } @@ -1225,13 +1216,13 @@ vector database_api_impl::get_trade_history( const string& base, if( assets[0]->id == itr->op.receives.asset_id ) { - trade.amount = price_to_real( itr->op.receives.amount, assets[0]->precision ); - trade.value = price_to_real( itr->op.pays.amount, assets[1]->precision ); + trade.amount = price_to_real( itr->op.pays.amount, assets[1]->precision ); + trade.value = price_to_real( itr->op.receives.amount, assets[0]->precision ); } else { - trade.amount = price_to_real( itr->op.pays.amount, assets[0]->precision ); - trade.value = price_to_real( itr->op.receives.amount, assets[1]->precision ); + trade.amount = price_to_real( itr->op.receives.amount, assets[1]->precision ); + trade.value = price_to_real( itr->op.pays.amount, assets[0]->precision ); } trade.date = itr->time; diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 59c905d4..f2d5a235 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -91,6 +91,22 @@ namespace graphene { namespace app { operation_history_id_type stop = operation_history_id_type(), unsigned limit = 100, operation_history_id_type start = operation_history_id_type())const; + /** + * @breif Get operations relevant to the specified account referenced + * by an event numbering specific to the account. The current number of operations + * for the account can be found in the account statistics (or use 0 for start). + * @param account The account whose history should be queried + * @param stop Sequence number of earliest operation. 0 is default and will + * query 'limit' number of operations. + * @param limit Maximum number of operations to retrieve (must not exceed 100) + * @param start Sequence number of the most recent operation to retrieve. + * 0 is default, which will start querying from the most recent operation. + * @return A list of operations performed by account, ordered from most recent to oldest. + */ + vector get_relative_account_history( account_id_type account, + uint32_t stop = 0, + unsigned limit = 100, + uint32_t start = 0) const; vector get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const; vector get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds, @@ -291,6 +307,7 @@ FC_REFLECT( graphene::app::verify_range_proof_rewind_result, FC_API(graphene::app::history_api, (get_account_history) + (get_relative_account_history) (get_fill_order_history) (get_market_history) (get_market_history_buckets) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 456e0bae..95ce16d6 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -64,40 +64,47 @@ using namespace std; class database_api_impl; +struct order +{ + double price; + double quote; + double base; +}; + struct order_book { - string base; - string quote; - vector< pair > bids; - vector< pair > asks; + string base; + string quote; + vector< order > bids; + vector< order > asks; }; struct market_ticker { - string base; - string quote; - double latest; - double lowest_ask; - double highest_bid; - double percent_change; - double base_volume; - double quote_volume; + string base; + string quote; + double latest; + double lowest_ask; + double highest_bid; + double percent_change; + double base_volume; + double quote_volume; }; struct market_volume { - string base; - string quote; - double base_volume; - double quote_volume; + string base; + string quote; + double base_volume; + double quote_volume; }; struct market_trade { - fc::time_point_sec date; - double price; - double amount; - double value; + fc::time_point_sec date; + double price; + double amount; + double value; }; /** @@ -116,7 +123,7 @@ class database_api ///////////// // Objects // ///////////// - + /** * @brief Get the objects corresponding to the provided IDs * @param ids IDs of the objects to retrieve @@ -202,7 +209,7 @@ class database_api ////////// // Keys // ////////// - + vector> get_key_references( vector key )const; ////////////// @@ -465,9 +472,9 @@ class database_api */ map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; - - /// WORKERS - + + /// WORKERS + /** * Return the worker objects associated with this account. */ @@ -515,7 +522,7 @@ class database_api bool verify_authority( const signed_transaction& trx )const; /** - * @return true if the signers have enough authority to authorize an account + * @return true if the signers have enough authority to authorize an account */ bool verify_account_authority( const string& name_or_id, const flat_set& signers )const; @@ -553,6 +560,8 @@ class database_api }; } } + +FC_REFLECT( graphene::app::order, (price)(quote)(base) ); FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) ); FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) ); diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 1f29bb0a..b9b27716 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -54,6 +55,43 @@ void verify_authority_accounts( const database& db, const authority& a ) } } +void verify_account_votes( const database& db, const account_options& options ) +{ + // ensure account's votes satisfy requirements + // NB only the part of vote checking that requires chain state is here, + // the rest occurs in account_options::validate() + + const auto& gpo = db.get_global_properties(); + const auto& chain_params = gpo.parameters; + + FC_ASSERT( options.num_witness <= chain_params.maximum_witness_count, + "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) ); + FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, + "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); + + uint32_t max_vote_id = gpo.next_available_vote_id; + bool has_worker_votes = false; + for( auto id : options.votes ) + { + FC_ASSERT( id < max_vote_id ); + has_worker_votes |= (id.type() == vote_id_type::worker); + } + + if( has_worker_votes && (db.head_block_time() >= HARDFORK_607_TIME) ) + { + const auto& against_worker_idx = db.get_index_type().indices().get(); + for( auto id : options.votes ) + { + if( id.type() == vote_id_type::worker ) + { + FC_ASSERT( against_worker_idx.find( id ) == against_worker_idx.end() ); + } + } + } + +} + + void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) { try { database& d = db(); @@ -62,14 +100,18 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio FC_ASSERT( !op.extensions.value.owner_special_authority.valid() ); FC_ASSERT( !op.extensions.value.active_special_authority.valid() ); } + if( d.head_block_time() < HARDFORK_599_TIME ) + { + FC_ASSERT( !op.extensions.value.null_ext.valid() ); + FC_ASSERT( !op.extensions.value.owner_special_authority.valid() ); + FC_ASSERT( !op.extensions.value.active_special_authority.valid() ); + FC_ASSERT( !op.extensions.value.buyback_options.valid() ); + } FC_ASSERT( d.find_object(op.options.voting_account), "Invalid proxy account specified." ); FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." ); FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." ); - const auto& global_props = d.get_global_properties(); - const auto& chain_params = global_props.parameters; - try { verify_authority_accounts( d, op.owner ); @@ -84,27 +126,7 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio evaluate_special_authority( d, *op.extensions.value.active_special_authority ); if( op.extensions.value.buyback_options.valid() ) evaluate_buyback_account_options( d, *op.extensions.value.buyback_options ); - - uint32_t max_vote_id = global_props.next_available_vote_id; - - FC_ASSERT( op.options.num_witness <= chain_params.maximum_witness_count, - "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) ); - - FC_ASSERT( op.options.num_committee <= chain_params.maximum_committee_count, - "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); - - safe counts[vote_id_type::VOTE_TYPE_COUNT]; - for( auto id : op.options.votes ) - { - FC_ASSERT( id < max_vote_id ); - counts[id.type()]++; - } - FC_ASSERT(counts[vote_id_type::witness] <= op.options.num_witness, - "", - ("count", counts[vote_id_type::witness])("num", op.options.num_witness)); - FC_ASSERT(counts[vote_id_type::committee] <= op.options.num_committee, - "", - ("count", counts[vote_id_type::committee])("num", op.options.num_committee)); + verify_account_votes( d, op.options ); auto& acnt_indx = d.get_index_type(); if( op.name.size() ) @@ -223,8 +245,12 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio FC_ASSERT( !o.extensions.value.owner_special_authority.valid() ); FC_ASSERT( !o.extensions.value.active_special_authority.valid() ); } - - const auto& chain_params = d.get_global_properties().parameters; + if( d.head_block_time() < HARDFORK_599_TIME ) + { + FC_ASSERT( !o.extensions.value.null_ext.valid() ); + FC_ASSERT( !o.extensions.value.owner_special_authority.valid() ); + FC_ASSERT( !o.extensions.value.active_special_authority.valid() ); + } try { @@ -241,16 +267,8 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio acnt = &o.account(d); - if( o.new_options ) - { - FC_ASSERT( o.new_options->num_witness <= chain_params.maximum_witness_count ); - FC_ASSERT( o.new_options->num_committee <= chain_params.maximum_committee_count ); - uint32_t max_vote_id = d.get_global_properties().next_available_vote_id; - for( auto id : o.new_options->votes ) - { - FC_ASSERT( id < max_vote_id ); - } - } + if( o.new_options.valid() ) + verify_account_votes( d, *o.new_options ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } @@ -371,11 +389,13 @@ void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator: a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage; } else if( a.is_annual_member(d.head_block_time()) ) { // Renew an annual subscription that's still in effect. + FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME ); FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650), "May not extend annual membership more than a decade into the future."); a.membership_expiration_date += fc::days(365); } else { // Upgrade from basic account. + FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME ); a.statistics(d).process_fees(a, d); assert(a.is_basic_account(d.head_block_time())); a.referrer = a.get_id(); diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index e141cd08..2e511591 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -36,6 +36,8 @@ #include #include +#include + namespace graphene { namespace chain { bool database::is_known_block( const block_id_type& id )const diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index c7e39d75..d515a961 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -102,11 +102,12 @@ void database::update_worker_votes() { auto& idx = get_index_type(); auto itr = idx.indices().get().begin(); + bool allow_negative_votes = (head_block_time() < HARDFORK_607_TIME); while( itr != idx.indices().get().end() ) { modify( *itr, [&]( worker_object& obj ){ obj.total_votes_for = _vote_tally_buffer[obj.vote_for]; - obj.total_votes_against = _vote_tally_buffer[obj.vote_against]; + obj.total_votes_against = allow_negative_votes ? _vote_tally_buffer[obj.vote_against] : 0; }); ++itr; } @@ -684,6 +685,37 @@ void create_buyback_orders( database& db ) return; } +void deprecate_annual_members( database& db ) +{ + const auto& account_idx = db.get_index_type().indices().get(); + fc::time_point_sec now = db.head_block_time(); + for( const account_object& acct : account_idx ) + { + try + { + transaction_evaluation_state upgrade_context(&db); + upgrade_context.skip_fee_schedule_check = true; + + if( acct.is_annual_member( now ) ) + { + account_upgrade_operation upgrade_vop; + upgrade_vop.fee = asset( 0, asset_id_type() ); + upgrade_vop.account_to_upgrade = acct.id; + upgrade_vop.upgrade_to_lifetime_member = true; + db.apply_operation( upgrade_context, upgrade_vop ); + } + } + catch( const fc::exception& e ) + { + // we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account + wlog( "Skipping annual member deprecate processing for account ${a} (${an}) at block ${n}; exception was ${e}", + ("a", acct.id)("an", acct.name)("n", db.head_block_num())("e", e.to_detail_string()) ); + continue; + } + } + return; +} + void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) { const auto& gpo = get_global_properties(); @@ -830,7 +862,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g } } - modify(get_dynamic_global_properties(), [next_maintenance_time](dynamic_global_property_object& d) { + const dynamic_global_property_object& dgpo = get_dynamic_global_properties(); + + if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) ) + deprecate_annual_members(*this); + + modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) { d.next_maintenance_time = next_maintenance_time; d.accounts_registered_this_interval = 0; }); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index da96eef9..2219136e 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -434,7 +435,12 @@ void database::update_expired_feeds() assert( a.is_market_issued() ); const asset_bitasset_data_object& b = a.bitasset_data(*this); - if( b.feed_is_expired(head_block_time()) ) + bool feed_is_expired; + if( head_block_time() < HARDFORK_615_TIME ) + feed_is_expired = b.feed_is_expired_before_hardfork_615( head_block_time() ); + else + feed_is_expired = b.feed_is_expired( head_block_time() ); + if( feed_is_expired ) { modify(b, [this](asset_bitasset_data_object& a) { a.update_median_feeds(head_block_time()); diff --git a/libraries/chain/hardfork.d/599.hf b/libraries/chain/hardfork.d/599.hf new file mode 100644 index 00000000..71f7e94e --- /dev/null +++ b/libraries/chain/hardfork.d/599.hf @@ -0,0 +1,4 @@ +// #599 Unpacking of extension is incorrect +#ifndef HARDFORK_599_TIME +#define HARDFORK_599_TIME (fc::time_point_sec( 1458061200 )) +#endif diff --git a/libraries/chain/hardfork.d/607.hf b/libraries/chain/hardfork.d/607.hf new file mode 100644 index 00000000..135619c2 --- /dev/null +++ b/libraries/chain/hardfork.d/607.hf @@ -0,0 +1,4 @@ +// #607 Disable negative voting on workers +#ifndef HARDFORK_607_TIME +#define HARDFORK_607_TIME (fc::time_point_sec( 1458061200 )) +#endif diff --git a/libraries/chain/hardfork.d/613.hf b/libraries/chain/hardfork.d/613.hf new file mode 100644 index 00000000..74c740fb --- /dev/null +++ b/libraries/chain/hardfork.d/613.hf @@ -0,0 +1,4 @@ +// #613 Deprecate annual membership +#ifndef HARDFORK_613_TIME +#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 )) +#endif diff --git a/libraries/chain/hardfork.d/615.hf b/libraries/chain/hardfork.d/615.hf new file mode 100644 index 00000000..a5599bbc --- /dev/null +++ b/libraries/chain/hardfork.d/615.hf @@ -0,0 +1,4 @@ +// #615 Fix price feed expiration check, so websocket server will never spam too much data +#ifndef HARDFORK_615_TIME +#define HARDFORK_615_TIME (fc::time_point_sec( 1457550000 )) +#endif diff --git a/libraries/chain/include/graphene/chain/account_object.hpp b/libraries/chain/include/graphene/chain/account_object.hpp index 789dac2d..faf59e22 100644 --- a/libraries/chain/include/graphene/chain/account_object.hpp +++ b/libraries/chain/include/graphene/chain/account_object.hpp @@ -50,6 +50,7 @@ namespace graphene { namespace chain { * Keep the most recent operation as a root pointer to a linked list of the transaction history. */ account_transaction_history_id_type most_recent_op; + uint32_t total_ops = 0; /** * When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for @@ -385,6 +386,7 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain::object), (owner) (most_recent_op) + (total_ops) (total_core_in_orders) (lifetime_fees_paid) (pending_fees)(pending_vested_fees) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 3bde61b3..28d5974e 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -211,6 +211,8 @@ namespace graphene { namespace chain { time_point_sec feed_expiration_time()const { return current_feed_publication_time + options.feed_lifetime_sec; } + bool feed_is_expired_before_hardfork_615(time_point_sec current_time)const + { return feed_expiration_time() >= current_time; } bool feed_is_expired(time_point_sec current_time)const { return feed_expiration_time() <= current_time; } void update_median_feeds(time_point_sec current_time); diff --git a/libraries/chain/include/graphene/chain/operation_history_object.hpp b/libraries/chain/include/graphene/chain/operation_history_object.hpp index 9781b420..ecbbc58d 100644 --- a/libraries/chain/include/graphene/chain/operation_history_object.hpp +++ b/libraries/chain/include/graphene/chain/operation_history_object.hpp @@ -24,6 +24,7 @@ #pragma once #include #include +#include namespace graphene { namespace chain { @@ -89,13 +90,44 @@ namespace graphene { namespace chain { public: static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_account_transaction_history_object_type; + account_id_type account; /// the account this operation applies to operation_history_id_type operation_id; + uint32_t sequence = 0; /// the operation position within the given account account_transaction_history_id_type next; + + //std::pair account_op()const { return std::tie( account, operation_id ); } + //std::pair account_seq()const { return std::tie( account, sequence ); } }; + + struct by_id; +struct by_seq; +struct by_op; +typedef multi_index_container< + account_transaction_history_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, + composite_key< account_transaction_history_object, + member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>, + member< account_transaction_history_object, uint32_t, &account_transaction_history_object::sequence> + > + >, + ordered_unique< tag, + composite_key< account_transaction_history_object, + member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>, + member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id> + > + > + > +> account_transaction_history_multi_index_type; + +typedef generic_index account_transaction_history_index; + + } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::operation_history_object, (graphene::chain::object), (op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) ) FC_REFLECT_DERIVED( graphene::chain::account_transaction_history_object, (graphene::chain::object), - (operation_id)(next) ) + (account)(operation_id)(sequence)(next) ) diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index c62b52f3..bdd1fb23 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -119,10 +119,13 @@ void account_history_plugin_impl::update_account_histories( const signed_block& const auto& stats_obj = account_id(db).statistics(db); const auto& ath = db.create( [&]( account_transaction_history_object& obj ){ obj.operation_id = oho.id; + obj.account = account_id; + obj.sequence = stats_obj.total_ops+1; obj.next = stats_obj.most_recent_op; }); db.modify( stats_obj, [&]( account_statistics_object& obj ){ obj.most_recent_op = ath.id; + obj.total_ops = ath.sequence; }); } } @@ -148,6 +151,11 @@ void account_history_plugin_impl::update_account_histories( const signed_block& } } // end namespace detail + + + + + account_history_plugin::account_history_plugin() : my( new detail::account_history_plugin_impl(*this) ) { @@ -177,7 +185,7 @@ void account_history_plugin::plugin_initialize(const boost::program_options::var { database().applied_block.connect( [&]( const signed_block& b){ my->update_account_histories(b); } ); database().add_index< primary_index< simple_index< operation_history_object > > >(); - database().add_index< primary_index< simple_index< account_transaction_history_object > > >(); + database().add_index< primary_index< account_transaction_history_index > >(); LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type); } diff --git a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp index 6acd0f45..ef89c488 100644 --- a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp +++ b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp @@ -26,10 +26,15 @@ #include #include +#include + #include namespace graphene { namespace account_history { -using namespace chain; + using namespace chain; + //using namespace graphene::db; + //using boost::multi_index_container; + //using namespace boost::multi_index; // // Plugins should #define their SPACE_ID's so plugins with @@ -78,3 +83,27 @@ class account_history_plugin : public graphene::app::plugin } } //graphene::account_history +/*struct by_id; +struct by_seq; +struct by_op; +typedef boost::multi_index_container< + graphene::chain::account_transaction_history_object, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< tag, member< object, object_id_type, &object::id > >, + boost::multi_index::ordered_unique< tag, + composite_key< account_transaction_history_object, + member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>, + member< account_transaction_history_object, uint32_t, &account_transaction_history_object::sequence> + > + >, + boost::multi_index::ordered_unique< tag, + composite_key< account_transaction_history_object, + member< account_transaction_history_object, account_id_type, &account_transaction_history_object::account>, + member< account_transaction_history_object, operation_history_id_type, &account_transaction_history_object::operation_id> + > + > + > +> account_transaction_history_multi_index_type; + +typedef graphene::account_history::generic_index account_transaction_history_index; +*/ diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index cdb9036e..fcf59a2e 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -849,6 +849,51 @@ class wallet_api uint32_t timeout_sec = 0, bool fill_or_kill = false, bool broadcast = false); + + /** Place a limit order attempting to sell one asset for another. + * + * This API call abstracts away some of the details of the sell_asset call to be more + * user friendly. All orders placed with sell never timeout and will not be killed if they + * cannot be filled immediately. If you wish for one of these parameters to be different, + * then sell_asset should be used instead. + * + * @param seller_account the account providing the asset being sold, and which will + * receive the processed of the sale. + * @param base The name or id of the asset to sell. + * @param quote The name or id of the asset to recieve. + * @param rate The rate in base:quote at which you want to sell. + * @param amount The amount of base you want to sell. + * @param broadcast true to broadcast the transaction on the network. + * @returns The signed transaction selling the funds. + */ + signed_transaction sell( string seller_account, + string base, + string quote, + double rate, + double amount, + bool broadcast ); + + /** Place a limit order attempting to buy one asset with another. + * + * This API call abstracts away some of the details of the sell_asset call to be more + * user friendly. All orders placed with buy never timeout and will not be killed if they + * cannot be filled immediately. If you wish for one of these parameters to be different, + * then sell_asset should be used instead. + * + * @param buyer_account The account buying the asset for another asset. + * @param base The name or id of the asset to buy. + * @param quote The name or id of the assest being offered as payment. + * @param rate The rate in base:quote at which you want to buy. + * @param amount the amount of base you want to buy. + * @param broadcast true to broadcast the transaction on the network. + * @param The signed transaction selling the funds. + */ + signed_transaction buy( string buyer_account, + string base, + string quote, + double rate, + double amount, + bool broadcast ); /** Borrow an asset or update the debt/collateral ratio for the loan. * @@ -1401,6 +1446,8 @@ class wallet_api const approval_delta& delta, bool broadcast /* = false */ ); + + order_book get_order_book( const string& base, const string& quote, unsigned limit = 50); void dbg_make_uia(string creator, string symbol); void dbg_make_mia(string creator, string symbol); @@ -1517,6 +1564,8 @@ FC_API( graphene::wallet::wallet_api, (upgrade_account) (create_account_with_brain_key) (sell_asset) + (sell) + (buy) (borrow_asset) (cancel_order) (transfer) @@ -1589,4 +1638,5 @@ FC_API( graphene::wallet::wallet_api, (blind_transfer) (blind_history) (receive_blind_transfer) + (get_order_book) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 3dd8b9e3..59a50829 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -280,7 +280,7 @@ private: auto iter = _wallet.pending_witness_registrations.find(witness_name); FC_ASSERT(iter != _wallet.pending_witness_registrations.end()); std::string wif_key = iter->second; - + // get the list key id this key is registered with in the chain fc::optional witness_private_key = wif_to_key(wif_key); FC_ASSERT(witness_private_key); @@ -316,12 +316,12 @@ private: if( optional_account ) claim_registered_account(*optional_account); } - + if (!_wallet.pending_witness_registrations.empty()) { // make a vector of the owner accounts for witnesses pending registration std::vector pending_witness_names = boost::copy_range >(boost::adaptors::keys(_wallet.pending_witness_registrations)); - + // look up the owners on the blockchain std::vector> owner_account_objects = _remote_db->lookup_account_names(pending_witness_names); @@ -365,9 +365,9 @@ private: // if the user executes the same command twice in quick succession, // we might generate the same transaction id, and cause the second - // transaction to be rejected. This can be avoided by altering the + // transaction to be rejected. This can be avoided by altering the // second transaction slightly (bumping up the expiration time by - // a second). Keep track of recent transaction ids we've generated + // a second). Keep track of recent transaction ids we've generated // so we can know if we need to do this struct recently_generated_transaction_record { @@ -377,7 +377,7 @@ private: struct timestamp_index{}; typedef boost::multi_index_container, std::hash >, boost::multi_index::ordered_non_unique, @@ -686,7 +686,7 @@ public: if (!optional_private_key) FC_THROW("Invalid private key"); graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key(); - + account_object account = get_account( account_name_or_id ); // make a list of all current public keys for the named account @@ -1042,7 +1042,7 @@ public: { try { int active_key_index = find_first_unused_derived_key_index(owner_privkey); fc::ecc::private_key active_privkey = derive_private_key( key_to_wif(owner_privkey), active_key_index); - + int memo_key_index = find_first_unused_derived_key_index(active_privkey); fc::ecc::private_key memo_privkey = derive_private_key( key_to_wif(active_privkey), memo_key_index); @@ -1350,7 +1350,7 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (authorizing_account)(account_to_list)(new_listing_status)(broadcast) ) } - signed_transaction create_committee_member(string owner_account, string url, + signed_transaction create_committee_member(string owner_account, string url, bool broadcast /* = false */) { try { @@ -1370,7 +1370,7 @@ public: witness_object get_witness(string owner_account) { - try + try { fc::optional witness_id = maybe_id(owner_account); if (witness_id) @@ -1405,7 +1405,7 @@ public: committee_member_object get_committee_member(string owner_account) { - try + try { fc::optional committee_member_id = maybe_id(owner_account); if (committee_member_id) @@ -1460,7 +1460,7 @@ public: tx.operations.push_back( witness_create_op ); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); - + _wallet.pending_witness_registrations[owner_account] = key_to_wif(witness_private_key); return sign_transaction( tx, broadcast ); @@ -1774,7 +1774,7 @@ public: if (account_object_to_modify.options.num_witness == desired_number_of_witnesses && account_object_to_modify.options.num_committee == desired_number_of_committee_members) - FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members", + FC_THROW("Account ${account} is already voting for ${witnesses} witnesses and ${committee_members} committee_members", ("account", account_to_modify)("witnesses", desired_number_of_witnesses)("committee_members",desired_number_of_witnesses)); account_object_to_modify.options.num_witness = desired_number_of_witnesses; account_object_to_modify.options.num_committee = desired_number_of_committee_members; @@ -2161,11 +2161,95 @@ public: for( auto& r : records ) { asset_object as = get_asset( r.amount.asset_id ); - ss << fc::get_approximate_relative_time_string( r.date ) + ss << fc::get_approximate_relative_time_string( r.date ) << " " << as.amount_to_pretty_string( r.amount ) << " " << r.from_label << " => " << r.to_label << " " << r.memo <<"\n"; } return ss.str(); }; + m["get_order_book"] = [this](variant result, const fc::variants& a) + { + auto orders = result.as(); + auto bids = orders.bids; + auto asks = orders.asks; + std::stringstream ss; + std::stringstream sum_stream; + sum_stream << "Sum(" << orders.base << ')'; + double bid_sum = 0; + double ask_sum = 0; + const int spacing = 20; + + auto prettify_num = [&]( double n ) + { + //ss << n; + if (abs( round( n ) - n ) < 0.00000000001 ) + { + //ss << setiosflags( !ios::fixed ) << (int) n; // doesn't compile on Linux with gcc + ss << (int) n; + } + else if (n - floor(n) < 0.000001) + { + ss << setiosflags( ios::fixed ) << setprecision(10) << n; + } + else + { + ss << setiosflags( ios::fixed ) << setprecision(6) << n; + } + }; + + ss << setprecision( 8 ) << setiosflags( ios::fixed ) << setiosflags( ios::left ); + + ss << ' ' << setw( (spacing * 4) + 6 ) << "BUY ORDERS" << "SELL ORDERS\n" + << ' ' << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing ) + << orders.base << ' ' << setw( spacing ) << sum_stream.str() + << " " << setw( spacing + 1 ) << "Price" << setw( spacing ) << orders.quote << ' ' << setw( spacing ) + << orders.base << ' ' << setw( spacing ) << sum_stream.str() + << "\n=====================================================================================" + << "|=====================================================================================\n"; + + for (int i = 0; i < bids.size() || i < asks.size() ; i++) + { + if ( i < bids.size() ) + { + bid_sum += bids[i].base; + ss << ' ' << setw( spacing ); + prettify_num( bids[i].price ); + ss << ' ' << setw( spacing ); + prettify_num( bids[i].quote ); + ss << ' ' << setw( spacing ); + prettify_num( bids[i].base ); + ss << ' ' << setw( spacing ); + prettify_num( bid_sum ); + ss << ' '; + } + else + { + ss << setw( (spacing * 4) + 5 ) << ' '; + } + + ss << '|'; + + if ( i < asks.size() ) + { + ask_sum += asks[i].base; + ss << ' ' << setw( spacing ); + prettify_num( asks[i].price ); + ss << ' ' << setw( spacing ); + prettify_num( asks[i].quote ); + ss << ' ' << setw( spacing ); + prettify_num( asks[i].base ); + ss << ' ' << setw( spacing ); + prettify_num( ask_sum ); + } + + ss << '\n'; + } + + ss << endl + << "Buy Total: " << bid_sum << ' ' << orders.base << endl + << "Sell Total: " << ask_sum << ' ' << orders.base << endl; + + return ss.str(); + }; return m; } @@ -2392,7 +2476,7 @@ public: dbg_make_uia(master.name, "SHILL"); } catch(...) {/* Ignore; the asset probably already exists.*/} - fc::time_point start = fc::time_point::now(); + fc::time_point start = fc::time_point::now(); for( int i = 0; i < number_of_accounts; ++i ) { std::ostringstream brain_key; @@ -2474,7 +2558,7 @@ std::string operation_printer::operator()(const T& op)const //op.get_balance_delta( acc, result ); auto a = wallet.get_asset( op.fee.asset_id ); auto payer = wallet.get_account( op.fee_payer() ); - + string op_name = fc::get_typename::name(); if( op_name.find_last_of(':') != string::npos ) op_name.erase(0, op_name.find_last_of(':')+1); @@ -2494,7 +2578,7 @@ std::string operation_printer::operator()(const transfer_from_blind_operation& o auto a = wallet.get_asset( op.fee.asset_id ); auto receiver = wallet.get_account( op.to ); - out << receiver.name + out << receiver.name << " received " << a.amount_to_pretty_string( op.amount ) << " from blinded balance"; return ""; } @@ -2504,7 +2588,7 @@ std::string operation_printer::operator()(const transfer_to_blind_operation& op) auto a = wallet.get_asset( op.amount.asset_id ); auto sender = wallet.get_account( op.from ); - out << sender.name + out << sender.name << " sent " << a.amount_to_pretty_string( op.amount ) << " to " << op.outputs.size() << " blinded balance" << (op.outputs.size()>1?"s":"") << " fee: " << fa.amount_to_pretty_string( op.fee ); return ""; @@ -2641,7 +2725,7 @@ vector wallet_api::get_account_history(string name, int limit) while( limit > 0 ) { operation_history_id_type start; - if( result.size() ) + if( result.size() ) { start = result.back().op.id; start = start + 1; @@ -3077,7 +3161,7 @@ signed_transaction wallet_api::whitelist_account(string authorizing_account, return my->whitelist_account(authorizing_account, account_to_list, new_listing_status, broadcast); } -signed_transaction wallet_api::create_committee_member(string owner_account, string url, +signed_transaction wallet_api::create_committee_member(string owner_account, string url, bool broadcast /* = false */) { return my->create_committee_member(owner_account, url, broadcast); @@ -3183,7 +3267,7 @@ signed_transaction wallet_api::set_desired_witness_and_committee_member_count(st uint16_t desired_number_of_committee_members, bool broadcast /* = false */) { - return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses, + return my->set_desired_witness_and_committee_member_count(account_to_modify, desired_number_of_witnesses, desired_number_of_committee_members, broadcast); } @@ -3522,7 +3606,7 @@ vector< signed_transaction > wallet_api_impl::import_balance( string name_or_id, if( broadcast ) _remote_net_broadcast->broadcast_transaction(signed_tx); } - + return result; } FC_CAPTURE_AND_RETHROW( (name_or_id) ) } @@ -3552,6 +3636,28 @@ signed_transaction wallet_api::sell_asset(string seller_account, symbol_to_receive, expiration, fill_or_kill, broadcast); } +signed_transaction wallet_api::sell( string seller_account, + string base, + string quote, + double rate, + double amount, + bool broadcast ) +{ + return my->sell_asset( seller_account, std::to_string( amount ), base, + std::to_string( rate * amount ), quote, 0, false, broadcast ); +} + +signed_transaction wallet_api::buy( string buyer_account, + string base, + string quote, + double rate, + double amount, + bool broadcast ) +{ + return my->sell_asset( buyer_account, std::to_string( rate * amount ), quote, + std::to_string( amount ), base, 0, false, broadcast ); +} + signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, string amount_of_collateral, bool broadcast) { @@ -3675,7 +3781,7 @@ vector wallet_api::get_blind_balances( string key_or_label ) } blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_key_or_label, - string to_account_id_or_name, + string to_account_id_or_name, string amount_in, string symbol, bool broadcast ) @@ -3694,7 +3800,7 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke auto conf = blind_transfer_help( from_blind_account_key_or_label, - from_blind_account_key_or_label, + from_blind_account_key_or_label, blind_in, symbol, false, true/*to_temp*/ ); FC_ASSERT( conf.outputs.size() > 0 ); @@ -3709,24 +3815,24 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke conf.trx.operations.push_back(from_blind); ilog( "about to validate" ); conf.trx.validate(); - + if( broadcast && conf.outputs.size() == 2 ) { - + // Save the change blind_confirmation::output conf_output; blind_confirmation::output change_output = conf.outputs[0]; - + // The wallet must have a private key for confirmation.to, this is used to decrypt the memo public_key_type from_key = get_public_key(from_blind_account_key_or_label); conf_output.confirmation.to = from_key; conf_output.confirmation.one_time_key = change_output.confirmation.one_time_key; conf_output.confirmation.encrypted_memo = change_output.confirmation.encrypted_memo; conf_output.confirmation_receipt = conf_output.confirmation; - //try { + //try { receive_blind_transfer( conf_output.confirmation_receipt, from_blind_account_key_or_label, "@"+to_account.name ); //} catch ( ... ){} } - + ilog( "about to broadcast" ); conf.trx = sign_transaction( conf.trx, broadcast ); @@ -3747,7 +3853,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, string symbol, bool broadcast, bool to_temp ) -{ +{ blind_confirmation confirm; try { @@ -3791,7 +3897,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, blinding_factors.push_back( start->data.blinding_factor ); total_amount += start->amount; - if( total_amount >= amount + blind_tr.fee ) + if( total_amount >= amount + blind_tr.fee ) break; } ++start; @@ -3859,7 +3965,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, conf_output.decrypted_memo.amount = change; conf_output.decrypted_memo.blinding_factor = change_blind_factor; conf_output.decrypted_memo.commitment = change_out.commitment; - conf_output.decrypted_memo.check = from_secret._hash[0]; + conf_output.decrypted_memo.check = from_secret._hash[0]; conf_output.confirmation.one_time_key = one_time_key.get_public_key(); conf_output.confirmation.to = from_key; conf_output.confirmation.encrypted_memo = fc::aes_encrypt( from_secret, fc::raw::pack( conf_output.decrypted_memo ) ); @@ -3877,7 +3983,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, conf_output.decrypted_memo.amount = amount; conf_output.decrypted_memo.blinding_factor = blind_factor; conf_output.decrypted_memo.commitment = to_out.commitment; - conf_output.decrypted_memo.check = secret._hash[0]; + conf_output.decrypted_memo.check = secret._hash[0]; conf_output.confirmation.one_time_key = one_time_key.get_public_key(); conf_output.confirmation.to = to_key; conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); @@ -3887,9 +3993,9 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, confirm.outputs.push_back( conf_output ); /** commitments must be in sorted order */ - std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(), + std::sort( blind_tr.outputs.begin(), blind_tr.outputs.end(), [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); - std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(), + std::sort( blind_tr.inputs.begin(), blind_tr.inputs.end(), [&]( const blind_input& a, const blind_input& b ){ return a.commitment < b.commitment; } ); confirm.trx.operations.emplace_back( std::move(blind_tr) ); @@ -3914,10 +4020,10 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, * Transfers a public balance from @from to one or more blinded balances using a * stealth transfer. */ -blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, +blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, string asset_symbol, /** map from key or label to amount */ - vector> to_amounts, + vector> to_amounts, bool broadcast ) { try { FC_ASSERT( !is_locked() ); @@ -3948,7 +4054,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name auto amount = asset_obj->amount_from_string(item.second); total_amount += amount; - + fc::ecc::public_key to_pub_key = to_key; blind_output out; @@ -3964,7 +4070,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name conf_output.decrypted_memo.amount = amount; conf_output.decrypted_memo.blinding_factor = blind_factor; conf_output.decrypted_memo.commitment = out.commitment; - conf_output.decrypted_memo.check = secret._hash[0]; + conf_output.decrypted_memo.check = secret._hash[0]; conf_output.confirmation.one_time_key = one_time_key.get_public_key(); conf_output.confirmation.to = to_key; conf_output.confirmation.encrypted_memo = fc::aes_encrypt( secret, fc::raw::pack( conf_output.decrypted_memo ) ); @@ -3978,7 +4084,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name bop.blinding_factor = fc::ecc::blind_sum( blinding_factors, blinding_factors.size() ); /** commitments must be in sorted order */ - std::sort( bop.outputs.begin(), bop.outputs.end(), + std::sort( bop.outputs.begin(), bop.outputs.end(), [&]( const blind_output& a, const blind_output& b ){ return a.commitment < b.commitment; } ); confirm.trx.operations.push_back( bop ); @@ -4024,7 +4130,7 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s result.to_key = *conf.to; result.to_label = get_key_label( result.to_key ); - if( memo.from ) + if( memo.from ) { result.from_key = *memo.from; result.from_label = get_key_label( result.from_key ); @@ -4044,7 +4150,7 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s // confirm the amount matches the commitment (verify the blinding factor) auto commtiment_test = fc::ecc::blind( memo.blinding_factor, memo.amount.amount.value ); FC_ASSERT( fc::ecc::verify_sum( {commtiment_test}, {memo.commitment}, 0 ) ); - + blind_balance bal; bal.amount = memo.amount; bal.to = *conf.to; @@ -4062,7 +4168,7 @@ blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, s auto child_key_itr = owner.key_auths.find( child_pubkey ); if( child_key_itr != owner.key_auths.end() ) my->_keys[child_key_itr->first] = key_to_wif( child_priv_key ); - + // my->_wallet.blinded_balances[memo.amount.asset_id][bal.to].push_back( bal ); result.date = fc::time_point::now(); @@ -4079,7 +4185,7 @@ vector wallet_api::blind_history( string key_or_account ) vector result; auto pub_key = get_public_key( key_or_account ); - if( pub_key == public_key_type() ) + if( pub_key == public_key_type() ) return vector(); for( auto& r : my->_wallet.blind_receipts ) @@ -4091,6 +4197,11 @@ vector wallet_api::blind_history( string key_or_account ) return result; } +order_book wallet_api::get_order_book( const string& base, const string& quote, unsigned limit ) +{ + return( my->_remote_db->get_order_book( base, quote, limit ) ); +} + signed_block_with_info::signed_block_with_info( const signed_block& block ) : signed_block( block ) { diff --git a/tests/tests/fee_tests.cpp b/tests/tests/fee_tests.cpp index 984c6fce..d6f26170 100644 --- a/tests/tests/fee_tests.cpp +++ b/tests/tests/fee_tests.cpp @@ -747,6 +747,7 @@ BOOST_AUTO_TEST_CASE( stealth_fba_test ) generate_blocks( HARDFORK_555_TIME ); generate_blocks( HARDFORK_563_TIME ); generate_blocks( HARDFORK_572_TIME ); + generate_blocks( HARDFORK_599_TIME ); // Philbin (registrar who registers Rex) diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index 6d08d686..8cefec4e 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -1338,6 +1338,7 @@ BOOST_AUTO_TEST_CASE( top_n_special ) ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) ); generate_blocks( HARDFORK_516_TIME ); + generate_blocks( HARDFORK_599_TIME ); try { @@ -1490,6 +1491,7 @@ BOOST_AUTO_TEST_CASE( buyback ) generate_blocks( HARDFORK_538_TIME ); generate_blocks( HARDFORK_555_TIME ); + generate_blocks( HARDFORK_599_TIME ); try {