diff --git a/docs b/docs index c004ae42..cdc8ea81 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit c004ae42a72d86bbc6c7e8d065deed284fd093a5 +Subproject commit cdc8ea8133a999afef8051700a4ce8edb0988ec4 diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 6a2531a1..32a416ea 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -4,6 +4,7 @@ file(GLOB EGENESIS_HEADERS "../egenesis/include/graphene/app/*.hpp") add_library( graphene_app api.cpp application.cpp + database_api.cpp impacted.cpp plugin.cpp ${HEADERS} diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index e440854f..3fad3833 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -35,519 +35,6 @@ namespace graphene { namespace app { - database_api::database_api(graphene::chain::database& db):_db(db) - { - wlog("creating database api ${x}", ("x",int64_t(this)) ); - _change_connection = _db.changed_objects.connect([this](const vector& ids) { - on_objects_changed(ids); - }); - _removed_connection = _db.removed_objects.connect([this](const vector& objs) { - on_objects_removed(objs); - }); - _applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); }); - - _pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){ - if( _pending_trx_callback ) _pending_trx_callback( fc::variant(trx) ); - }); - } - - database_api::~database_api() - { - elog("freeing database api ${x}", ("x",int64_t(this)) ); - } - - void database_api::set_subscribe_callback( std::function cb, bool clear_filter ) - { - edump((clear_filter)); - _subscribe_callback = cb; - if( clear_filter || !cb ) - { - static fc::bloom_parameters param; - param.projected_element_count = 10000; - param.false_positive_probability = 1.0/10000; - param.maximum_size = 1024*8*8*2; - param.compute_optimal_parameters(); - _subscribe_filter = fc::bloom_filter(param); - } - } - - fc::variants database_api::get_objects(const vector& ids)const - { - if( _subscribe_callback ) { - for( auto id : ids ) - { - if( id.type() == operation_history_object_type && id.space() == protocol_ids ) continue; - if( id.type() == impl_account_transaction_history_object_type && id.space() == implementation_ids ) continue; - - this->subscribe_to_item( id ); - } - } - else - { - elog( "getObjects without subscribe callback??" ); - } - - fc::variants result; - result.reserve(ids.size()); - - std::transform(ids.begin(), ids.end(), std::back_inserter(result), - [this](object_id_type id) -> fc::variant { - if(auto obj = _db.find_object(id)) - return obj->to_variant(); - return {}; - }); - - return result; - } - - optional database_api::get_block_header(uint32_t block_num) const - { - auto result = _db.fetch_block_by_number(block_num); - if(result) - return *result; - return {}; - } - - optional database_api::get_block(uint32_t block_num)const - { - return _db.fetch_block_by_number(block_num); - } - processed_transaction database_api::get_transaction(uint32_t block_num, uint32_t trx_num)const - { - auto opt_block = _db.fetch_block_by_number(block_num); - FC_ASSERT( opt_block ); - FC_ASSERT( opt_block->transactions.size() > trx_num ); - return opt_block->transactions[trx_num]; - } - - vector> database_api::lookup_account_names(const vector& account_names)const - { - const auto& accounts_by_name = _db.get_index_type().indices().get(); - vector > result; - result.reserve(account_names.size()); - std::transform(account_names.begin(), account_names.end(), std::back_inserter(result), - [&accounts_by_name](const string& name) -> optional { - auto itr = accounts_by_name.find(name); - return itr == accounts_by_name.end()? optional() : *itr; - }); - return result; - } - - vector> database_api::lookup_asset_symbols(const vector& symbols_or_ids)const - { - const auto& assets_by_symbol = _db.get_index_type().indices().get(); - vector > result; - result.reserve(symbols_or_ids.size()); - std::transform(symbols_or_ids.begin(), symbols_or_ids.end(), std::back_inserter(result), - [this, &assets_by_symbol](const string& symbol_or_id) -> optional { - if( !symbol_or_id.empty() && std::isdigit(symbol_or_id[0]) ) - { - auto ptr = _db.find(variant(symbol_or_id).as()); - return ptr == nullptr? optional() : *ptr; - } - auto itr = assets_by_symbol.find(symbol_or_id); - return itr == assets_by_symbol.end()? optional() : *itr; - }); - return result; - } - - chain_property_object database_api::get_chain_properties()const - { - return _db.get(chain_property_id_type()); - } - - global_property_object database_api::get_global_properties()const - { - return _db.get(global_property_id_type()); - } - - fc::variant_object database_api::get_config()const - { - return get_config(); - } - - chain_id_type database_api::get_chain_id()const - { - return _db.get_chain_id(); - } - - dynamic_global_property_object database_api::get_dynamic_global_properties()const - { - return _db.get(dynamic_global_property_id_type()); - } - - - vector> database_api::get_accounts(const vector& account_ids)const - { - vector> result; result.reserve(account_ids.size()); - std::transform(account_ids.begin(), account_ids.end(), std::back_inserter(result), - [this](account_id_type id) -> optional { - if(auto o = _db.find(id)) - { - subscribe_to_item( id ); - return *o; - } - return {}; - }); - return result; - } - - vector> database_api::get_assets(const vector& asset_ids)const - { - vector> result; result.reserve(asset_ids.size()); - std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result), - [this](asset_id_type id) -> optional { - if(auto o = _db.find(id)) - { - subscribe_to_item( id ); - return *o; - } - return {}; - }); - return result; - } - - uint64_t database_api::get_account_count()const - { - return _db.get_index_type().indices().size(); - } - - map database_api::lookup_accounts(const string& lower_bound_name, uint32_t limit)const - { - FC_ASSERT( limit <= 1000 ); - const auto& accounts_by_name = _db.get_index_type().indices().get(); - map result; - - for( auto itr = accounts_by_name.lower_bound(lower_bound_name); - limit-- && itr != accounts_by_name.end(); - ++itr ) - { - result.insert(make_pair(itr->name, itr->get_id())); - if( limit == 1 ) - subscribe_to_item( itr->get_id() ); - } - - return result; - } - - std::map database_api::get_full_accounts( const vector& names_or_ids, bool subscribe) - { - idump((names_or_ids)); - std::map results; - - for (const std::string& account_name_or_id : names_or_ids) - { - const account_object* account = nullptr; - if (std::isdigit(account_name_or_id[0])) - account = _db.find(fc::variant(account_name_or_id).as()); - else - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(account_name_or_id); - if (itr != idx.end()) - account = &*itr; - } - if (account == nullptr) - continue; - - if( subscribe ) - { - ilog( "subscribe to ${id}", ("id",account->name) ); - subscribe_to_item( account->id ); - } - - // fc::mutable_variant_object full_account; - full_account acnt; - acnt.account = *account; - acnt.statistics = account->statistics(_db); - acnt.registrar_name = account->registrar(_db).name; - acnt.referrer_name = account->referrer(_db).name; - acnt.lifetime_referrer_name = account->lifetime_referrer(_db).name; - acnt.votes = lookup_vote_ids( vector(account->options.votes.begin(),account->options.votes.end()) ); - - // Add the account itself, its statistics object, cashback balance, and referral account names - /* - full_account("account", *account)("statistics", account->statistics(_db)) - ("registrar_name", account->registrar(_db).name)("referrer_name", account->referrer(_db).name) - ("lifetime_referrer_name", account->lifetime_referrer(_db).name); - */ - if (account->cashback_vb) - { - acnt.cashback_balance = account->cashback_balance(_db); - } - // Add the account's proposals - const auto& proposal_idx = _db.get_index_type(); - const auto& pidx = dynamic_cast&>(proposal_idx); - const auto& proposals_by_account = pidx.get_secondary_index(); - auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id ); - if( required_approvals_itr != proposals_by_account._account_to_proposals.end() ) - { - acnt.proposals.reserve( required_approvals_itr->second.size() ); - for( auto proposal_id : required_approvals_itr->second ) - acnt.proposals.push_back( proposal_id(_db) ); - } - - - // Add the account's balances - auto balance_range = _db.get_index_type().indices().get().equal_range(account->id); - //vector balances; - std::for_each(balance_range.first, balance_range.second, - [&acnt](const account_balance_object& balance) { - acnt.balances.emplace_back(balance); - }); - - // Add the account's vesting balances - auto vesting_range = _db.get_index_type().indices().get().equal_range(account->id); - std::for_each(vesting_range.first, vesting_range.second, - [&acnt](const vesting_balance_object& balance) { - acnt.vesting_balances.emplace_back(balance); - }); - - // Add the account's orders - auto order_range = _db.get_index_type().indices().get().equal_range(account->id); - std::for_each(order_range.first, order_range.second, - [&acnt] (const limit_order_object& order) { - acnt.limit_orders.emplace_back(order); - }); - auto call_range = _db.get_index_type().indices().get().equal_range(account->id); - std::for_each(call_range.first, call_range.second, - [&acnt] (const call_order_object& call) { - acnt.call_orders.emplace_back(call); - }); - results[account_name_or_id] = acnt; - } - return results; - } - - vector database_api::get_account_balances(account_id_type acnt, const flat_set& assets)const - { - vector result; - if (assets.empty()) - { - // if the caller passes in an empty list of assets, return balances for all assets the account owns - const account_balance_index& balance_index = _db.get_index_type(); - auto range = balance_index.indices().get().equal_range(acnt); - for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second)) - result.push_back(asset(balance.get_balance())); - } - else - { - result.reserve(assets.size()); - - std::transform(assets.begin(), assets.end(), std::back_inserter(result), - [this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); }); - } - - return result; - } - - vector database_api::get_named_account_balances(const std::string& name, const flat_set& assets) const - { - const auto& accounts_by_name = _db.get_index_type().indices().get(); - auto itr = accounts_by_name.find(name); - FC_ASSERT( itr != accounts_by_name.end() ); - return get_account_balances(itr->get_id(), assets); - } - - /** - * @return the limit orders for both sides of the book for the two assets specified up to limit number on each side. - */ - vector database_api::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const - { - const auto& limit_order_idx = _db.get_index_type(); - const auto& limit_price_idx = limit_order_idx.indices().get(); - - vector result; - - uint32_t count = 0; - auto limit_itr = limit_price_idx.lower_bound(price::max(a,b)); - auto limit_end = limit_price_idx.upper_bound(price::min(a,b)); - while(limit_itr != limit_end && count < limit) - { - result.push_back(*limit_itr); - ++limit_itr; - ++count; - } - count = 0; - limit_itr = limit_price_idx.lower_bound(price::max(b,a)); - limit_end = limit_price_idx.upper_bound(price::min(b,a)); - while(limit_itr != limit_end && count < limit) - { - result.push_back(*limit_itr); - ++limit_itr; - ++count; - } - - return result; - } - - vector database_api::get_call_orders(asset_id_type a, uint32_t limit)const - { - const auto& call_index = _db.get_index_type().indices().get(); - const asset_object& mia = _db.get(a); - price index_price = price::min(mia.bitasset_data(_db).options.short_backing_asset, mia.get_id()); - - return vector(call_index.lower_bound(index_price.min()), - call_index.lower_bound(index_price.max())); - } - - vector database_api::get_settle_orders(asset_id_type a, uint32_t limit)const - { - const auto& settle_index = _db.get_index_type().indices().get(); - const asset_object& mia = _db.get(a); - return vector(settle_index.lower_bound(mia.get_id()), - settle_index.upper_bound(mia.get_id())); - } - - vector database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const - { - FC_ASSERT( limit <= 100 ); - const auto& assets_by_symbol = _db.get_index_type().indices().get(); - vector result; - result.reserve(limit); - - auto itr = assets_by_symbol.lower_bound(lower_bound_symbol); - - if( lower_bound_symbol == "" ) - itr = assets_by_symbol.begin(); - - while(limit-- && itr != assets_by_symbol.end()) - result.emplace_back(*itr++); - - return result; - } - - fc::optional database_api::get_committee_member_by_account(account_id_type account) const - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(account); - if( itr != idx.end() ) - return *itr; - return {}; - } - - fc::optional database_api::get_witness_by_account(account_id_type account) const - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(account); - if( itr != idx.end() ) - return *itr; - return {}; - } - vector database_api::lookup_vote_ids( const vector& votes )const - { - FC_ASSERT( votes.size() < 100, "Only 100 votes can be queried at a time" ); - - const auto& witness_idx = _db.get_index_type().indices().get(); - const auto& committee_idx = _db.get_index_type().indices().get(); - - vector result; - result.reserve( votes.size() ); - for( auto id : votes ) - { - switch( id.type() ) - { - case vote_id_type::committee: - { - auto itr = committee_idx.find( id ); - if( itr != committee_idx.end() ) - result.emplace_back( variant( *itr ) ); - else - result.emplace_back( variant() ); - break; - } - case vote_id_type::witness: - { - auto itr = witness_idx.find( id ); - if( itr != witness_idx.end() ) - result.emplace_back( variant( *itr ) ); - else - result.emplace_back( variant() ); - break; - } - case vote_id_type::worker: - break; - case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings - } - } - return result; - } - - uint64_t database_api::get_witness_count()const - { - return _db.get_index_type().indices().size(); - } - - map database_api::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const - { - FC_ASSERT( limit <= 1000 ); - const auto& witnesses_by_id = _db.get_index_type().indices().get(); - - // we want to order witnesses by account name, but that name is in the account object - // so the witness_index doesn't have a quick way to access it. - // get all the names and look them all up, sort them, then figure out what - // records to return. This could be optimized, but we expect the - // number of witnesses to be few and the frequency of calls to be rare - std::map witnesses_by_account_name; - for (const witness_object& witness : witnesses_by_id) - if (auto account_iter = _db.find(witness.witness_account)) - if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name - witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id)); - - auto end_iter = witnesses_by_account_name.begin(); - while (end_iter != witnesses_by_account_name.end() && limit--) - ++end_iter; - witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end()); - return witnesses_by_account_name; - } - - map database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const - { - FC_ASSERT( limit <= 1000 ); - const auto& committee_members_by_id = _db.get_index_type().indices().get(); - - // we want to order committee_members by account name, but that name is in the account object - // so the committee_member_index doesn't have a quick way to access it. - // get all the names and look them all up, sort them, then figure out what - // records to return. This could be optimized, but we expect the - // number of committee_members to be few and the frequency of calls to be rare - std::map committee_members_by_account_name; - for (const committee_member_object& committee_member : committee_members_by_id) - if (auto account_iter = _db.find(committee_member.committee_member_account)) - if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name - committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id)); - - auto end_iter = committee_members_by_account_name.begin(); - while (end_iter != committee_members_by_account_name.end() && limit--) - ++end_iter; - committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end()); - return committee_members_by_account_name; - } - - vector> database_api::get_witnesses(const vector& witness_ids)const - { - vector> result; result.reserve(witness_ids.size()); - std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result), - [this](witness_id_type id) -> optional { - if(auto o = _db.find(id)) - return *o; - return {}; - }); - return result; - } - - vector> database_api::get_committee_members(const vector& committee_member_ids)const - { - vector> result; result.reserve(committee_member_ids.size()); - std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result), - [this](committee_member_id_type id) -> optional { - if(auto o = _db.find(id)) - return *o; - return {}; - }); - return result; - } - login_api::login_api(application& a) :_app(a) { @@ -811,184 +298,6 @@ namespace graphene { namespace app { return result; } // end get_relevant_accounts( obj ) - - void database_api::broadcast_updates( const vector& updates ) - { - if( updates.size() ) { - auto capture_this = shared_from_this(); - fc::async([capture_this,updates](){ - capture_this->_subscribe_callback( fc::variant(updates) ); - }); - } - } - - void database_api::on_objects_removed( const vector& objs ) - { - /// we need to ensure the database_api is not deleted for the life of the async operation - if( _subscribe_callback ) - { - vector updates; - updates.reserve(objs.size()); - - for( auto obj : objs ) - updates.emplace_back( obj->id ); - broadcast_updates( updates ); - } - - if( _market_subscriptions.size() ) - { - map< pair, vector > broadcast_queue; - for( const auto& obj : objs ) - { - const limit_order_object* order = dynamic_cast(obj); - if( order ) - { - auto sub = _market_subscriptions.find( order->get_market() ); - if( sub != _market_subscriptions.end() ) - broadcast_queue[order->get_market()].emplace_back( order->id ); - } - } - if( broadcast_queue.size() ) - { - auto capture_this = shared_from_this(); - fc::async([capture_this,this,broadcast_queue](){ - for( const auto& item : broadcast_queue ) - { - auto sub = _market_subscriptions.find(item.first); - if( sub != _market_subscriptions.end() ) - sub->second( fc::variant(item.second ) ); - } - }); - } - } - } - - void database_api::on_objects_changed(const vector& ids) - { - vector updates; - map< pair, vector > market_broadcast_queue; - - for(auto id : ids) - { - const object* obj = nullptr; - if( _subscribe_callback ) - { - obj = _db.find_object( id ); - if( obj ) - { - updates.emplace_back( obj->to_variant() ); - } - else - { - updates.emplace_back(id); // send just the id to indicate removal - } - } - - if( _market_subscriptions.size() ) - { - if( !_subscribe_callback ) - obj = _db.find_object( id ); - if( obj ) - { - const limit_order_object* order = dynamic_cast(obj); - if( order ) - { - auto sub = _market_subscriptions.find( order->get_market() ); - if( sub != _market_subscriptions.end() ) - market_broadcast_queue[order->get_market()].emplace_back( order->id ); - } - } - } - } - - auto capture_this = shared_from_this(); - - /// pushing the future back / popping the prior future if it is complete. - /// if a connection hangs then this could get backed up and result in - /// a failure to exit cleanly. - fc::async([capture_this,this,updates,market_broadcast_queue](){ - if( _subscribe_callback ) _subscribe_callback( updates ); - - for( const auto& item : market_broadcast_queue ) - { - auto sub = _market_subscriptions.find(item.first); - if( sub != _market_subscriptions.end() ) - sub->second( fc::variant(item.second ) ); - } - }); - } - - /** note: this method cannot yield because it is called in the middle of - * apply a block. - */ - void database_api::on_applied_block() - { - if (_block_applied_callback) - { - auto capture_this = shared_from_this(); - block_id_type block_id = _db.head_block_id(); - fc::async([this,capture_this,block_id](){ - _block_applied_callback(fc::variant(block_id)); - }); - } - - if(_market_subscriptions.size() == 0) - return; - - const auto& ops = _db.get_applied_operations(); - map< std::pair, vector> > subscribed_markets_ops; - for(const auto& op : ops) - { - std::pair market; - switch(op.op.which()) - { - /* This is sent via the object_changed callback - case operation::tag::value: - market = op.op.get().get_market(); - break; - */ - case operation::tag::value: - market = op.op.get().get_market(); - break; - /* - case operation::tag::value: - */ - default: break; - } - if(_market_subscriptions.count(market)) - subscribed_markets_ops[market].push_back(std::make_pair(op.op, op.result)); - } - /// we need to ensure the database_api is not deleted for the life of the async operation - auto capture_this = shared_from_this(); - fc::async([this,capture_this,subscribed_markets_ops](){ - for(auto item : subscribed_markets_ops) - { - auto itr = _market_subscriptions.find(item.first); - if(itr != _market_subscriptions.end()) - itr->second(fc::variant(item.second)); - } - }); - } - - void database_api::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) - { - if(a > b) std::swap(a,b); - FC_ASSERT(a != b); - _market_subscriptions[ std::make_pair(a,b) ] = callback; - } - - void database_api::unsubscribe_from_market(asset_id_type a, asset_id_type b) - { - if(a > b) std::swap(a,b); - FC_ASSERT(a != b); - _market_subscriptions.erase(std::make_pair(a,b)); - } - - std::string database_api::get_transaction_hex(const signed_transaction& trx)const - { - return fc::to_hex(fc::raw::pack(trx)); - } - 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()); @@ -1011,7 +320,6 @@ namespace graphene { namespace app { return result; } - flat_set history_api::get_market_history_buckets()const { auto hist = _app.get_plugin( "market_history" ); @@ -1043,268 +351,4 @@ namespace graphene { namespace app { return result; } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } - /** - * @return all accounts that referr to the key or account id in their owner or active authorities. - */ - vector database_api::get_account_references( account_id_type account_id )const - { - const auto& idx = _db.get_index_type(); - const auto& aidx = dynamic_cast&>(idx); - const auto& refs = aidx.get_secondary_index(); - auto itr = refs.account_to_account_memberships.find(account_id); - vector result; - - if( itr != refs.account_to_account_memberships.end() ) - { - result.reserve( itr->second.size() ); - for( auto item : itr->second ) result.push_back(item); - } - return result; - } - /** - * @return all accounts that referr to the key or account id in their owner or active authorities. - */ - vector> database_api::get_key_references( vector keys )const - { - wdump( (keys) ); - vector< vector > final_result; - final_result.reserve(keys.size()); - - for( auto& key : keys ) - { - - address a1( pts_address(key, false, 56) ); - address a2( pts_address(key, true, 56) ); - address a3( pts_address(key, false, 0) ); - address a4( pts_address(key, true, 0) ); - address a5( key ); - - subscribe_to_item( key ); - subscribe_to_item( a1 ); - subscribe_to_item( a2 ); - subscribe_to_item( a3 ); - subscribe_to_item( a4 ); - subscribe_to_item( a5 ); - - const auto& idx = _db.get_index_type(); - const auto& aidx = dynamic_cast&>(idx); - const auto& refs = aidx.get_secondary_index(); - auto itr = refs.account_to_key_memberships.find(key); - vector result; - - for( auto& a : {a1,a2,a3,a4,a5} ) - { - auto itr = refs.account_to_address_memberships.find(a); - if( itr != refs.account_to_address_memberships.end() ) - { - result.reserve( itr->second.size() ); - for( auto item : itr->second ) - { - wdump((a)(item)(item(_db).name)); - result.push_back(item); - } - } - } - - if( itr != refs.account_to_key_memberships.end() ) - { - result.reserve( itr->second.size() ); - for( auto item : itr->second ) result.push_back(item); - } - final_result.emplace_back( std::move(result) ); - } - - for( auto i : final_result ) - subscribe_to_item(i); - - return final_result; - } - - /** TODO: add secondary index that will accelerate this process */ - vector database_api::get_proposed_transactions( account_id_type id )const - { - const auto& idx = _db.get_index_type(); - vector result; - - idx.inspect_all_objects( [&](const object& obj){ - const proposal_object& p = static_cast(obj); - if( p.required_active_approvals.find( id ) != p.required_active_approvals.end() ) - result.push_back(p); - else if ( p.required_owner_approvals.find( id ) != p.required_owner_approvals.end() ) - result.push_back(p); - else if ( p.available_active_approvals.find( id ) != p.available_active_approvals.end() ) - result.push_back(p); - }); - return result; - } - - vector database_api::get_margin_positions( const account_id_type& id )const - { try { - const auto& idx = _db.get_index_type(); - const auto& aidx = idx.indices().get(); - auto start = aidx.lower_bound( boost::make_tuple( id, 0 ) ); - auto end = aidx.lower_bound( boost::make_tuple( id+1, 0 ) ); - vector result; - while( start != end ) - { - result.push_back(*start); - ++start; - } - return result; - } FC_CAPTURE_AND_RETHROW( (id) ) } - - - vector database_api::get_vested_balances( const vector& objs )const - { try { - vector result; - result.reserve( objs.size() ); - auto now = _db.head_block_time(); - for( auto obj : objs ) - result.push_back( obj(_db).available( now ) ); - return result; - } FC_CAPTURE_AND_RETHROW( (objs) ) } - - vector database_api::get_vesting_balances( account_id_type account_id )const - { - try - { - vector result; - auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id); - std::for_each(vesting_range.first, vesting_range.second, - [&result](const vesting_balance_object& balance) { - result.emplace_back(balance); - }); - return result; - } - FC_CAPTURE_AND_RETHROW( (account_id) ); - } - - vector database_api::get_balance_objects( const vector
& addrs )const - { try { - const auto& bal_idx = _db.get_index_type(); - const auto& by_owner_idx = bal_idx.indices().get(); - - vector result; - - for( const auto& owner : addrs ) - { - subscribe_to_item( owner ); - auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) ); - while( itr != by_owner_idx.end() && itr->owner == owner ) - { - result.push_back( *itr ); - ++itr; - } - } - return result; - } FC_CAPTURE_AND_RETHROW( (addrs) ) } - - set database_api::get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const - { - wdump((trx)(available_keys)); - auto result = trx.get_required_signatures( _db.get_chain_id(), - available_keys, - [&]( account_id_type id ){ return &id(_db).active; }, - [&]( account_id_type id ){ return &id(_db).owner; }, - _db.get_global_properties().parameters.max_authority_depth ); - wdump((result)); - return result; - } - set database_api::get_potential_signatures( const signed_transaction& trx )const - { - wdump((trx)); - set result; - trx.get_required_signatures( _db.get_chain_id(), - flat_set(), - [&]( account_id_type id ){ - const auto& auth = id(_db).active; - for( const auto& k : auth.get_keys() ) - result.insert(k); - return &auth; }, - [&]( account_id_type id ){ - const auto& auth = id(_db).owner; - for( const auto& k : auth.get_keys() ) - result.insert(k); - return &auth; - }, - _db.get_global_properties().parameters.max_authority_depth ); - - wdump((result)); - return result; - } - /** - * Validates a transaction against the current state without broadcast it on the network. - */ - processed_transaction database_api::validate_transaction( const signed_transaction& trx )const - { - return _db.validate_transaction(trx); - } - - bool database_api::verify_authority( const signed_transaction& trx )const - { - trx.verify_authority( _db.get_chain_id(), - [&]( account_id_type id ){ return &id(_db).active; }, - [&]( account_id_type id ){ return &id(_db).owner; }, - _db.get_global_properties().parameters.max_authority_depth ); - return true; - } - - bool database_api::verify_account_authority( const string& name_or_id, const flat_set& keys )const - { - 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()); - else - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(name_or_id); - if (itr != idx.end()) - account = &*itr; - } - FC_ASSERT( account, "no such account" ); - - - /// reuse trx.verify_authority by creating a dummy transfer - signed_transaction trx; - transfer_operation op; - op.from = account->id; - trx.operations.emplace_back(op); - - return verify_authority( trx ); - } - - vector database_api::get_blinded_balances( const flat_set& commitments )const - { - vector result; result.reserve(commitments.size()); - const auto& bal_idx = _db.get_index_type(); - const auto& by_commitment_idx = bal_idx.indices().get(); - for( const auto& c : commitments ) - { - auto itr = by_commitment_idx.find( c ); - if( itr != by_commitment_idx.end() ) - result.push_back( *itr ); - } - return result; - } - - vector database_api::get_required_fees( const vector& ops, asset_id_type id )const - { - vector result; - result.reserve(ops.size()); - const asset_object& a = id(_db); - for( const auto& op : ops ) - result.push_back( _db.current_fee_schedule().calculate_fee( op, a.options.core_exchange_rate ) ); - return result; - } - - optional database_api::get_account_by_name( string name )const - { - const auto& idx = _db.get_index_type().indices().get(); - auto itr = idx.find(name); - if (itr != idx.end()) - return *itr; - return optional(); - } - } } // graphene::app diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp new file mode 100644 index 00000000..8b55561e --- /dev/null +++ b/libraries/app/database_api.cpp @@ -0,0 +1,1495 @@ + +#include + +#include + +#include + +#include + +namespace graphene { namespace app { + +class database_api_impl; + +class database_api_impl : public std::enable_shared_from_this +{ + public: + database_api_impl( graphene::chain::database& db ); + ~database_api_impl(); + + // Objects + fc::variants get_objects(const vector& ids)const; + + // Subscriptions + void set_subscribe_callback( std::function cb, bool clear_filter ); + void set_pending_transaction_callback( std::function cb ); + void set_block_applied_callback( std::function cb ); + void cancel_all_subscriptions(); + + // Blocks and transactions + optional get_block_header(uint32_t block_num)const; + optional get_block(uint32_t block_num)const; + processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; + + // Globals + chain_property_object get_chain_properties()const; + global_property_object get_global_properties()const; + fc::variant_object get_config()const; + chain_id_type get_chain_id()const; + dynamic_global_property_object get_dynamic_global_properties()const; + + // Keys + vector> get_key_references( vector key )const; + + // Accounts + vector> get_accounts(const vector& account_ids)const; + std::map get_full_accounts( const vector& names_or_ids, bool subscribe ); + optional get_account_by_name( string name )const; + vector get_account_references( account_id_type account_id )const; + vector> lookup_account_names(const vector& account_names)const; + map lookup_accounts(const string& lower_bound_name, uint32_t limit)const; + uint64_t get_account_count()const; + + // Balances + vector get_account_balances(account_id_type id, const flat_set& assets)const; + vector get_named_account_balances(const std::string& name, const flat_set& assets)const; + vector get_balance_objects( const vector
& addrs )const; + vector get_vested_balances( const vector& objs )const; + vector get_vesting_balances( account_id_type account_id )const; + + // Assets + vector> get_assets(const vector& asset_ids)const; + vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; + vector> lookup_asset_symbols(const vector& symbols_or_ids)const; + + // Markets / feeds + vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; + vector get_call_orders(asset_id_type a, uint32_t limit)const; + vector get_settle_orders(asset_id_type a, uint32_t limit)const; + vector get_margin_positions( const account_id_type& id )const; + void subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b); + void unsubscribe_from_market(asset_id_type a, asset_id_type b); + + // Witnesses + vector> get_witnesses(const vector& witness_ids)const; + fc::optional get_witness_by_account(account_id_type account)const; + map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; + uint64_t get_witness_count()const; + + // Committee members + vector> get_committee_members(const vector& committee_member_ids)const; + fc::optional get_committee_member_by_account(account_id_type account)const; + map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; + + // Votes + vector lookup_vote_ids( const vector& votes )const; + + // Authority / validation + std::string get_transaction_hex(const signed_transaction& trx)const; + set get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const; + set get_potential_signatures( const signed_transaction& trx )const; + bool verify_authority( const signed_transaction& trx )const; + bool verify_account_authority( const string& name_or_id, const flat_set& signers )const; + processed_transaction validate_transaction( const signed_transaction& trx )const; + vector get_required_fees( const vector& ops, asset_id_type id )const; + + // Proposed transactions + vector get_proposed_transactions( account_id_type id )const; + + // Blinded balances + vector get_blinded_balances( const flat_set& commitments )const; + + private: + template + void subscribe_to_item( const T& i )const + { + auto vec = fc::raw::pack(i); + if( !_subscribe_callback ) + return; + + if( !is_subscribed_to_item(i) ) + { + idump((i)); + _subscribe_filter.insert( vec.data(), vec.size() );//(vecconst char*)&i, sizeof(i) ); + } + } + + template + bool is_subscribed_to_item( const T& i )const + { + if( !_subscribe_callback ) + return false; + return true; + return _subscribe_filter.contains( i ); + } + + void broadcast_updates( const vector& updates ); + + /** called every time a block is applied to report the objects that were changed */ + void on_objects_changed(const vector& ids); + void on_objects_removed(const vector& objs); + void on_applied_block(); + + mutable fc::bloom_filter _subscribe_filter; + std::function _subscribe_callback; + std::function _pending_trx_callback; + std::function _block_applied_callback; + + boost::signals2::scoped_connection _change_connection; + boost::signals2::scoped_connection _removed_connection; + boost::signals2::scoped_connection _applied_block_connection; + boost::signals2::scoped_connection _pending_trx_connection; + map< pair, std::function > _market_subscriptions; + graphene::chain::database& _db; +}; + +////////////////////////////////////////////////////////////////////// +// // +// Constructors // +// // +////////////////////////////////////////////////////////////////////// + +database_api::database_api( graphene::chain::database& db ) + : my( new database_api_impl( db ) ) {} + +database_api::~database_api() {} + +database_api_impl::database_api_impl( graphene::chain::database& db ):_db(db) +{ + wlog("creating database api ${x}", ("x",int64_t(this)) ); + _change_connection = _db.changed_objects.connect([this](const vector& ids) { + on_objects_changed(ids); + }); + _removed_connection = _db.removed_objects.connect([this](const vector& objs) { + on_objects_removed(objs); + }); + _applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); }); + + _pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){ + if( _pending_trx_callback ) _pending_trx_callback( fc::variant(trx) ); + }); +} + +database_api_impl::~database_api_impl() +{ + elog("freeing database api ${x}", ("x",int64_t(this)) ); +} + +////////////////////////////////////////////////////////////////////// +// // +// Objects // +// // +////////////////////////////////////////////////////////////////////// + +fc::variants database_api::get_objects(const vector& ids)const +{ + return my->get_objects( ids ); +} + +fc::variants database_api_impl::get_objects(const vector& ids)const +{ + if( _subscribe_callback ) { + for( auto id : ids ) + { + if( id.type() == operation_history_object_type && id.space() == protocol_ids ) continue; + if( id.type() == impl_account_transaction_history_object_type && id.space() == implementation_ids ) continue; + + this->subscribe_to_item( id ); + } + } + else + { + elog( "getObjects without subscribe callback??" ); + } + + fc::variants result; + result.reserve(ids.size()); + + std::transform(ids.begin(), ids.end(), std::back_inserter(result), + [this](object_id_type id) -> fc::variant { + if(auto obj = _db.find_object(id)) + return obj->to_variant(); + return {}; + }); + + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Subscriptions // +// // +////////////////////////////////////////////////////////////////////// + +void database_api::set_subscribe_callback( std::function cb, bool clear_filter ) +{ + my->set_subscribe_callback( cb, clear_filter ); +} + +void database_api_impl::set_subscribe_callback( std::function cb, bool clear_filter ) +{ + edump((clear_filter)); + _subscribe_callback = cb; + if( clear_filter || !cb ) + { + static fc::bloom_parameters param; + param.projected_element_count = 10000; + param.false_positive_probability = 1.0/10000; + param.maximum_size = 1024*8*8*2; + param.compute_optimal_parameters(); + _subscribe_filter = fc::bloom_filter(param); + } +} + +void database_api::set_pending_transaction_callback( std::function cb ) +{ + my->set_pending_transaction_callback( cb ); +} + +void database_api_impl::set_pending_transaction_callback( std::function cb ) +{ + _pending_trx_callback = cb; +} + +void database_api::set_block_applied_callback( std::function cb ) +{ + my->set_block_applied_callback( cb ); +} + +void database_api_impl::set_block_applied_callback( std::function cb ) +{ + _block_applied_callback = cb; +} + +void database_api::cancel_all_subscriptions() +{ + my->cancel_all_subscriptions(); +} + +void database_api_impl::cancel_all_subscriptions() +{ + set_subscribe_callback( std::function(), true); + _market_subscriptions.clear(); +} + +////////////////////////////////////////////////////////////////////// +// // +// Blocks and transactions // +// // +////////////////////////////////////////////////////////////////////// + +optional database_api::get_block_header(uint32_t block_num)const +{ + return my->get_block_header( block_num ); +} + +optional database_api_impl::get_block_header(uint32_t block_num) const +{ + auto result = _db.fetch_block_by_number(block_num); + if(result) + return *result; + return {}; +} + +optional database_api::get_block(uint32_t block_num)const +{ + return my->get_block( block_num ); +} + +optional database_api_impl::get_block(uint32_t block_num)const +{ + return _db.fetch_block_by_number(block_num); +} + +processed_transaction database_api::get_transaction( uint32_t block_num, uint32_t trx_in_block )const +{ + return my->get_transaction( block_num, trx_in_block ); +} + +processed_transaction database_api_impl::get_transaction(uint32_t block_num, uint32_t trx_num)const +{ + auto opt_block = _db.fetch_block_by_number(block_num); + FC_ASSERT( opt_block ); + FC_ASSERT( opt_block->transactions.size() > trx_num ); + return opt_block->transactions[trx_num]; +} + +////////////////////////////////////////////////////////////////////// +// // +// Globals // +// // +////////////////////////////////////////////////////////////////////// + +chain_property_object database_api::get_chain_properties()const +{ + return my->get_chain_properties(); +} + +chain_property_object database_api_impl::get_chain_properties()const +{ + return _db.get(chain_property_id_type()); +} + +global_property_object database_api::get_global_properties()const +{ + return my->get_global_properties(); +} + +global_property_object database_api_impl::get_global_properties()const +{ + return _db.get(global_property_id_type()); +} + +fc::variant_object database_api::get_config()const +{ + return my->get_config(); +} + +fc::variant_object database_api_impl::get_config()const +{ + return get_config(); +} + +chain_id_type database_api::get_chain_id()const +{ + return my->get_chain_id(); +} + +chain_id_type database_api_impl::get_chain_id()const +{ + return _db.get_chain_id(); +} + +dynamic_global_property_object database_api::get_dynamic_global_properties()const +{ + return my->get_dynamic_global_properties(); +} + +dynamic_global_property_object database_api_impl::get_dynamic_global_properties()const +{ + return _db.get(dynamic_global_property_id_type()); +} + +////////////////////////////////////////////////////////////////////// +// // +// Keys // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_key_references( vector key )const +{ + return my->get_key_references( key ); +} + +/** + * @return all accounts that referr to the key or account id in their owner or active authorities. + */ +vector> database_api_impl::get_key_references( vector keys )const +{ + wdump( (keys) ); + vector< vector > final_result; + final_result.reserve(keys.size()); + + for( auto& key : keys ) + { + + address a1( pts_address(key, false, 56) ); + address a2( pts_address(key, true, 56) ); + address a3( pts_address(key, false, 0) ); + address a4( pts_address(key, true, 0) ); + address a5( key ); + + subscribe_to_item( key ); + subscribe_to_item( a1 ); + subscribe_to_item( a2 ); + subscribe_to_item( a3 ); + subscribe_to_item( a4 ); + subscribe_to_item( a5 ); + + const auto& idx = _db.get_index_type(); + const auto& aidx = dynamic_cast&>(idx); + const auto& refs = aidx.get_secondary_index(); + auto itr = refs.account_to_key_memberships.find(key); + vector result; + + for( auto& a : {a1,a2,a3,a4,a5} ) + { + auto itr = refs.account_to_address_memberships.find(a); + if( itr != refs.account_to_address_memberships.end() ) + { + result.reserve( itr->second.size() ); + for( auto item : itr->second ) + { + wdump((a)(item)(item(_db).name)); + result.push_back(item); + } + } + } + + if( itr != refs.account_to_key_memberships.end() ) + { + result.reserve( itr->second.size() ); + for( auto item : itr->second ) result.push_back(item); + } + final_result.emplace_back( std::move(result) ); + } + + for( auto i : final_result ) + subscribe_to_item(i); + + return final_result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Accounts // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_accounts(const vector& account_ids)const +{ + return my->get_accounts( account_ids ); +} + +vector> database_api_impl::get_accounts(const vector& account_ids)const +{ + vector> result; result.reserve(account_ids.size()); + std::transform(account_ids.begin(), account_ids.end(), std::back_inserter(result), + [this](account_id_type id) -> optional { + if(auto o = _db.find(id)) + { + subscribe_to_item( id ); + return *o; + } + return {}; + }); + return result; +} + +std::map database_api::get_full_accounts( const vector& names_or_ids, bool subscribe ) +{ + return my->get_full_accounts( names_or_ids, subscribe ); +} + +std::map database_api_impl::get_full_accounts( const vector& names_or_ids, bool subscribe) +{ + idump((names_or_ids)); + std::map results; + + for (const std::string& account_name_or_id : names_or_ids) + { + const account_object* account = nullptr; + if (std::isdigit(account_name_or_id[0])) + account = _db.find(fc::variant(account_name_or_id).as()); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account_name_or_id); + if (itr != idx.end()) + account = &*itr; + } + if (account == nullptr) + continue; + + if( subscribe ) + { + ilog( "subscribe to ${id}", ("id",account->name) ); + subscribe_to_item( account->id ); + } + + // fc::mutable_variant_object full_account; + full_account acnt; + acnt.account = *account; + acnt.statistics = account->statistics(_db); + acnt.registrar_name = account->registrar(_db).name; + acnt.referrer_name = account->referrer(_db).name; + acnt.lifetime_referrer_name = account->lifetime_referrer(_db).name; + acnt.votes = lookup_vote_ids( vector(account->options.votes.begin(),account->options.votes.end()) ); + + // Add the account itself, its statistics object, cashback balance, and referral account names + /* + full_account("account", *account)("statistics", account->statistics(_db)) + ("registrar_name", account->registrar(_db).name)("referrer_name", account->referrer(_db).name) + ("lifetime_referrer_name", account->lifetime_referrer(_db).name); + */ + if (account->cashback_vb) + { + acnt.cashback_balance = account->cashback_balance(_db); + } + // Add the account's proposals + const auto& proposal_idx = _db.get_index_type(); + const auto& pidx = dynamic_cast&>(proposal_idx); + const auto& proposals_by_account = pidx.get_secondary_index(); + auto required_approvals_itr = proposals_by_account._account_to_proposals.find( account->id ); + if( required_approvals_itr != proposals_by_account._account_to_proposals.end() ) + { + acnt.proposals.reserve( required_approvals_itr->second.size() ); + for( auto proposal_id : required_approvals_itr->second ) + acnt.proposals.push_back( proposal_id(_db) ); + } + + + // Add the account's balances + auto balance_range = _db.get_index_type().indices().get().equal_range(account->id); + //vector balances; + std::for_each(balance_range.first, balance_range.second, + [&acnt](const account_balance_object& balance) { + acnt.balances.emplace_back(balance); + }); + + // Add the account's vesting balances + auto vesting_range = _db.get_index_type().indices().get().equal_range(account->id); + std::for_each(vesting_range.first, vesting_range.second, + [&acnt](const vesting_balance_object& balance) { + acnt.vesting_balances.emplace_back(balance); + }); + + // Add the account's orders + auto order_range = _db.get_index_type().indices().get().equal_range(account->id); + std::for_each(order_range.first, order_range.second, + [&acnt] (const limit_order_object& order) { + acnt.limit_orders.emplace_back(order); + }); + auto call_range = _db.get_index_type().indices().get().equal_range(account->id); + std::for_each(call_range.first, call_range.second, + [&acnt] (const call_order_object& call) { + acnt.call_orders.emplace_back(call); + }); + results[account_name_or_id] = acnt; + } + return results; +} + +optional database_api::get_account_by_name( string name )const +{ + return my->get_account_by_name( name ); +} + +optional database_api_impl::get_account_by_name( string name )const +{ + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(name); + if (itr != idx.end()) + return *itr; + return optional(); +} + +vector database_api::get_account_references( account_id_type account_id )const +{ + return my->get_account_references( account_id ); +} + +vector database_api_impl::get_account_references( account_id_type account_id )const +{ + const auto& idx = _db.get_index_type(); + const auto& aidx = dynamic_cast&>(idx); + const auto& refs = aidx.get_secondary_index(); + auto itr = refs.account_to_account_memberships.find(account_id); + vector result; + + if( itr != refs.account_to_account_memberships.end() ) + { + result.reserve( itr->second.size() ); + for( auto item : itr->second ) result.push_back(item); + } + return result; +} + +vector> database_api::lookup_account_names(const vector& account_names)const +{ + return my->lookup_account_names( account_names ); +} + +vector> database_api_impl::lookup_account_names(const vector& account_names)const +{ + const auto& accounts_by_name = _db.get_index_type().indices().get(); + vector > result; + result.reserve(account_names.size()); + std::transform(account_names.begin(), account_names.end(), std::back_inserter(result), + [&accounts_by_name](const string& name) -> optional { + auto itr = accounts_by_name.find(name); + return itr == accounts_by_name.end()? optional() : *itr; + }); + return result; +} + +map database_api::lookup_accounts(const string& lower_bound_name, uint32_t limit)const +{ + return my->lookup_accounts( lower_bound_name, limit ); +} + +map database_api_impl::lookup_accounts(const string& lower_bound_name, uint32_t limit)const +{ + FC_ASSERT( limit <= 1000 ); + const auto& accounts_by_name = _db.get_index_type().indices().get(); + map result; + + for( auto itr = accounts_by_name.lower_bound(lower_bound_name); + limit-- && itr != accounts_by_name.end(); + ++itr ) + { + result.insert(make_pair(itr->name, itr->get_id())); + if( limit == 1 ) + subscribe_to_item( itr->get_id() ); + } + + return result; +} + +uint64_t database_api::get_account_count()const +{ + return my->get_account_count(); +} + +uint64_t database_api_impl::get_account_count()const +{ + return _db.get_index_type().indices().size(); +} + +////////////////////////////////////////////////////////////////////// +// // +// Balances // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::get_account_balances(account_id_type id, const flat_set& assets)const +{ + return my->get_account_balances( id, assets ); +} + +vector database_api_impl::get_account_balances(account_id_type acnt, const flat_set& assets)const +{ + vector result; + if (assets.empty()) + { + // if the caller passes in an empty list of assets, return balances for all assets the account owns + const account_balance_index& balance_index = _db.get_index_type(); + auto range = balance_index.indices().get().equal_range(acnt); + for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second)) + result.push_back(asset(balance.get_balance())); + } + else + { + result.reserve(assets.size()); + + std::transform(assets.begin(), assets.end(), std::back_inserter(result), + [this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); }); + } + + return result; +} + +vector database_api::get_named_account_balances(const std::string& name, const flat_set& assets)const +{ + return my->get_named_account_balances( name, assets ); +} + +vector database_api_impl::get_named_account_balances(const std::string& name, const flat_set& assets) const +{ + const auto& accounts_by_name = _db.get_index_type().indices().get(); + auto itr = accounts_by_name.find(name); + FC_ASSERT( itr != accounts_by_name.end() ); + return get_account_balances(itr->get_id(), assets); +} + +vector database_api::get_balance_objects( const vector
& addrs )const +{ + return my->get_balance_objects( addrs ); +} + +vector database_api_impl::get_balance_objects( const vector
& addrs )const +{ + try + { + const auto& bal_idx = _db.get_index_type(); + const auto& by_owner_idx = bal_idx.indices().get(); + + vector result; + + for( const auto& owner : addrs ) + { + subscribe_to_item( owner ); + auto itr = by_owner_idx.lower_bound( boost::make_tuple( owner, asset_id_type(0) ) ); + while( itr != by_owner_idx.end() && itr->owner == owner ) + { + result.push_back( *itr ); + ++itr; + } + } + return result; + } + FC_CAPTURE_AND_RETHROW( (addrs) ) +} + +vector database_api::get_vested_balances( const vector& objs )const +{ + return my->get_vested_balances( objs ); +} + +vector database_api_impl::get_vested_balances( const vector& objs )const +{ + try + { + vector result; + result.reserve( objs.size() ); + auto now = _db.head_block_time(); + for( auto obj : objs ) + result.push_back( obj(_db).available( now ) ); + return result; + } FC_CAPTURE_AND_RETHROW( (objs) ) +} + +vector database_api::get_vesting_balances( account_id_type account_id )const +{ + return my->get_vesting_balances( account_id ); +} + +vector database_api_impl::get_vesting_balances( account_id_type account_id )const +{ + try + { + vector result; + auto vesting_range = _db.get_index_type().indices().get().equal_range(account_id); + std::for_each(vesting_range.first, vesting_range.second, + [&result](const vesting_balance_object& balance) { + result.emplace_back(balance); + }); + return result; + } + FC_CAPTURE_AND_RETHROW( (account_id) ); +} + +////////////////////////////////////////////////////////////////////// +// // +// Assets // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_assets(const vector& asset_ids)const +{ + return my->get_assets( asset_ids ); +} + +vector> database_api_impl::get_assets(const vector& asset_ids)const +{ + vector> result; result.reserve(asset_ids.size()); + std::transform(asset_ids.begin(), asset_ids.end(), std::back_inserter(result), + [this](asset_id_type id) -> optional { + if(auto o = _db.find(id)) + { + subscribe_to_item( id ); + return *o; + } + return {}; + }); + return result; +} + +vector database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const +{ + return my->list_assets( lower_bound_symbol, limit ); +} + +vector database_api_impl::list_assets(const string& lower_bound_symbol, uint32_t limit)const +{ + FC_ASSERT( limit <= 100 ); + const auto& assets_by_symbol = _db.get_index_type().indices().get(); + vector result; + result.reserve(limit); + + auto itr = assets_by_symbol.lower_bound(lower_bound_symbol); + + if( lower_bound_symbol == "" ) + itr = assets_by_symbol.begin(); + + while(limit-- && itr != assets_by_symbol.end()) + result.emplace_back(*itr++); + + return result; +} + +vector> database_api::lookup_asset_symbols(const vector& symbols_or_ids)const +{ + return my->lookup_asset_symbols( symbols_or_ids ); +} + +vector> database_api_impl::lookup_asset_symbols(const vector& symbols_or_ids)const +{ + const auto& assets_by_symbol = _db.get_index_type().indices().get(); + vector > result; + result.reserve(symbols_or_ids.size()); + std::transform(symbols_or_ids.begin(), symbols_or_ids.end(), std::back_inserter(result), + [this, &assets_by_symbol](const string& symbol_or_id) -> optional { + if( !symbol_or_id.empty() && std::isdigit(symbol_or_id[0]) ) + { + auto ptr = _db.find(variant(symbol_or_id).as()); + return ptr == nullptr? optional() : *ptr; + } + auto itr = assets_by_symbol.find(symbol_or_id); + return itr == assets_by_symbol.end()? optional() : *itr; + }); + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Markets / feeds // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const +{ + return my->get_limit_orders( a, b, limit ); +} + +/** + * @return the limit orders for both sides of the book for the two assets specified up to limit number on each side. + */ +vector database_api_impl::get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const +{ + const auto& limit_order_idx = _db.get_index_type(); + const auto& limit_price_idx = limit_order_idx.indices().get(); + + vector result; + + uint32_t count = 0; + auto limit_itr = limit_price_idx.lower_bound(price::max(a,b)); + auto limit_end = limit_price_idx.upper_bound(price::min(a,b)); + while(limit_itr != limit_end && count < limit) + { + result.push_back(*limit_itr); + ++limit_itr; + ++count; + } + count = 0; + limit_itr = limit_price_idx.lower_bound(price::max(b,a)); + limit_end = limit_price_idx.upper_bound(price::min(b,a)); + while(limit_itr != limit_end && count < limit) + { + result.push_back(*limit_itr); + ++limit_itr; + ++count; + } + + return result; +} + +vector database_api::get_call_orders(asset_id_type a, uint32_t limit)const +{ + return my->get_call_orders( a, limit ); +} + +vector database_api_impl::get_call_orders(asset_id_type a, uint32_t limit)const +{ + const auto& call_index = _db.get_index_type().indices().get(); + const asset_object& mia = _db.get(a); + price index_price = price::min(mia.bitasset_data(_db).options.short_backing_asset, mia.get_id()); + + return vector(call_index.lower_bound(index_price.min()), + call_index.lower_bound(index_price.max())); +} + +vector database_api::get_settle_orders(asset_id_type a, uint32_t limit)const +{ + return my->get_settle_orders( a, limit ); +} + +vector database_api_impl::get_settle_orders(asset_id_type a, uint32_t limit)const +{ + const auto& settle_index = _db.get_index_type().indices().get(); + const asset_object& mia = _db.get(a); + return vector(settle_index.lower_bound(mia.get_id()), + settle_index.upper_bound(mia.get_id())); +} + +vector database_api::get_margin_positions( const account_id_type& id )const +{ + return my->get_margin_positions( id ); +} + +vector database_api_impl::get_margin_positions( const account_id_type& id )const +{ + try + { + const auto& idx = _db.get_index_type(); + const auto& aidx = idx.indices().get(); + auto start = aidx.lower_bound( boost::make_tuple( id, 0 ) ); + auto end = aidx.lower_bound( boost::make_tuple( id+1, 0 ) ); + vector result; + while( start != end ) + { + result.push_back(*start); + ++start; + } + return result; + } FC_CAPTURE_AND_RETHROW( (id) ) +} + +void database_api::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) +{ + my->subscribe_to_market( callback, a, b ); +} + +void database_api_impl::subscribe_to_market(std::function callback, asset_id_type a, asset_id_type b) +{ + if(a > b) std::swap(a,b); + FC_ASSERT(a != b); + _market_subscriptions[ std::make_pair(a,b) ] = callback; +} + +void database_api::unsubscribe_from_market(asset_id_type a, asset_id_type b) +{ + my->unsubscribe_from_market( a, b ); +} + +void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b) +{ + if(a > b) std::swap(a,b); + FC_ASSERT(a != b); + _market_subscriptions.erase(std::make_pair(a,b)); +} + +////////////////////////////////////////////////////////////////////// +// // +// Witnesses // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_witnesses(const vector& witness_ids)const +{ + return my->get_witnesses( witness_ids ); +} + +vector> database_api_impl::get_witnesses(const vector& witness_ids)const +{ + vector> result; result.reserve(witness_ids.size()); + std::transform(witness_ids.begin(), witness_ids.end(), std::back_inserter(result), + [this](witness_id_type id) -> optional { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +fc::optional database_api::get_witness_by_account(account_id_type account)const +{ + return my->get_witness_by_account( account ); +} + +fc::optional database_api_impl::get_witness_by_account(account_id_type account) const +{ + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + if( itr != idx.end() ) + return *itr; + return {}; +} + +map database_api::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const +{ + return my->lookup_witness_accounts( lower_bound_name, limit ); +} + +map database_api_impl::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const +{ + FC_ASSERT( limit <= 1000 ); + const auto& witnesses_by_id = _db.get_index_type().indices().get(); + + // we want to order witnesses by account name, but that name is in the account object + // so the witness_index doesn't have a quick way to access it. + // get all the names and look them all up, sort them, then figure out what + // records to return. This could be optimized, but we expect the + // number of witnesses to be few and the frequency of calls to be rare + std::map witnesses_by_account_name; + for (const witness_object& witness : witnesses_by_id) + if (auto account_iter = _db.find(witness.witness_account)) + if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name + witnesses_by_account_name.insert(std::make_pair(account_iter->name, witness.id)); + + auto end_iter = witnesses_by_account_name.begin(); + while (end_iter != witnesses_by_account_name.end() && limit--) + ++end_iter; + witnesses_by_account_name.erase(end_iter, witnesses_by_account_name.end()); + return witnesses_by_account_name; +} + +uint64_t database_api::get_witness_count()const +{ + return my->get_witness_count(); +} + +uint64_t database_api_impl::get_witness_count()const +{ + return _db.get_index_type().indices().size(); +} + +////////////////////////////////////////////////////////////////////// +// // +// Committee members // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_committee_members(const vector& committee_member_ids)const +{ + return my->get_committee_members( committee_member_ids ); +} + +vector> database_api_impl::get_committee_members(const vector& committee_member_ids)const +{ + vector> result; result.reserve(committee_member_ids.size()); + std::transform(committee_member_ids.begin(), committee_member_ids.end(), std::back_inserter(result), + [this](committee_member_id_type id) -> optional { + if(auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +fc::optional database_api::get_committee_member_by_account(account_id_type account)const +{ + return my->get_committee_member_by_account( account ); +} + +fc::optional database_api_impl::get_committee_member_by_account(account_id_type account) const +{ + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + if( itr != idx.end() ) + return *itr; + return {}; +} + +map database_api::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const +{ + return my->lookup_committee_member_accounts( lower_bound_name, limit ); +} + +map database_api_impl::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const +{ + FC_ASSERT( limit <= 1000 ); + const auto& committee_members_by_id = _db.get_index_type().indices().get(); + + // we want to order committee_members by account name, but that name is in the account object + // so the committee_member_index doesn't have a quick way to access it. + // get all the names and look them all up, sort them, then figure out what + // records to return. This could be optimized, but we expect the + // number of committee_members to be few and the frequency of calls to be rare + std::map committee_members_by_account_name; + for (const committee_member_object& committee_member : committee_members_by_id) + if (auto account_iter = _db.find(committee_member.committee_member_account)) + if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name + committee_members_by_account_name.insert(std::make_pair(account_iter->name, committee_member.id)); + + auto end_iter = committee_members_by_account_name.begin(); + while (end_iter != committee_members_by_account_name.end() && limit--) + ++end_iter; + committee_members_by_account_name.erase(end_iter, committee_members_by_account_name.end()); + return committee_members_by_account_name; +} + +////////////////////////////////////////////////////////////////////// +// // +// Votes // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::lookup_vote_ids( const vector& votes )const +{ + return my->lookup_vote_ids( votes ); +} + +vector database_api_impl::lookup_vote_ids( const vector& votes )const +{ + FC_ASSERT( votes.size() < 100, "Only 100 votes can be queried at a time" ); + + const auto& witness_idx = _db.get_index_type().indices().get(); + const auto& committee_idx = _db.get_index_type().indices().get(); + + vector result; + result.reserve( votes.size() ); + for( auto id : votes ) + { + switch( id.type() ) + { + case vote_id_type::committee: + { + auto itr = committee_idx.find( id ); + if( itr != committee_idx.end() ) + result.emplace_back( variant( *itr ) ); + else + result.emplace_back( variant() ); + break; + } + case vote_id_type::witness: + { + auto itr = witness_idx.find( id ); + if( itr != witness_idx.end() ) + result.emplace_back( variant( *itr ) ); + else + result.emplace_back( variant() ); + break; + } + case vote_id_type::worker: + break; + case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings + } + } + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Authority / validation // +// // +////////////////////////////////////////////////////////////////////// + +std::string database_api::get_transaction_hex(const signed_transaction& trx)const +{ + return my->get_transaction_hex( trx ); +} + +std::string database_api_impl::get_transaction_hex(const signed_transaction& trx)const +{ + return fc::to_hex(fc::raw::pack(trx)); +} + +set database_api::get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const +{ + return my->get_required_signatures( trx, available_keys ); +} + +set database_api_impl::get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const +{ + wdump((trx)(available_keys)); + auto result = trx.get_required_signatures( _db.get_chain_id(), + available_keys, + [&]( account_id_type id ){ return &id(_db).active; }, + [&]( account_id_type id ){ return &id(_db).owner; }, + _db.get_global_properties().parameters.max_authority_depth ); + wdump((result)); + return result; +} + +set database_api::get_potential_signatures( const signed_transaction& trx )const +{ + return my->get_potential_signatures( trx ); +} + +set database_api_impl::get_potential_signatures( const signed_transaction& trx )const +{ + wdump((trx)); + set result; + trx.get_required_signatures( + _db.get_chain_id(), + flat_set(), + [&]( account_id_type id ) + { + const auto& auth = id(_db).active; + for( const auto& k : auth.get_keys() ) + result.insert(k); + return &auth; + }, + [&]( account_id_type id ) + { + const auto& auth = id(_db).owner; + for( const auto& k : auth.get_keys() ) + result.insert(k); + return &auth; + }, + _db.get_global_properties().parameters.max_authority_depth + ); + + wdump((result)); + return result; +} + +bool database_api::verify_authority( const signed_transaction& trx )const +{ + return my->verify_authority( trx ); +} + +bool database_api_impl::verify_authority( const signed_transaction& trx )const +{ + trx.verify_authority( _db.get_chain_id(), + [&]( account_id_type id ){ return &id(_db).active; }, + [&]( account_id_type id ){ return &id(_db).owner; }, + _db.get_global_properties().parameters.max_authority_depth ); + return true; +} + +bool database_api::verify_account_authority( const string& name_or_id, const flat_set& signers )const +{ + return my->verify_account_authority( name_or_id, signers ); +} + +bool database_api_impl::verify_account_authority( const string& name_or_id, const flat_set& keys )const +{ + 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()); + else + { + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(name_or_id); + if (itr != idx.end()) + account = &*itr; + } + FC_ASSERT( account, "no such account" ); + + + /// reuse trx.verify_authority by creating a dummy transfer + signed_transaction trx; + transfer_operation op; + op.from = account->id; + trx.operations.emplace_back(op); + + return verify_authority( trx ); +} + +processed_transaction database_api::validate_transaction( const signed_transaction& trx )const +{ + return my->validate_transaction( trx ); +} + +processed_transaction database_api_impl::validate_transaction( const signed_transaction& trx )const +{ + return _db.validate_transaction(trx); +} + +vector database_api::get_required_fees( const vector& ops, asset_id_type id )const +{ + return my->get_required_fees( ops, id ); +} + +vector database_api_impl::get_required_fees( const vector& ops, asset_id_type id )const +{ + vector result; + result.reserve(ops.size()); + const asset_object& a = id(_db); + for( const auto& op : ops ) + result.push_back( _db.current_fee_schedule().calculate_fee( op, a.options.core_exchange_rate ) ); + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Proposed transactions // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::get_proposed_transactions( account_id_type id )const +{ + return my->get_proposed_transactions( id ); +} + +/** TODO: add secondary index that will accelerate this process */ +vector database_api_impl::get_proposed_transactions( account_id_type id )const +{ + const auto& idx = _db.get_index_type(); + vector result; + + idx.inspect_all_objects( [&](const object& obj){ + const proposal_object& p = static_cast(obj); + if( p.required_active_approvals.find( id ) != p.required_active_approvals.end() ) + result.push_back(p); + else if ( p.required_owner_approvals.find( id ) != p.required_owner_approvals.end() ) + result.push_back(p); + else if ( p.available_active_approvals.find( id ) != p.available_active_approvals.end() ) + result.push_back(p); + }); + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Blinded balances // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::get_blinded_balances( const flat_set& commitments )const +{ + return my->get_blinded_balances( commitments ); +} + +vector database_api_impl::get_blinded_balances( const flat_set& commitments )const +{ + vector result; result.reserve(commitments.size()); + const auto& bal_idx = _db.get_index_type(); + const auto& by_commitment_idx = bal_idx.indices().get(); + for( const auto& c : commitments ) + { + auto itr = by_commitment_idx.find( c ); + if( itr != by_commitment_idx.end() ) + result.push_back( *itr ); + } + return result; +} + +////////////////////////////////////////////////////////////////////// +// // +// Private methods // +// // +////////////////////////////////////////////////////////////////////// + +void database_api_impl::broadcast_updates( const vector& updates ) +{ + if( updates.size() ) { + auto capture_this = shared_from_this(); + fc::async([capture_this,updates](){ + capture_this->_subscribe_callback( fc::variant(updates) ); + }); + } +} + +void database_api_impl::on_objects_removed( const vector& objs ) +{ + /// we need to ensure the database_api is not deleted for the life of the async operation + if( _subscribe_callback ) + { + vector updates; + updates.reserve(objs.size()); + + for( auto obj : objs ) + updates.emplace_back( obj->id ); + broadcast_updates( updates ); + } + + if( _market_subscriptions.size() ) + { + map< pair, vector > broadcast_queue; + for( const auto& obj : objs ) + { + const limit_order_object* order = dynamic_cast(obj); + if( order ) + { + auto sub = _market_subscriptions.find( order->get_market() ); + if( sub != _market_subscriptions.end() ) + broadcast_queue[order->get_market()].emplace_back( order->id ); + } + } + if( broadcast_queue.size() ) + { + auto capture_this = shared_from_this(); + fc::async([capture_this,this,broadcast_queue](){ + for( const auto& item : broadcast_queue ) + { + auto sub = _market_subscriptions.find(item.first); + if( sub != _market_subscriptions.end() ) + sub->second( fc::variant(item.second ) ); + } + }); + } + } +} + +void database_api_impl::on_objects_changed(const vector& ids) +{ + vector updates; + map< pair, vector > market_broadcast_queue; + + for(auto id : ids) + { + const object* obj = nullptr; + if( _subscribe_callback ) + { + obj = _db.find_object( id ); + if( obj ) + { + updates.emplace_back( obj->to_variant() ); + } + else + { + updates.emplace_back(id); // send just the id to indicate removal + } + } + + if( _market_subscriptions.size() ) + { + if( !_subscribe_callback ) + obj = _db.find_object( id ); + if( obj ) + { + const limit_order_object* order = dynamic_cast(obj); + if( order ) + { + auto sub = _market_subscriptions.find( order->get_market() ); + if( sub != _market_subscriptions.end() ) + market_broadcast_queue[order->get_market()].emplace_back( order->id ); + } + } + } + } + + auto capture_this = shared_from_this(); + + /// pushing the future back / popping the prior future if it is complete. + /// if a connection hangs then this could get backed up and result in + /// a failure to exit cleanly. + fc::async([capture_this,this,updates,market_broadcast_queue](){ + if( _subscribe_callback ) _subscribe_callback( updates ); + + for( const auto& item : market_broadcast_queue ) + { + auto sub = _market_subscriptions.find(item.first); + if( sub != _market_subscriptions.end() ) + sub->second( fc::variant(item.second ) ); + } + }); +} + +/** note: this method cannot yield because it is called in the middle of + * apply a block. + */ +void database_api_impl::on_applied_block() +{ + if (_block_applied_callback) + { + auto capture_this = shared_from_this(); + block_id_type block_id = _db.head_block_id(); + fc::async([this,capture_this,block_id](){ + _block_applied_callback(fc::variant(block_id)); + }); + } + + if(_market_subscriptions.size() == 0) + return; + + const auto& ops = _db.get_applied_operations(); + map< std::pair, vector> > subscribed_markets_ops; + for(const auto& op : ops) + { + std::pair market; + switch(op.op.which()) + { + /* This is sent via the object_changed callback + case operation::tag::value: + market = op.op.get().get_market(); + break; + */ + case operation::tag::value: + market = op.op.get().get_market(); + break; + /* + case operation::tag::value: + */ + default: break; + } + if(_market_subscriptions.count(market)) + subscribed_markets_ops[market].push_back(std::make_pair(op.op, op.result)); + } + /// we need to ensure the database_api is not deleted for the life of the async operation + auto capture_this = shared_from_this(); + fc::async([this,capture_this,subscribed_markets_ops](){ + for(auto item : subscribed_markets_ops) + { + auto itr = _market_subscriptions.find(item.first); + if(itr != _market_subscriptions.end()) + itr->second(fc::variant(item.second)); + } + }); +} + +} } // graphene::app diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 8a94706f..c29be561 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -17,398 +17,32 @@ */ #pragma once -#include +#include #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - #include #include #include -#include +#include +#include + +#include + +#include +#include +#include +#include namespace graphene { namespace app { using namespace graphene::chain; using namespace graphene::market_history; + using namespace std; class application; - /** - * @brief The database_api class implements the RPC API for the chain database. - * - * This API exposes accessors on the database which query state tracked by a blockchain validating node. This API is - * read-only; all modifications to the database must be performed via transactions. Transactions are broadcast via - * the @ref network_broadcast_api. - */ - class database_api : public std::enable_shared_from_this - { - public: - database_api(graphene::chain::database& db); - ~database_api(); - /** - * @brief Get the objects corresponding to the provided IDs - * @param ids IDs of the objects to retrieve - * @return The objects retrieved, in the order they are mentioned in ids - * - * If any of the provided IDs does not map to an object, a null variant is returned in its position. - */ - fc::variants get_objects(const vector& ids)const; - /** - * @brief Retrieve a block header - * @param block_num Height of the block whose header should be returned - * @return header of the referenced block, or null if no matching block was found - */ - optional get_block_header(uint32_t block_num)const; - /** - * @brief Retrieve a full, signed block - * @param block_num Height of the block to be returned - * @return the referenced block, or null if no matching block was found - */ - optional get_block(uint32_t block_num)const; - - /** - * @brief used to fetch an individual transaction. - */ - processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; - - /** - * @brief Retrieve the @ref chain_property_object associated with the chain - */ - chain_property_object get_chain_properties()const; - - /** - * @brief Retrieve the current @ref global_property_object - */ - global_property_object get_global_properties()const; - - /** - * @brief Retrieve compile-time constants - */ - fc::variant_object get_config()const; - - /** - * @brief Get the chain ID - */ - chain_id_type get_chain_id()const; - - /** - * @brief Retrieve the current @ref dynamic_global_property_object - */ - dynamic_global_property_object get_dynamic_global_properties()const; - /** - * @brief Get a list of accounts by ID - * @param account_ids IDs of the accounts to retrieve - * @return The accounts corresponding to the provided IDs - * - * This function has semantics identical to @ref get_objects - */ - vector> get_accounts(const vector& account_ids)const; - /** - * @brief Get a list of assets by ID - * @param asset_ids IDs of the assets to retrieve - * @return The assets corresponding to the provided IDs - * - * This function has semantics identical to @ref get_objects - */ - vector> get_assets(const vector& asset_ids)const; - /** - * @brief Get a list of accounts by name - * @param account_names Names of the accounts to retrieve - * @return The accounts holding the provided names - * - * This function has semantics identical to @ref get_objects - */ - vector> lookup_account_names(const vector& account_names)const; - optional get_account_by_name( string name )const; - - /** - * @brief Get a list of assets by symbol - * @param asset_symbols Symbols or stringified IDs of the assets to retrieve - * @return The assets corresponding to the provided symbols or IDs - * - * This function has semantics identical to @ref get_objects - */ - vector> lookup_asset_symbols(const vector& symbols_or_ids)const; - - /** - * @brief Get an account's balances in various assets - * @param id ID of the account to get balances for - * @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in - * @return Balances of the account - */ - vector get_account_balances(account_id_type id, const flat_set& assets)const; - /// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID. - vector get_named_account_balances(const std::string& name, const flat_set& assets)const; - /** - * @brief Get the total number of accounts registered with the blockchain - */ - uint64_t get_account_count()const; - /** - * @brief Get names and IDs for registered accounts - * @param lower_bound_name Lower bound of the first name to return - * @param limit Maximum number of results to return -- must not exceed 1000 - * @return Map of account names to corresponding IDs - */ - map lookup_accounts(const string& lower_bound_name, uint32_t limit)const; - - /** - * @brief Fetch all objects relevant to the specified accounts and subscribe to updates - * @param callback Function to call with updates - * @param names_or_ids Each item must be the name or ID of an account to retrieve - * @return Map of string from @ref names_or_ids to the corresponding account - * - * This function fetches all relevant objects for the given accounts, and subscribes to updates to the given - * accounts. If any of the strings in @ref names_or_ids cannot be tied to an account, that input will be - * ignored. All other accounts will be retrieved and subscribed. - * - */ - std::map get_full_accounts( const vector& names_or_ids, bool subscribe ); - - - /** - * @brief Get limit orders in a given market - * @param a ID of asset being sold - * @param b ID of asset being purchased - * @param limit Maximum number of orders to retrieve - * @return The limit orders, ordered from least price to greatest - */ - vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; - /** - * @brief Get call orders in a given asset - * @param a ID of asset being called - * @param limit Maximum number of orders to retrieve - * @return The call orders, ordered from earliest to be called to latest - */ - vector get_call_orders(asset_id_type a, uint32_t limit)const; - /** - * @brief Get forced settlement orders in a given asset - * @param a ID of asset being settled - * @param limit Maximum number of orders to retrieve - * @return The settle orders, ordered from earliest settlement date to latest - */ - vector get_settle_orders(asset_id_type a, uint32_t limit)const; - - /** - * @brief Get assets alphabetically by symbol name - * @param lower_bound_symbol Lower bound of symbol names to retrieve - * @param limit Maximum number of assets to fetch (must not exceed 100) - * @return The assets found - */ - vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; - - /** - * @brief Get the committee_member owned by a given account - * @param account The ID of the account whose committee_member should be retrieved - * @return The committee_member object, or null if the account does not have a committee_member - */ - fc::optional get_committee_member_by_account(account_id_type account)const; - /** - * @brief Get the witness owned by a given account - * @param account The ID of the account whose witness should be retrieved - * @return The witness object, or null if the account does not have a witness - */ - fc::optional get_witness_by_account(account_id_type account)const; - - /** - * @brief Given a set of votes, return the objects they are voting for. - * - * This will be a mixture of committee_member_object, witness_objects, and worker_objects - * - * The results will be in the same order as the votes. Null will be returned for - * any vote ids that are not found. - */ - vector lookup_vote_ids( const vector& votes )const; - - /** - * @brief Get the total number of witnesses registered with the blockchain - */ - uint64_t get_witness_count()const; - - /** - * @brief Get names and IDs for registered witnesses - * @param lower_bound_name Lower bound of the first name to return - * @param limit Maximum number of results to return -- must not exceed 1000 - * @return Map of witness names to corresponding IDs - */ - map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; - - /** - * @brief Get names and IDs for registered committee_members - * @param lower_bound_name Lower bound of the first name to return - * @param limit Maximum number of results to return -- must not exceed 1000 - * @return Map of committee_member names to corresponding IDs - */ - map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; - - /** - * @brief Get a list of witnesses by ID - * @param witness_ids IDs of the witnesses to retrieve - * @return The witnesses corresponding to the provided IDs - * - * This function has semantics identical to @ref get_objects - */ - vector> get_witnesses(const vector& witness_ids)const; - - /** - * @brief Get a list of committee_members by ID - * @param committee_member_ids IDs of the committee_members to retrieve - * @return The committee_members corresponding to the provided IDs - * - * This function has semantics identical to @ref get_objects - */ - vector> get_committee_members(const vector& committee_member_ids)const; - - /** - * @brief Request notification when the active orders in the market between two assets changes - * @param callback Callback method which is called when the market changes - * @param a First asset ID - * @param b Second asset ID - * - * Callback will be passed a variant containing a vector>. The vector will - * contain, in order, the operations which changed the market, and their results. - */ - void subscribe_to_market(std::function callback, - asset_id_type a, asset_id_type b); - /** - * @brief Unsubscribe from updates to a given market - * @param a First asset ID - * @param b Second asset ID - */ - void unsubscribe_from_market(asset_id_type a, asset_id_type b); - /** - * @brief Stop receiving any notifications - * - * This unsubscribes from all subscribed markets and objects. - */ - void cancel_all_subscriptions() - { set_subscribe_callback( std::function(), true); _market_subscriptions.clear(); } - - /// @brief Get a hexdump of the serialized binary form of a transaction - std::string get_transaction_hex(const signed_transaction& trx)const; - - /** - * @return the set of proposed transactions relevant to the specified account id. - */ - vector get_proposed_transactions( account_id_type id )const; - - /** - * @return all accounts that referr to the key or account id in their owner or active authorities. - */ - vector get_account_references( account_id_type account_id )const; - vector> get_key_references( vector key )const; - - /** - * @return all open margin positions for a given account id. - */ - vector get_margin_positions( const account_id_type& id )const; - - /** @return all unclaimed balance objects for a set of addresses */ - vector get_balance_objects( const vector
& addrs )const; - - vector get_vested_balances( const vector& objs )const; - - vector get_vesting_balances( account_id_type account_id )const; - - /** - * This API will take a partially signed transaction and a set of public keys that the owner has the ability to sign for - * and return the minimal subset of public keys that should add signatures to the transaction. - */ - set get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const; - - /** - * This method will return the set of all public keys that could possibly sign for a given transaction. This call can - * be used by wallets to filter their set of public keys to just the relevant subset prior to calling @ref get_required_signatures - * to get the minimum subset. - */ - set get_potential_signatures( const signed_transaction& trx )const; - - /** - * @return true of the @ref trx has all of the required signatures, otherwise throws an exception - */ - bool verify_authority( const signed_transaction& trx )const; - - /** - * @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; - - /** - * Validates a transaction against the current state without broadcast it on the network. - */ - processed_transaction validate_transaction( const signed_transaction& trx )const; - - - /** - * @return the set of blinded balance objects by commitment ID - */ - vector get_blinded_balances( const flat_set& commitments )const; - - - /** - * For each operation calculate the required fee in the specified asset type. If the asset type does - * not have a valid core_exchange_rate - */ - vector get_required_fees( const vector& ops, asset_id_type id = asset_id_type() )const; - - void set_subscribe_callback( std::function cb, bool clear_filter ); - void set_pending_transaction_callback( std::function cb ){ _pending_trx_callback = cb; } - void set_block_applied_callback( std::function cb ){ _block_applied_callback = cb; } - private: - template - void subscribe_to_item( const T& i )const - { - auto vec = fc::raw::pack(i); - if( !_subscribe_callback ) return; - - if( !is_subscribed_to_item(i) ) - { - idump((i)); - _subscribe_filter.insert( vec.data(), vec.size() );//(vecconst char*)&i, sizeof(i) ); - } - } - - template - bool is_subscribed_to_item( const T& i )const - { - if( !_subscribe_callback ) return false; - return true; - return _subscribe_filter.contains( i ); - } - void broadcast_updates( const vector& updates ); - - /** called every time a block is applied to report the objects that were changed */ - void on_objects_changed(const vector& ids); - void on_objects_removed(const vector& objs); - void on_applied_block(); - - mutable fc::bloom_filter _subscribe_filter; - std::function _subscribe_callback; - std::function _pending_trx_callback; - std::function _block_applied_callback; - - boost::signals2::scoped_connection _change_connection; - boost::signals2::scoped_connection _removed_connection; - boost::signals2::scoped_connection _applied_block_connection; - boost::signals2::scoped_connection _pending_trx_connection; - map< pair, std::function > _market_subscriptions; - graphene::chain::database& _db; - }; - /** * @brief The history_api class implements the RPC API for account history * @@ -558,59 +192,6 @@ namespace graphene { namespace app { FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation, (id)(block_num)(trx_num)(trx) ) -FC_API(graphene::app::database_api, - (get_objects) - (get_block_header) - (get_block) - (get_transaction) - (get_chain_properties) - (get_global_properties) - (get_chain_id) - (get_dynamic_global_properties) - (get_accounts) - (get_assets) - (lookup_account_names) - (get_account_by_name) - (get_account_count) - (lookup_accounts) - (get_full_accounts) - (get_account_balances) - (get_named_account_balances) - (lookup_asset_symbols) - (get_limit_orders) - (get_call_orders) - (get_settle_orders) - (list_assets) - (get_committee_member_by_account) - (get_witnesses) - (get_committee_members) - (get_witness_by_account) - (lookup_vote_ids) - (get_witness_count) - (lookup_witness_accounts) - (lookup_committee_member_accounts) - (subscribe_to_market) - (unsubscribe_from_market) - (cancel_all_subscriptions) - (get_transaction_hex) - (get_proposed_transactions) - (get_account_references) - (get_key_references) - (get_margin_positions) - (get_balance_objects) - (get_vested_balances) - (get_vesting_balances) - (get_required_signatures) - (get_potential_signatures) - (verify_authority) - (verify_account_authority) - (get_blinded_balances) - (get_required_fees) - (set_subscribe_callback) - (set_pending_transaction_callback) - (set_block_applied_callback) - (validate_transaction) - ) FC_API(graphene::app::history_api, (get_account_history) (get_market_history) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp new file mode 100644 index 00000000..8975fa1e --- /dev/null +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2015, Cryptonomex, Inc. + * All rights reserved. + * + * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and + * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, + * are permitted until September 8, 2015, provided that the following conditions are met: + * + * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace graphene { namespace app { + +using namespace graphene::chain; +using namespace std; + +class database_api_impl; + +/** + * @brief The database_api class implements the RPC API for the chain database. + * + * This API exposes accessors on the database which query state tracked by a blockchain validating node. This API is + * read-only; all modifications to the database must be performed via transactions. Transactions are broadcast via + * the @ref network_broadcast_api. + */ +class database_api +{ + public: + database_api(graphene::chain::database& db); + ~database_api(); + + ///////////// + // Objects // + ///////////// + + /** + * @brief Get the objects corresponding to the provided IDs + * @param ids IDs of the objects to retrieve + * @return The objects retrieved, in the order they are mentioned in ids + * + * If any of the provided IDs does not map to an object, a null variant is returned in its position. + */ + fc::variants get_objects(const vector& ids)const; + + /////////////////// + // Subscriptions // + /////////////////// + + void set_subscribe_callback( std::function cb, bool clear_filter ); + void set_pending_transaction_callback( std::function cb ); + void set_block_applied_callback( std::function cb ); + /** + * @brief Stop receiving any notifications + * + * This unsubscribes from all subscribed markets and objects. + */ + void cancel_all_subscriptions(); + + ///////////////////////////// + // Blocks and transactions // + ///////////////////////////// + + /** + * @brief Retrieve a block header + * @param block_num Height of the block whose header should be returned + * @return header of the referenced block, or null if no matching block was found + */ + optional get_block_header(uint32_t block_num)const; + + /** + * @brief Retrieve a full, signed block + * @param block_num Height of the block to be returned + * @return the referenced block, or null if no matching block was found + */ + optional get_block(uint32_t block_num)const; + + /** + * @brief used to fetch an individual transaction. + */ + processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; + + ///////////// + // Globals // + ///////////// + + /** + * @brief Retrieve the @ref chain_property_object associated with the chain + */ + chain_property_object get_chain_properties()const; + + /** + * @brief Retrieve the current @ref global_property_object + */ + global_property_object get_global_properties()const; + + /** + * @brief Retrieve compile-time constants + */ + fc::variant_object get_config()const; + + /** + * @brief Get the chain ID + */ + chain_id_type get_chain_id()const; + + /** + * @brief Retrieve the current @ref dynamic_global_property_object + */ + dynamic_global_property_object get_dynamic_global_properties()const; + + ////////// + // Keys // + ////////// + + vector> get_key_references( vector key )const; + + ////////////// + // Accounts // + ////////////// + + /** + * @brief Get a list of accounts by ID + * @param account_ids IDs of the accounts to retrieve + * @return The accounts corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_accounts(const vector& account_ids)const; + + /** + * @brief Fetch all objects relevant to the specified accounts and subscribe to updates + * @param callback Function to call with updates + * @param names_or_ids Each item must be the name or ID of an account to retrieve + * @return Map of string from @ref names_or_ids to the corresponding account + * + * This function fetches all relevant objects for the given accounts, and subscribes to updates to the given + * accounts. If any of the strings in @ref names_or_ids cannot be tied to an account, that input will be + * ignored. All other accounts will be retrieved and subscribed. + * + */ + std::map get_full_accounts( const vector& names_or_ids, bool subscribe ); + + optional get_account_by_name( string name )const; + + /** + * @return all accounts that referr to the key or account id in their owner or active authorities. + */ + vector get_account_references( account_id_type account_id )const; + + /** + * @brief Get a list of accounts by name + * @param account_names Names of the accounts to retrieve + * @return The accounts holding the provided names + * + * This function has semantics identical to @ref get_objects + */ + vector> lookup_account_names(const vector& account_names)const; + + /** + * @brief Get names and IDs for registered accounts + * @param lower_bound_name Lower bound of the first name to return + * @param limit Maximum number of results to return -- must not exceed 1000 + * @return Map of account names to corresponding IDs + */ + map lookup_accounts(const string& lower_bound_name, uint32_t limit)const; + + ////////////// + // Balances // + ////////////// + + /** + * @brief Get an account's balances in various assets + * @param id ID of the account to get balances for + * @param assets IDs of the assets to get balances of; if empty, get all assets account has a balance in + * @return Balances of the account + */ + vector get_account_balances(account_id_type id, const flat_set& assets)const; + + /// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID. + vector get_named_account_balances(const std::string& name, const flat_set& assets)const; + + /** @return all unclaimed balance objects for a set of addresses */ + vector get_balance_objects( const vector
& addrs )const; + + vector get_vested_balances( const vector& objs )const; + + vector get_vesting_balances( account_id_type account_id )const; + + /** + * @brief Get the total number of accounts registered with the blockchain + */ + uint64_t get_account_count()const; + + //////////// + // Assets // + //////////// + + /** + * @brief Get a list of assets by ID + * @param asset_ids IDs of the assets to retrieve + * @return The assets corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_assets(const vector& asset_ids)const; + + /** + * @brief Get assets alphabetically by symbol name + * @param lower_bound_symbol Lower bound of symbol names to retrieve + * @param limit Maximum number of assets to fetch (must not exceed 100) + * @return The assets found + */ + vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; + + /** + * @brief Get a list of assets by symbol + * @param asset_symbols Symbols or stringified IDs of the assets to retrieve + * @return The assets corresponding to the provided symbols or IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> lookup_asset_symbols(const vector& symbols_or_ids)const; + + ///////////////////// + // Markets / feeds // + ///////////////////// + + /** + * @brief Get limit orders in a given market + * @param a ID of asset being sold + * @param b ID of asset being purchased + * @param limit Maximum number of orders to retrieve + * @return The limit orders, ordered from least price to greatest + */ + vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; + + /** + * @brief Get call orders in a given asset + * @param a ID of asset being called + * @param limit Maximum number of orders to retrieve + * @return The call orders, ordered from earliest to be called to latest + */ + vector get_call_orders(asset_id_type a, uint32_t limit)const; + + /** + * @brief Get forced settlement orders in a given asset + * @param a ID of asset being settled + * @param limit Maximum number of orders to retrieve + * @return The settle orders, ordered from earliest settlement date to latest + */ + vector get_settle_orders(asset_id_type a, uint32_t limit)const; + + /** + * @return all open margin positions for a given account id. + */ + vector get_margin_positions( const account_id_type& id )const; + + /** + * @brief Request notification when the active orders in the market between two assets changes + * @param callback Callback method which is called when the market changes + * @param a First asset ID + * @param b Second asset ID + * + * Callback will be passed a variant containing a vector>. The vector will + * contain, in order, the operations which changed the market, and their results. + */ + void subscribe_to_market(std::function callback, + asset_id_type a, asset_id_type b); + + /** + * @brief Unsubscribe from updates to a given market + * @param a First asset ID + * @param b Second asset ID + */ + void unsubscribe_from_market(asset_id_type a, asset_id_type b); + + /////////////// + // Witnesses // + /////////////// + + /** + * @brief Get a list of witnesses by ID + * @param witness_ids IDs of the witnesses to retrieve + * @return The witnesses corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_witnesses(const vector& witness_ids)const; + + /** + * @brief Get the witness owned by a given account + * @param account The ID of the account whose witness should be retrieved + * @return The witness object, or null if the account does not have a witness + */ + fc::optional get_witness_by_account(account_id_type account)const; + + /** + * @brief Get names and IDs for registered witnesses + * @param lower_bound_name Lower bound of the first name to return + * @param limit Maximum number of results to return -- must not exceed 1000 + * @return Map of witness names to corresponding IDs + */ + map lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const; + + /** + * @brief Get the total number of witnesses registered with the blockchain + */ + uint64_t get_witness_count()const; + + /////////////////////// + // Committee members // + /////////////////////// + + /** + * @brief Get a list of committee_members by ID + * @param committee_member_ids IDs of the committee_members to retrieve + * @return The committee_members corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_committee_members(const vector& committee_member_ids)const; + + /** + * @brief Get the committee_member owned by a given account + * @param account The ID of the account whose committee_member should be retrieved + * @return The committee_member object, or null if the account does not have a committee_member + */ + fc::optional get_committee_member_by_account(account_id_type account)const; + + /** + * @brief Get names and IDs for registered committee_members + * @param lower_bound_name Lower bound of the first name to return + * @param limit Maximum number of results to return -- must not exceed 1000 + * @return Map of committee_member names to corresponding IDs + */ + map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; + + /////////// + // Votes // + /////////// + + /** + * @brief Given a set of votes, return the objects they are voting for. + * + * This will be a mixture of committee_member_object, witness_objects, and worker_objects + * + * The results will be in the same order as the votes. Null will be returned for + * any vote ids that are not found. + */ + vector lookup_vote_ids( const vector& votes )const; + + //////////////////////////// + // Authority / validation // + //////////////////////////// + + /// @brief Get a hexdump of the serialized binary form of a transaction + std::string get_transaction_hex(const signed_transaction& trx)const; + + /** + * This API will take a partially signed transaction and a set of public keys that the owner has the ability to sign for + * and return the minimal subset of public keys that should add signatures to the transaction. + */ + set get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const; + + /** + * This method will return the set of all public keys that could possibly sign for a given transaction. This call can + * be used by wallets to filter their set of public keys to just the relevant subset prior to calling @ref get_required_signatures + * to get the minimum subset. + */ + set get_potential_signatures( const signed_transaction& trx )const; + + /** + * @return true of the @ref trx has all of the required signatures, otherwise throws an exception + */ + bool verify_authority( const signed_transaction& trx )const; + + /** + * @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; + + /** + * Validates a transaction against the current state without broadcasting it on the network. + */ + processed_transaction validate_transaction( const signed_transaction& trx )const; + + /** + * For each operation calculate the required fee in the specified asset type. If the asset type does + * not have a valid core_exchange_rate + */ + vector get_required_fees( const vector& ops, asset_id_type id )const; + + /////////////////////////// + // Proposed transactions // + /////////////////////////// + + /** + * @return the set of proposed transactions relevant to the specified account id. + */ + vector get_proposed_transactions( account_id_type id )const; + + ////////////////////// + // Blinded balances // + ////////////////////// + + /** + * @return the set of blinded balance objects by commitment ID + */ + vector get_blinded_balances( const flat_set& commitments )const; + + private: + std::shared_ptr< database_api_impl > my; +}; + +} } + +FC_API(graphene::app::database_api, + // Objects + (get_objects) + + // Subscriptions + (set_subscribe_callback) + (set_pending_transaction_callback) + (set_block_applied_callback) + (cancel_all_subscriptions) + + // Blocks and transactions + (get_block_header) + (get_block) + (get_transaction) + + // Globals + (get_chain_properties) + (get_global_properties) + (get_config) + (get_chain_id) + (get_dynamic_global_properties) + + // Keys + (get_key_references) + + // Accounts + (get_accounts) + (get_full_accounts) + (get_account_by_name) + (get_account_references) + (lookup_account_names) + (lookup_accounts) + (get_account_count) + + // Balances + (get_account_balances) + (get_named_account_balances) + (get_balance_objects) + (get_vested_balances) + (get_vesting_balances) + + // Assets + (get_assets) + (list_assets) + (lookup_asset_symbols) + + // Markets / feeds + (get_limit_orders) + (get_call_orders) + (get_settle_orders) + (get_margin_positions) + (subscribe_to_market) + (unsubscribe_from_market) + + // Witnesses + (get_witnesses) + (get_witness_by_account) + (lookup_witness_accounts) + (get_witness_count) + + // Committee members + (get_committee_members) + (get_committee_member_by_account) + (lookup_committee_member_accounts) + + // Votes + (lookup_vote_ids) + + // Authority / validation + (get_transaction_hex) + (get_required_signatures) + (get_potential_signatures) + (verify_authority) + (verify_account_authority) + (validate_transaction) + (get_required_fees) + + // Proposed transactions + (get_proposed_transactions) + + // Blinded balances + (get_blinded_balances) +) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 55122d12..6f7a7623 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -588,7 +588,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) // Set active witnesses modify(get_global_properties(), [&](global_property_object& p) { - for( int i = 1; i <= genesis_state.initial_active_witnesses; ++i ) + for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i ) { p.active_witnesses.insert(i); p.witness_accounts.insert(get(witness_id_type(i)).witness_account); diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 824e64ae..dd87a9b4 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -118,7 +118,7 @@ void database::pay_workers( share_type& budget ) return wa.id < wb.id; }); - for( int i = 0; i < active_workers.size() && budget > 0; ++i ) + for( uint32_t i = 0; i < active_workers.size() && budget > 0; ++i ) { const worker_object& active_worker = active_workers[i]; share_type requested_pay = active_worker.daily_pay; diff --git a/libraries/chain/include/graphene/chain/protocol/address.hpp b/libraries/chain/include/graphene/chain/protocol/address.hpp index 39f80800..ca264084 100644 --- a/libraries/chain/include/graphene/chain/protocol/address.hpp +++ b/libraries/chain/include/graphene/chain/protocol/address.hpp @@ -56,7 +56,12 @@ namespace graphene { namespace chain { explicit operator std::string()const; ///< converts to base58 + checksum - friend size_t hash_value( const address& v ) { return *((size_t*)&v.addr._hash[2]); } + friend size_t hash_value( const address& v ) { + const void* tmp = static_cast(v.addr._hash+2); + + const size_t* tmp2 = reinterpret_cast(tmp); + return *tmp2; + } fc::ripemd160 addr; }; inline bool operator == ( const address& a, const address& b ) { return a.addr == b.addr; } diff --git a/libraries/chain/transaction_evaluation_state.cpp b/libraries/chain/transaction_evaluation_state.cpp deleted file mode 100644 index f6b4fc70..00000000 --- a/libraries/chain/transaction_evaluation_state.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2015, Cryptonomex, Inc. - * All rights reserved. - * - * This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and - * the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification, - * are permitted until September 8, 2015, provided that the following conditions are met: - * - * 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include -#include -#include -#include -#include -#include - -namespace graphene { namespace chain { - /* - bool transaction_evaluation_state::check_authority( const account_object& account, authority::classification auth_class, int depth ) - { - if( (!_is_proposed_trx) && (_db->get_node_properties().skip_flags & database::skip_authority_check) ) - return true; - if( (!_is_proposed_trx) && (_db->get_node_properties().skip_flags & database::skip_transaction_signatures) ) - return true; - if( account.get_id() == GRAPHENE_TEMP_ACCOUNT || - approved_by.find(make_pair(account.id, auth_class)) != approved_by.end() ) - return true; - - FC_ASSERT( account.id.instance() != 0 || _is_proposed_trx, "", ("account",account)("is_proposed",_is_proposed_trx) ); - - bool valid = false; - switch( auth_class ) - { - case authority::owner: - valid = check_authority( account.owner, auth_class, depth ); - break; - case authority::active: - valid = check_authority( account.active, auth_class, depth ); - break; - default: - FC_ASSERT( false, "Invalid Account Auth Class" ); - }; - if( valid ) - approved_by.insert( std::make_pair(account.id, auth_class) ); - return valid; - } - - bool transaction_evaluation_state::check_authority( const authority& au, authority::classification auth_class, int depth ) - { try { - if( (!_is_proposed_trx) && (_db->get_node_properties().skip_flags & database::skip_authority_check) ) - return true; - if( (!_is_proposed_trx) && (_db->get_node_properties().skip_flags & database::skip_transaction_signatures) ) - return true; - - uint32_t total_weight = 0; - - for( const auto& key : au.key_auths ) - { - if( signed_by( key.first ) ) - { - total_weight += key.second; - if( total_weight >= au.weight_threshold ) - return true; - } - } - for( const auto& key : au.address_auths ) - { - if( signed_by( key.first ) ) - { - total_weight += key.second; - if( total_weight >= au.weight_threshold ) - return true; - } - } - - for( const auto& auth : au.account_auths ) - { - if( approved_by.find( std::make_pair(auth.first,auth_class) ) != approved_by.end() ) - { - total_weight += auth.second; - if( total_weight >= au.weight_threshold ) - return true; - } - else - { - if( depth == GRAPHENE_MAX_SIG_CHECK_DEPTH ) - { - //elog("Failing authority verification due to recursion depth."); - return false; - } - const account_object& acnt = auth.first(*_db); - if( check_authority( acnt, auth_class, depth + 1 ) ) - { - approved_by.insert( std::make_pair(acnt.id,auth_class) ); - total_weight += auth.second; - if( total_weight >= au.weight_threshold ) - return true; - } - } - } - - return total_weight >= au.weight_threshold; - } FC_CAPTURE_AND_RETHROW( (au)(auth_class)(depth) ) } - - bool transaction_evaluation_state::signed_by(const public_key_type& k) - { - auto itr = _sigs.find(k); - return itr != _sigs.end() && (itr->second = true); - } - - bool transaction_evaluation_state::signed_by(const address& k) - { - for( auto itr = _sigs.begin(); itr != _sigs.end(); ++itr ) - if( itr->first == k ) return itr->second = true; - return false; - } - */ - -} } // namespace graphene::chain diff --git a/libraries/fc b/libraries/fc index 19e42ac4..483b3488 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 19e42ac4c41d0a2bbdc8094c2efeed5e28e0ed75 +Subproject commit 483b348878f284c474511db05e120466ffbfc132