Merge branches '477-history-account-history-indexing', '592-get_ticker', '599-fork-ext-unpack', '607-fork-disable-negative-worker-votes', '613-fork-deprecate-annual' and '615-fork-feed-expiration' into develop
This commit is contained in:
commit
7574aea75a
22 changed files with 550 additions and 182 deletions
|
|
@ -11,7 +11,7 @@ add_library( graphene_app
|
||||||
${EGENESIS_HEADERS}
|
${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
|
target_include_directories( graphene_app
|
||||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||||
|
|
|
||||||
|
|
@ -380,25 +380,58 @@ namespace graphene { namespace app {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<operation_history_object> history_api::get_account_history(account_id_type account, operation_history_id_type stop, unsigned limit, operation_history_id_type start) const
|
vector<operation_history_object> 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<operation_history_object> 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<operation_history_object> 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();
|
const auto& db = *_app.chain_database();
|
||||||
FC_ASSERT(limit <= 100);
|
FC_ASSERT(limit <= 100);
|
||||||
vector<operation_history_object> result;
|
vector<operation_history_object> result;
|
||||||
const auto& stats = account(db).statistics(db);
|
if( start == 0 )
|
||||||
if(stats.most_recent_op == account_transaction_history_id_type()) return result;
|
start = account(db).statistics(db).total_ops;
|
||||||
const account_transaction_history_object* node = &stats.most_recent_op(db);
|
else start = min( account(db).statistics(db).total_ops, start );
|
||||||
if(start == operation_history_id_type())
|
const auto& hist_idx = db.get_index_type<account_transaction_history_index>();
|
||||||
start = node->id;
|
const auto& by_seq_idx = hist_idx.indices().get<by_seq>();
|
||||||
while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit)
|
|
||||||
|
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( itr->operation_id(db) );
|
||||||
result.push_back(node->operation_id(db));
|
--itr;
|
||||||
if(node->next == account_transaction_history_id_type())
|
|
||||||
node = nullptr;
|
|
||||||
else node = db.find(node->next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@
|
||||||
#include <fc/crypto/hex.hpp>
|
#include <fc/crypto/hex.hpp>
|
||||||
|
|
||||||
#include <boost/range/iterator_range.hpp>
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
#include <boost/rational.hpp>
|
||||||
|
#include <boost/multiprecision/cpp_int.hpp>
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
|
|
@ -1025,56 +1027,37 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q
|
||||||
try {
|
try {
|
||||||
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
if( base_id > quote_id ) std::swap(base_id, quote_id);
|
||||||
|
|
||||||
const auto& bidx = _db.get_index_type<bucket_index>();
|
uint32_t day = 86400;
|
||||||
const auto& by_key_idx = bidx.indices().get<by_key>();
|
|
||||||
uint32_t bucket_size = 86400;
|
|
||||||
auto now = fc::time_point_sec( fc::time_point::now() );
|
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 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)
|
while (trades.size() == 100)
|
||||||
{
|
{
|
||||||
result.latest = trades[0].price;
|
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - day ), 100 );
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( market_trade t: trades )
|
for ( market_trade t: trades )
|
||||||
{
|
{
|
||||||
result.base_volume += t.amount;
|
result.base_volume += t.value;
|
||||||
result.quote_volume += t.value;
|
result.quote_volume += t.amount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (trades.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;
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//if (assets[0]->id == base_id)
|
||||||
|
{
|
||||||
|
result.lowest_ask = orders.asks[0].price;
|
||||||
|
result.highest_bid = orders.bids[0].price;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1111,19 +1094,19 @@ market_volume database_api_impl::get_24_volume( const string& base, const string
|
||||||
|
|
||||||
for ( market_trade t: trades )
|
for ( market_trade t: trades )
|
||||||
{
|
{
|
||||||
result.base_volume += t.amount;
|
result.base_volume += t.value;
|
||||||
result.quote_volume += t.value;
|
result.quote_volume += t.amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (trades.size() == 100)
|
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 )
|
for ( market_trade t: trades )
|
||||||
{
|
{
|
||||||
result.base_volume += t.amount;
|
result.base_volume += t.value;
|
||||||
result.quote_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;
|
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
|
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 );
|
FC_ASSERT( limit <= 50 );
|
||||||
|
|
||||||
order_book result;
|
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 )
|
auto price_to_real = [&]( const price& p )
|
||||||
{
|
{
|
||||||
if( p.base.asset_id == base_id )
|
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
|
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 )
|
if( o.sell_price.base.asset_id == base_id )
|
||||||
{
|
{
|
||||||
result.asks.push_back( std::make_pair( price_to_real(o.sell_price),
|
order ord;
|
||||||
asset_to_real(o.sell_price.base, assets[0]->precision)) );
|
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
|
else
|
||||||
{
|
{
|
||||||
result.bids.push_back( std::make_pair( price_to_real(o.sell_price),
|
order ord;
|
||||||
asset_to_real(o.sell_price.quote, assets[0]->precision ) ) );
|
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<market_trade> database_api_impl::get_trade_history( const string& base,
|
||||||
|
|
||||||
if( assets[0]->id == itr->op.receives.asset_id )
|
if( assets[0]->id == itr->op.receives.asset_id )
|
||||||
{
|
{
|
||||||
trade.amount = price_to_real( itr->op.receives.amount, assets[0]->precision );
|
trade.amount = price_to_real( itr->op.pays.amount, assets[1]->precision );
|
||||||
trade.value = price_to_real( itr->op.pays.amount, assets[1]->precision );
|
trade.value = price_to_real( itr->op.receives.amount, assets[0]->precision );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trade.amount = price_to_real( itr->op.pays.amount, assets[0]->precision );
|
trade.amount = price_to_real( itr->op.receives.amount, assets[1]->precision );
|
||||||
trade.value = 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;
|
trade.date = itr->time;
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,22 @@ namespace graphene { namespace app {
|
||||||
operation_history_id_type stop = operation_history_id_type(),
|
operation_history_id_type stop = operation_history_id_type(),
|
||||||
unsigned limit = 100,
|
unsigned limit = 100,
|
||||||
operation_history_id_type start = operation_history_id_type())const;
|
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<operation_history_object> get_relative_account_history( account_id_type account,
|
||||||
|
uint32_t stop = 0,
|
||||||
|
unsigned limit = 100,
|
||||||
|
uint32_t start = 0) const;
|
||||||
|
|
||||||
vector<order_history_object> get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const;
|
vector<order_history_object> get_fill_order_history( asset_id_type a, asset_id_type b, uint32_t limit )const;
|
||||||
vector<bucket_object> get_market_history( asset_id_type a, asset_id_type b, uint32_t bucket_seconds,
|
vector<bucket_object> 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,
|
FC_API(graphene::app::history_api,
|
||||||
(get_account_history)
|
(get_account_history)
|
||||||
|
(get_relative_account_history)
|
||||||
(get_fill_order_history)
|
(get_fill_order_history)
|
||||||
(get_market_history)
|
(get_market_history)
|
||||||
(get_market_history_buckets)
|
(get_market_history_buckets)
|
||||||
|
|
|
||||||
|
|
@ -64,40 +64,47 @@ using namespace std;
|
||||||
|
|
||||||
class database_api_impl;
|
class database_api_impl;
|
||||||
|
|
||||||
|
struct order
|
||||||
|
{
|
||||||
|
double price;
|
||||||
|
double quote;
|
||||||
|
double base;
|
||||||
|
};
|
||||||
|
|
||||||
struct order_book
|
struct order_book
|
||||||
{
|
{
|
||||||
string base;
|
string base;
|
||||||
string quote;
|
string quote;
|
||||||
vector< pair<double,double> > bids;
|
vector< order > bids;
|
||||||
vector< pair<double,double> > asks;
|
vector< order > asks;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct market_ticker
|
struct market_ticker
|
||||||
{
|
{
|
||||||
string base;
|
string base;
|
||||||
string quote;
|
string quote;
|
||||||
double latest;
|
double latest;
|
||||||
double lowest_ask;
|
double lowest_ask;
|
||||||
double highest_bid;
|
double highest_bid;
|
||||||
double percent_change;
|
double percent_change;
|
||||||
double base_volume;
|
double base_volume;
|
||||||
double quote_volume;
|
double quote_volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct market_volume
|
struct market_volume
|
||||||
{
|
{
|
||||||
string base;
|
string base;
|
||||||
string quote;
|
string quote;
|
||||||
double base_volume;
|
double base_volume;
|
||||||
double quote_volume;
|
double quote_volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct market_trade
|
struct market_trade
|
||||||
{
|
{
|
||||||
fc::time_point_sec date;
|
fc::time_point_sec date;
|
||||||
double price;
|
double price;
|
||||||
double amount;
|
double amount;
|
||||||
double value;
|
double value;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -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::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_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) );
|
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include <graphene/chain/internal_exceptions.hpp>
|
#include <graphene/chain/internal_exceptions.hpp>
|
||||||
#include <graphene/chain/special_authority.hpp>
|
#include <graphene/chain/special_authority.hpp>
|
||||||
#include <graphene/chain/special_authority_object.hpp>
|
#include <graphene/chain/special_authority_object.hpp>
|
||||||
|
#include <graphene/chain/worker_object.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
@ -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<worker_index>().indices().get<by_vote_against>();
|
||||||
|
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 )
|
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
|
||||||
{ try {
|
{ try {
|
||||||
database& d = db();
|
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.owner_special_authority.valid() );
|
||||||
FC_ASSERT( !op.extensions.value.active_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( 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( 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." );
|
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
|
try
|
||||||
{
|
{
|
||||||
verify_authority_accounts( d, op.owner );
|
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 );
|
evaluate_special_authority( d, *op.extensions.value.active_special_authority );
|
||||||
if( op.extensions.value.buyback_options.valid() )
|
if( op.extensions.value.buyback_options.valid() )
|
||||||
evaluate_buyback_account_options( d, *op.extensions.value.buyback_options );
|
evaluate_buyback_account_options( d, *op.extensions.value.buyback_options );
|
||||||
|
verify_account_votes( d, op.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<uint32_t> 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));
|
|
||||||
|
|
||||||
auto& acnt_indx = d.get_index_type<account_index>();
|
auto& acnt_indx = d.get_index_type<account_index>();
|
||||||
if( op.name.size() )
|
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.owner_special_authority.valid() );
|
||||||
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
|
FC_ASSERT( !o.extensions.value.active_special_authority.valid() );
|
||||||
}
|
}
|
||||||
|
if( d.head_block_time() < HARDFORK_599_TIME )
|
||||||
const auto& chain_params = d.get_global_properties().parameters;
|
{
|
||||||
|
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
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -241,16 +267,8 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
|
||||||
|
|
||||||
acnt = &o.account(d);
|
acnt = &o.account(d);
|
||||||
|
|
||||||
if( o.new_options )
|
if( o.new_options.valid() )
|
||||||
{
|
verify_account_votes( d, *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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return void_result();
|
return void_result();
|
||||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
} 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;
|
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;
|
||||||
} else if( a.is_annual_member(d.head_block_time()) ) {
|
} else if( a.is_annual_member(d.head_block_time()) ) {
|
||||||
// Renew an annual subscription that's still in effect.
|
// 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),
|
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.");
|
"May not extend annual membership more than a decade into the future.");
|
||||||
a.membership_expiration_date += fc::days(365);
|
a.membership_expiration_date += fc::days(365);
|
||||||
} else {
|
} else {
|
||||||
// Upgrade from basic account.
|
// Upgrade from basic account.
|
||||||
|
FC_ASSERT( d.head_block_time() <= HARDFORK_613_TIME );
|
||||||
a.statistics(d).process_fees(a, d);
|
a.statistics(d).process_fees(a, d);
|
||||||
assert(a.is_basic_account(d.head_block_time()));
|
assert(a.is_basic_account(d.head_block_time()));
|
||||||
a.referrer = a.get_id();
|
a.referrer = a.get_id();
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@
|
||||||
#include <graphene/chain/exceptions.hpp>
|
#include <graphene/chain/exceptions.hpp>
|
||||||
#include <graphene/chain/evaluator.hpp>
|
#include <graphene/chain/evaluator.hpp>
|
||||||
|
|
||||||
|
#include <fc/smart_ref_impl.hpp>
|
||||||
|
|
||||||
namespace graphene { namespace chain {
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
bool database::is_known_block( const block_id_type& id )const
|
bool database::is_known_block( const block_id_type& id )const
|
||||||
|
|
|
||||||
|
|
@ -102,11 +102,12 @@ void database::update_worker_votes()
|
||||||
{
|
{
|
||||||
auto& idx = get_index_type<worker_index>();
|
auto& idx = get_index_type<worker_index>();
|
||||||
auto itr = idx.indices().get<by_account>().begin();
|
auto itr = idx.indices().get<by_account>().begin();
|
||||||
|
bool allow_negative_votes = (head_block_time() < HARDFORK_607_TIME);
|
||||||
while( itr != idx.indices().get<by_account>().end() )
|
while( itr != idx.indices().get<by_account>().end() )
|
||||||
{
|
{
|
||||||
modify( *itr, [&]( worker_object& obj ){
|
modify( *itr, [&]( worker_object& obj ){
|
||||||
obj.total_votes_for = _vote_tally_buffer[obj.vote_for];
|
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;
|
++itr;
|
||||||
}
|
}
|
||||||
|
|
@ -684,6 +685,37 @@ void create_buyback_orders( database& db )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deprecate_annual_members( database& db )
|
||||||
|
{
|
||||||
|
const auto& account_idx = db.get_index_type<account_index>().indices().get<by_id>();
|
||||||
|
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)
|
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||||
{
|
{
|
||||||
const auto& gpo = get_global_properties();
|
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.next_maintenance_time = next_maintenance_time;
|
||||||
d.accounts_registered_this_interval = 0;
|
d.accounts_registered_this_interval = 0;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <graphene/chain/asset_object.hpp>
|
#include <graphene/chain/asset_object.hpp>
|
||||||
#include <graphene/chain/global_property_object.hpp>
|
#include <graphene/chain/global_property_object.hpp>
|
||||||
|
#include <graphene/chain/hardfork.hpp>
|
||||||
#include <graphene/chain/market_object.hpp>
|
#include <graphene/chain/market_object.hpp>
|
||||||
#include <graphene/chain/proposal_object.hpp>
|
#include <graphene/chain/proposal_object.hpp>
|
||||||
#include <graphene/chain/transaction_object.hpp>
|
#include <graphene/chain/transaction_object.hpp>
|
||||||
|
|
@ -434,7 +435,12 @@ void database::update_expired_feeds()
|
||||||
assert( a.is_market_issued() );
|
assert( a.is_market_issued() );
|
||||||
|
|
||||||
const asset_bitasset_data_object& b = a.bitasset_data(*this);
|
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) {
|
modify(b, [this](asset_bitasset_data_object& a) {
|
||||||
a.update_median_feeds(head_block_time());
|
a.update_median_feeds(head_block_time());
|
||||||
|
|
|
||||||
4
libraries/chain/hardfork.d/599.hf
Normal file
4
libraries/chain/hardfork.d/599.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// #599 Unpacking of extension is incorrect
|
||||||
|
#ifndef HARDFORK_599_TIME
|
||||||
|
#define HARDFORK_599_TIME (fc::time_point_sec( 1458061200 ))
|
||||||
|
#endif
|
||||||
4
libraries/chain/hardfork.d/607.hf
Normal file
4
libraries/chain/hardfork.d/607.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// #607 Disable negative voting on workers
|
||||||
|
#ifndef HARDFORK_607_TIME
|
||||||
|
#define HARDFORK_607_TIME (fc::time_point_sec( 1458061200 ))
|
||||||
|
#endif
|
||||||
4
libraries/chain/hardfork.d/613.hf
Normal file
4
libraries/chain/hardfork.d/613.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// #613 Deprecate annual membership
|
||||||
|
#ifndef HARDFORK_613_TIME
|
||||||
|
#define HARDFORK_613_TIME (fc::time_point_sec( 1458061200 ))
|
||||||
|
#endif
|
||||||
4
libraries/chain/hardfork.d/615.hf
Normal file
4
libraries/chain/hardfork.d/615.hf
Normal file
|
|
@ -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
|
||||||
|
|
@ -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.
|
* 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;
|
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
|
* 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),
|
(graphene::chain::object),
|
||||||
(owner)
|
(owner)
|
||||||
(most_recent_op)
|
(most_recent_op)
|
||||||
|
(total_ops)
|
||||||
(total_core_in_orders)
|
(total_core_in_orders)
|
||||||
(lifetime_fees_paid)
|
(lifetime_fees_paid)
|
||||||
(pending_fees)(pending_vested_fees)
|
(pending_fees)(pending_vested_fees)
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,8 @@ namespace graphene { namespace chain {
|
||||||
|
|
||||||
time_point_sec feed_expiration_time()const
|
time_point_sec feed_expiration_time()const
|
||||||
{ return current_feed_publication_time + options.feed_lifetime_sec; }
|
{ 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
|
bool feed_is_expired(time_point_sec current_time)const
|
||||||
{ return feed_expiration_time() <= current_time; }
|
{ return feed_expiration_time() <= current_time; }
|
||||||
void update_median_feeds(time_point_sec current_time);
|
void update_median_feeds(time_point_sec current_time);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <graphene/chain/protocol/operations.hpp>
|
#include <graphene/chain/protocol/operations.hpp>
|
||||||
#include <graphene/db/object.hpp>
|
#include <graphene/db/object.hpp>
|
||||||
|
#include <boost/multi_index/composite_key.hpp>
|
||||||
|
|
||||||
namespace graphene { namespace chain {
|
namespace graphene { namespace chain {
|
||||||
|
|
||||||
|
|
@ -89,13 +90,44 @@ namespace graphene { namespace chain {
|
||||||
public:
|
public:
|
||||||
static const uint8_t space_id = implementation_ids;
|
static const uint8_t space_id = implementation_ids;
|
||||||
static const uint8_t type_id = impl_account_transaction_history_object_type;
|
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;
|
operation_history_id_type operation_id;
|
||||||
|
uint32_t sequence = 0; /// the operation position within the given account
|
||||||
account_transaction_history_id_type next;
|
account_transaction_history_id_type next;
|
||||||
|
|
||||||
|
//std::pair<account_id_type,operation_history_id_type> account_op()const { return std::tie( account, operation_id ); }
|
||||||
|
//std::pair<account_id_type,uint32_t> 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<by_id>, member< object, object_id_type, &object::id > >,
|
||||||
|
ordered_unique< tag<by_seq>,
|
||||||
|
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<by_op>,
|
||||||
|
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_object, account_transaction_history_multi_index_type> account_transaction_history_index;
|
||||||
|
|
||||||
|
|
||||||
} } // graphene::chain
|
} } // graphene::chain
|
||||||
|
|
||||||
FC_REFLECT_DERIVED( graphene::chain::operation_history_object, (graphene::chain::object),
|
FC_REFLECT_DERIVED( graphene::chain::operation_history_object, (graphene::chain::object),
|
||||||
(op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) )
|
(op)(result)(block_num)(trx_in_block)(op_in_trx)(virtual_op) )
|
||||||
|
|
||||||
FC_REFLECT_DERIVED( graphene::chain::account_transaction_history_object, (graphene::chain::object),
|
FC_REFLECT_DERIVED( graphene::chain::account_transaction_history_object, (graphene::chain::object),
|
||||||
(operation_id)(next) )
|
(account)(operation_id)(sequence)(next) )
|
||||||
|
|
|
||||||
|
|
@ -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& stats_obj = account_id(db).statistics(db);
|
||||||
const auto& ath = db.create<account_transaction_history_object>( [&]( account_transaction_history_object& obj ){
|
const auto& ath = db.create<account_transaction_history_object>( [&]( account_transaction_history_object& obj ){
|
||||||
obj.operation_id = oho.id;
|
obj.operation_id = oho.id;
|
||||||
|
obj.account = account_id;
|
||||||
|
obj.sequence = stats_obj.total_ops+1;
|
||||||
obj.next = stats_obj.most_recent_op;
|
obj.next = stats_obj.most_recent_op;
|
||||||
});
|
});
|
||||||
db.modify( stats_obj, [&]( account_statistics_object& obj ){
|
db.modify( stats_obj, [&]( account_statistics_object& obj ){
|
||||||
obj.most_recent_op = ath.id;
|
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
|
} // end namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
account_history_plugin::account_history_plugin() :
|
account_history_plugin::account_history_plugin() :
|
||||||
my( new detail::account_history_plugin_impl(*this) )
|
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().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< 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);
|
LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,15 @@
|
||||||
#include <graphene/app/plugin.hpp>
|
#include <graphene/app/plugin.hpp>
|
||||||
#include <graphene/chain/database.hpp>
|
#include <graphene/chain/database.hpp>
|
||||||
|
|
||||||
|
#include <graphene/chain/operation_history_object.hpp>
|
||||||
|
|
||||||
#include <fc/thread/future.hpp>
|
#include <fc/thread/future.hpp>
|
||||||
|
|
||||||
namespace graphene { namespace account_history {
|
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
|
// 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
|
} } //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<by_id>, member< object, object_id_type, &object::id > >,
|
||||||
|
boost::multi_index::ordered_unique< tag<by_seq>,
|
||||||
|
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<by_op>,
|
||||||
|
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<graphene::chain::account_transaction_history_object, account_transaction_history_multi_index_type> account_transaction_history_index;
|
||||||
|
*/
|
||||||
|
|
|
||||||
|
|
@ -850,6 +850,51 @@ class wallet_api
|
||||||
bool fill_or_kill = false,
|
bool fill_or_kill = false,
|
||||||
bool broadcast = 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.
|
/** Borrow an asset or update the debt/collateral ratio for the loan.
|
||||||
*
|
*
|
||||||
* This is the first step in shorting an asset. Call \c sell_asset() to complete the short.
|
* This is the first step in shorting an asset. Call \c sell_asset() to complete the short.
|
||||||
|
|
@ -1402,6 +1447,8 @@ class wallet_api
|
||||||
bool broadcast /* = false */
|
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_uia(string creator, string symbol);
|
||||||
void dbg_make_mia(string creator, string symbol);
|
void dbg_make_mia(string creator, string symbol);
|
||||||
void flood_network(string prefix, uint32_t number_of_transactions);
|
void flood_network(string prefix, uint32_t number_of_transactions);
|
||||||
|
|
@ -1517,6 +1564,8 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(upgrade_account)
|
(upgrade_account)
|
||||||
(create_account_with_brain_key)
|
(create_account_with_brain_key)
|
||||||
(sell_asset)
|
(sell_asset)
|
||||||
|
(sell)
|
||||||
|
(buy)
|
||||||
(borrow_asset)
|
(borrow_asset)
|
||||||
(cancel_order)
|
(cancel_order)
|
||||||
(transfer)
|
(transfer)
|
||||||
|
|
@ -1589,4 +1638,5 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(blind_transfer)
|
(blind_transfer)
|
||||||
(blind_history)
|
(blind_history)
|
||||||
(receive_blind_transfer)
|
(receive_blind_transfer)
|
||||||
|
(get_order_book)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2166,6 +2166,90 @@ public:
|
||||||
}
|
}
|
||||||
return ss.str();
|
return ss.str();
|
||||||
};
|
};
|
||||||
|
m["get_order_book"] = [this](variant result, const fc::variants& a)
|
||||||
|
{
|
||||||
|
auto orders = result.as<order_book>();
|
||||||
|
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;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
@ -3552,6 +3636,28 @@ signed_transaction wallet_api::sell_asset(string seller_account,
|
||||||
symbol_to_receive, expiration, fill_or_kill, broadcast);
|
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,
|
signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell,
|
||||||
string asset_symbol, string amount_of_collateral, bool broadcast)
|
string asset_symbol, string amount_of_collateral, bool broadcast)
|
||||||
{
|
{
|
||||||
|
|
@ -4091,6 +4197,11 @@ vector<blind_receipt> wallet_api::blind_history( string key_or_account )
|
||||||
return result;
|
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_with_info::signed_block_with_info( const signed_block& block )
|
||||||
: signed_block( block )
|
: signed_block( block )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -747,6 +747,7 @@ BOOST_AUTO_TEST_CASE( stealth_fba_test )
|
||||||
generate_blocks( HARDFORK_555_TIME );
|
generate_blocks( HARDFORK_555_TIME );
|
||||||
generate_blocks( HARDFORK_563_TIME );
|
generate_blocks( HARDFORK_563_TIME );
|
||||||
generate_blocks( HARDFORK_572_TIME );
|
generate_blocks( HARDFORK_572_TIME );
|
||||||
|
generate_blocks( HARDFORK_599_TIME );
|
||||||
|
|
||||||
// Philbin (registrar who registers Rex)
|
// Philbin (registrar who registers Rex)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1338,6 +1338,7 @@ BOOST_AUTO_TEST_CASE( top_n_special )
|
||||||
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) );
|
ACTORS( (alice)(bob)(chloe)(dan)(izzy)(stan) );
|
||||||
|
|
||||||
generate_blocks( HARDFORK_516_TIME );
|
generate_blocks( HARDFORK_516_TIME );
|
||||||
|
generate_blocks( HARDFORK_599_TIME );
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -1490,6 +1491,7 @@ BOOST_AUTO_TEST_CASE( buyback )
|
||||||
|
|
||||||
generate_blocks( HARDFORK_538_TIME );
|
generate_blocks( HARDFORK_538_TIME );
|
||||||
generate_blocks( HARDFORK_555_TIME );
|
generate_blocks( HARDFORK_555_TIME );
|
||||||
|
generate_blocks( HARDFORK_599_TIME );
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue