Compare commits

...

2 commits

Author SHA1 Message Date
satyakoneru
31d8e68d89 GRPH-60 Added Recursion guard to be 2*witness_node_count 2019-08-27 02:53:23 +00:00
satyakoneru
dea5a973ba GRPH-57-CompilerWarningFixesDBTests 2019-08-23 08:35:44 +00:00
10 changed files with 340 additions and 2 deletions

View file

@ -113,8 +113,15 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const; vector<bet_object> get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const;
vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const; vector<bet_object> get_all_unmatched_bets_for_bettor(account_id_type) const;
const account_object* get_account_from_string( const std::string& name_or_id, bool throw_if_not_found = true ) const;
// Markets / feeds // Markets / feeds
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
vector<limit_order_object> get_account_limit_orders( const string& account_name_or_id,
const string &base,
const string &quote, uint32_t limit,
optional<limit_order_id_type> ostart_id,
optional<price> ostart_price );
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const; vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const; vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const;
vector<call_order_object> get_margin_positions( const account_id_type& id )const; vector<call_order_object> get_margin_positions( const account_id_type& id )const;
@ -623,6 +630,92 @@ vector<optional<account_object>> database_api_impl::get_accounts(const vector<ac
return result; return result;
} }
vector<limit_order_object> database_api::get_account_limit_orders( const string& account_name_or_id, const string &base,
const string &quote, uint32_t limit, optional<limit_order_id_type> ostart_id, optional<price> ostart_price)
{
return my->get_account_limit_orders( account_name_or_id, base, quote, limit, ostart_id, ostart_price );
}
vector<limit_order_object> database_api_impl::get_account_limit_orders( const string& account_name_or_id, const string &base,
const string &quote, uint32_t limit, optional<limit_order_id_type> ostart_id, optional<price> ostart_price)
{
FC_ASSERT( limit <= 101 );
vector<limit_order_object> results;
uint32_t count = 0;
const account_object* account = get_account_from_string(account_name_or_id);
if (account == nullptr)
return results;
auto assets = lookup_asset_symbols( {base, quote} );
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
auto base_id = assets[0]->id;
auto quote_id = assets[1]->id;
if (ostart_price.valid()) {
FC_ASSERT(ostart_price->base.asset_id == base_id, "Base asset inconsistent with start price");
FC_ASSERT(ostart_price->quote.asset_id == quote_id, "Quote asset inconsistent with start price");
}
const auto& index_by_account = _db.get_index_type<limit_order_index>().indices().get<by_account>();
limit_order_multi_index_type::index<by_account>::type::const_iterator lower_itr;
limit_order_multi_index_type::index<by_account>::type::const_iterator upper_itr;
// if both order_id and price are invalid, query the first page
if ( !ostart_id.valid() && !ostart_price.valid() )
{
lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, price::max(base_id, quote_id)));
}
else if ( ostart_id.valid() )
{
// in case of the order been deleted during page querying
const limit_order_object *p_loo = _db.find(*ostart_id);
if ( !p_loo )
{
if ( ostart_price.valid() )
{
lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price, *ostart_id));
}
else
{
// start order id been deleted, yet not provided price either
FC_THROW("Order id invalid (maybe just been canceled?), and start price not provided");
}
}
else
{
const limit_order_object &loo = *p_loo;
// in case of the order not belongs to specified account or market
FC_ASSERT(loo.sell_price.base.asset_id == base_id, "Order base asset inconsistent");
FC_ASSERT(loo.sell_price.quote.asset_id == quote_id, "Order quote asset inconsistent with order");
FC_ASSERT(loo.seller == account->get_id(), "Order not owned by specified account");
lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, loo.sell_price, *ostart_id));
}
}
else
{
// if reach here start_price must be valid
lower_itr = index_by_account.lower_bound(std::make_tuple(account->id, *ostart_price));
}
upper_itr = index_by_account.upper_bound(std::make_tuple(account->id, price::min(base_id, quote_id)));
// Add the account's orders
for ( ; lower_itr != upper_itr && count < limit; ++lower_itr, ++count)
{
const limit_order_object &order = *lower_itr;
results.emplace_back(order);
}
return results;
}
std::map<string,full_account> database_api::get_full_accounts( const vector<string>& names_or_ids, bool subscribe ) std::map<string,full_account> database_api::get_full_accounts( const vector<string>& names_or_ids, bool subscribe )
{ {
return my->get_full_accounts( names_or_ids, subscribe ); return my->get_full_accounts( names_or_ids, subscribe );
@ -1094,6 +1187,25 @@ vector<bet_object> database_api_impl::get_all_unmatched_bets_for_bettor(account_
return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id))); return boost::copy_range<vector<bet_object> >(bet_idx.equal_range(std::make_tuple(bettor_id)));
} }
const account_object* database_api_impl::get_account_from_string( const std::string& name_or_id, bool throw_if_not_found) const
{
// TODO cache the result to avoid repeatly fetching from db
FC_ASSERT( name_or_id.size() > 0);
const account_object* account = nullptr;
if (std::isdigit(name_or_id[0]))
account = _db.find(fc::variant(name_or_id).as<account_id_type>());
else
{
const auto& idx = _db.get_index_type<account_index>().indices().get<by_name>();
auto itr = idx.find(name_or_id);
if (itr != idx.end())
account = &*itr;
}
if(throw_if_not_found)
FC_ASSERT( account, "no such account" );
return account;
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// // // //
// Markets / feeds // // Markets / feeds //

View file

@ -255,6 +255,36 @@ class database_api
*/ */
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const; vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
/**
* @brief Fetch all orders relevant to the specified account and specified market, result orders
* are sorted descendingly by price
*
* @param account_name_or_id The name or ID of an account to retrieve
* @param base Base asset
* @param quote Quote asset
* @param limit The limitation of items each query can fetch, not greater than 101
* @param start_id Start order id, fetch orders which price lower than this order, or price equal to this order
* but order ID greater than this order
* @param start_price Fetch orders with price lower than or equal to this price
*
* @return List of orders from @ref account_name_or_id to the corresponding account
*
* @note
* 1. if @ref account_name_or_id cannot be tied to an account, empty result will be returned
* 2. @ref start_id and @ref start_price can be empty, if so the api will return the "first page" of orders;
* if start_id is specified, its price will be used to do page query preferentially, otherwise the start_price
* will be used; start_id and start_price may be used cooperatively in case of the order specified by start_id
* was just canceled accidentally, in such case, the result orders' price may lower or equal to start_price,
* but orders' id greater than start_id
*/
vector<limit_order_object> get_account_limit_orders( const string& account_name_or_id,
const string &base,
const string &quote,
uint32_t limit = 101,
optional<limit_order_id_type> ostart_id = optional<limit_order_id_type>(),
optional<price> ostart_price = optional<price>());
/** /**
* @brief Fetch all objects relevant to the specified accounts and subscribe to updates * @brief Fetch all objects relevant to the specified accounts and subscribe to updates
* @param callback Function to call with updates * @param callback Function to call with updates
@ -737,6 +767,7 @@ FC_API(graphene::app::database_api,
// Markets / feeds // Markets / feeds
(get_order_book) (get_order_book)
(get_limit_orders) (get_limit_orders)
(get_account_limit_orders)
(get_call_orders) (get_call_orders)
(get_settle_orders) (get_settle_orders)
(get_margin_positions) (get_margin_positions)

View file

@ -325,6 +325,24 @@ processed_transaction database::validate_transaction( const signed_transaction&
return _apply_transaction( trx ); return _apply_transaction( trx );
} }
class push_proposal_nesting_guard {
public:
push_proposal_nesting_guard( uint32_t& nesting_counter, const database& db )
: orig_value(nesting_counter), counter(nesting_counter)
{
FC_ASSERT( counter < db.get_global_properties().active_witnesses.size() * 2, "Max proposal nesting depth exceeded!" );
counter++;
}
~push_proposal_nesting_guard()
{
if( --counter != orig_value )
elog( "Unexpected proposal nesting count value: ${n} != ${o}", ("n",counter)("o",orig_value) );
}
private:
const uint32_t orig_value;
uint32_t& counter;
};
processed_transaction database::push_proposal(const proposal_object& proposal) processed_transaction database::push_proposal(const proposal_object& proposal)
{ try { { try {
transaction_evaluation_state eval_state(this); transaction_evaluation_state eval_state(this);
@ -336,6 +354,9 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
size_t old_applied_ops_size = _applied_ops.size(); size_t old_applied_ops_size = _applied_ops.size();
try { try {
push_proposal_nesting_guard guard( _push_proposal_nesting_depth, *this );
if( _undo_db.size() >= _undo_db.max_size() )
_undo_db.set_max_size( _undo_db.size() + 1 );
auto session = _undo_db.start_undo_session(true); auto session = _undo_db.start_undo_session(true);
for( auto& op : proposal.proposed_transaction.operations ) for( auto& op : proposal.proposed_transaction.operations )
eval_state.operation_results.emplace_back(apply_operation(eval_state, op)); eval_state.operation_results.emplace_back(apply_operation(eval_state, op));

View file

@ -544,6 +544,8 @@ namespace graphene { namespace chain {
node_property_object _node_property_object; node_property_object _node_property_object;
fc::hash_ctr_rng<secret_hash_type, 20> _random_number_generator; fc::hash_ctr_rng<secret_hash_type, 20> _random_number_generator;
bool _slow_replays = false; bool _slow_replays = false;
// Counts nested proposal updates
uint32_t _push_proposal_nesting_depth = 0;
}; };
namespace detail namespace detail

View file

@ -89,6 +89,7 @@ typedef multi_index_container<
ordered_unique< tag<by_account>, ordered_unique< tag<by_account>,
composite_key< limit_order_object, composite_key< limit_order_object,
member<limit_order_object, account_id_type, &limit_order_object::seller>, member<limit_order_object, account_id_type, &limit_order_object::seller>,
member<limit_order_object, price, &limit_order_object::sell_price>,
member<object, object_id_type, &object::id> member<object, object_id_type, &object::id>
> >
> >

View file

@ -89,7 +89,8 @@ void proposal_update_operation::get_required_authorities( vector<authority>& o )
auth.key_auths[k] = 1; auth.key_auths[k] = 1;
auth.weight_threshold = auth.key_auths.size(); auth.weight_threshold = auth.key_auths.size();
o.emplace_back( std::move(auth) ); if( auth.weight_threshold > 0 )
o.emplace_back( std::move(auth) );
} }
void proposal_update_operation::get_required_active_authorities( flat_set<account_id_type>& a )const void proposal_update_operation::get_required_active_authorities( flat_set<account_id_type>& a )const

View file

@ -372,6 +372,30 @@ class wallet_api
vector<account_balance_object> list_core_accounts()const; vector<account_balance_object> list_core_accounts()const;
vector<bucket_object> get_market_history(string symbol, string symbol2, uint32_t bucket, fc::time_point_sec start, fc::time_point_sec end)const; vector<bucket_object> get_market_history(string symbol, string symbol2, uint32_t bucket, fc::time_point_sec start, fc::time_point_sec end)const;
/**
* @brief Fetch all orders relevant to the specified account sorted descendingly by price
*
* @param name_or_id The name or ID of an account to retrieve
* @param base Base asset
* @param quote Quote asset
* @param limit The limitation of items each query can fetch (max: 101)
* @param ostart_id Start order id, fetch orders which price are lower than or equal to this order
* @param ostart_price Fetch orders with price lower than or equal to this price
*
* @return List of orders from \c name_or_id to the corresponding account
*
* @note
* 1. if \c name_or_id cannot be tied to an account, empty result will be returned
* 2. \c ostart_id and \c ostart_price can be \c null, if so the api will return the "first page" of orders;
* if \c ostart_id is specified and valid, its price will be used to do page query preferentially,
* otherwise the \c ostart_price will be used
*/
vector<limit_order_object> get_account_limit_orders( const string& name_or_id,
const string &base,
const string &quote,
uint32_t limit = 101,
optional<limit_order_id_type> ostart_id = optional<limit_order_id_type>(),
optional<price> ostart_price = optional<price>());
vector<limit_order_object> get_limit_orders(string a, string b, uint32_t limit)const; vector<limit_order_object> get_limit_orders(string a, string b, uint32_t limit)const;
vector<call_order_object> get_call_orders(string a, uint32_t limit)const; vector<call_order_object> get_call_orders(string a, uint32_t limit)const;
vector<force_settlement_object> get_settle_orders(string a, uint32_t limit)const; vector<force_settlement_object> get_settle_orders(string a, uint32_t limit)const;
@ -1984,6 +2008,7 @@ FC_API( graphene::wallet::wallet_api,
(get_private_key) (get_private_key)
(load_wallet_file) (load_wallet_file)
(normalize_brain_key) (normalize_brain_key)
(get_account_limit_orders)
(get_limit_orders) (get_limit_orders)
(get_call_orders) (get_call_orders)
(get_settle_orders) (get_settle_orders)

View file

@ -3522,6 +3522,17 @@ vector<bucket_object> wallet_api::get_market_history( string symbol1, string sym
return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, start, end ); return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, start, end );
} }
vector<limit_order_object> wallet_api::get_account_limit_orders(
const string& name_or_id,
const string &base,
const string &quote,
uint32_t limit,
optional<limit_order_id_type> ostart_id,
optional<price> ostart_price)
{
return my->_remote_db->get_account_limit_orders(name_or_id, base, quote, limit, ostart_id, ostart_price);
}
vector<limit_order_object> wallet_api::get_limit_orders(string a, string b, uint32_t limit)const vector<limit_order_object> wallet_api::get_limit_orders(string a, string b, uint32_t limit)const
{ {
return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit); return my->_remote_db->get_limit_orders(get_asset(a).id, get_asset(b).id, limit);

View file

@ -894,7 +894,6 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )
}); });
transaction tx; transaction tx;
processed_transaction ptx;
private_key_type committee_key = init_account_priv_key; private_key_type committee_key = init_account_priv_key;
// Sam is the creator of accounts // Sam is the creator of accounts
@ -1313,4 +1312,58 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
} }
} }
BOOST_AUTO_TEST_CASE( nested_execution )
{ try {
ACTORS( (alice)(bob) );
fund( alice );
generate_blocks( fc::time_point::now() + fc::hours(1) );
set_expiration( db, trx );
const auto& gpo = db.get_global_properties();
proposal_create_operation pco;
pco.expiration_time = db.head_block_time() + fc::minutes(1);
pco.fee_paying_account = alice_id;
proposal_id_type inner;
{
transfer_operation top;
top.from = alice_id;
top.to = bob_id;
top.amount = asset( 10 );
pco.proposed_ops.emplace_back( top );
trx.operations.push_back( pco );
inner = PUSH_TX( db, trx, ~0 ).operation_results.front().get<object_id_type>();
trx.clear();
pco.proposed_ops.clear();
}
std::vector<proposal_id_type> nested;
nested.push_back( inner );
for( size_t i = 0; i < gpo.active_witnesses.size() * 2; i++ )
{
proposal_update_operation pup;
pup.fee_paying_account = alice_id;
pup.proposal = nested.back();
pup.active_approvals_to_add.insert( alice_id );
pco.proposed_ops.emplace_back( pup );
trx.operations.push_back( pco );
nested.push_back( PUSH_TX( db, trx, ~0 ).operation_results.front().get<object_id_type>() );
trx.clear();
pco.proposed_ops.clear();
}
proposal_update_operation pup;
pup.fee_paying_account = alice_id;
pup.proposal = nested.back();
pup.active_approvals_to_add.insert( alice_id );
trx.operations.push_back( pup );
PUSH_TX( db, trx, ~0 );
for( size_t i = 1; i < nested.size(); i++ )
BOOST_CHECK_THROW( db.get<proposal_object>( nested[i] ), fc::assert_exception ); // executed successfully -> object removed
db.get<proposal_object>( inner ); // asn't executed -> object exists, doesn't throw
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View file

@ -68,4 +68,85 @@ BOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture)
} FC_LOG_AND_RETHROW() } FC_LOG_AND_RETHROW()
} }
BOOST_AUTO_TEST_CASE(get_account_limit_orders)
{ try {
ACTORS((seller));
const auto& bitcny = create_bitasset("CNY");
const auto& core = asset_id_type()(db);
int64_t init_balance(10000000);
transfer(committee_account, seller_id, asset(init_balance));
BOOST_CHECK_EQUAL( 10000000, get_balance(seller, core) );
/// Create 250 versatile orders
for (size_t i = 0 ; i < 50 ; ++i) {
BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250)));
}
for (size_t i = 1 ; i < 101 ; ++i) {
BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 + i)));
BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250 - i)));
}
graphene::app::database_api db_api(db);
std::vector<limit_order_object> results;
limit_order_object o;
// query with no constraint, expected:
// 1. up to 101 orders returned
// 2. orders were sorted by price desendingly
results = db_api.get_account_limit_orders(seller.name, "BTS", "CNY");
BOOST_CHECK(results.size() == 101);
for (size_t i = 0 ; i < results.size() - 1 ; ++i) {
BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);
}
results.clear();
// query with specified limit, expected:
// 1. up to specified amount of orders returned
// 2. orders were sorted by price desendingly
results = db_api.get_account_limit_orders(seller.name, "BTS", "CNY", 50);
results = db_api.get_account_limit_orders(seller.name, "BTS", "CNY", 50);
BOOST_CHECK(results.size() == 50);
for (size_t i = 0 ; i < results.size() - 1 ; ++i) {
BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);
}
o = results.back();
results.clear();
// query with specified order id and limit, expected:
// same as before, but also the first order's id equal to specified
results = db_api.get_account_limit_orders(seller.name, "BTS", "CNY", 100,
limit_order_id_type(o.id));
BOOST_CHECK(results.size() == 100);
BOOST_CHECK(results.front().id == o.id);
for (size_t i = 0 ; i < results.size() - 1 ; ++i) {
BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);
}
o = results.back();
results.clear();
// query with specified price and an not exists order id, expected:
// 1. the canceled order should not exists in returned orders and first order's
// id should greater than specified
// 2. returned orders sorted by price desendingly
// 3. the first order's sell price equal to specified
cancel_limit_order(o); // NOTE 1: this canceled order was in scope of the
// first created 50 orders, so with price 2.5 BTS/CNY
results = db_api.get_account_limit_orders(seller.name, "BTS", "CNY", 101,
limit_order_id_type(o.id), o.sell_price);
BOOST_CHECK(results.size() <= 101);
BOOST_CHECK(results.front().id > o.id);
// NOTE 2: because of NOTE 1, here should be equal
BOOST_CHECK(results.front().sell_price == o.sell_price);
for (size_t i = 0 ; i < results.size() - 1 ; ++i) {
BOOST_CHECK(results[i].sell_price >= results[i+1].sell_price);
}
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()