diff --git a/.gitmodules b/.gitmodules index ebd9398a..76da68c0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ [submodule "docs"] - path = docs - url = https://github.com/cryptonomex/graphene.wiki.git - ignore = dirty + path = docs + url = https://github.com/bitshares/bitshares-core.wiki.git + ignore = dirty [submodule "libraries/fc"] - path = libraries/fc - url = git@bitbucket.org:peerplaysblockchain/peerplays-fc.git - ignore = dirty + path = libraries/fc + url = git@bitbucket.org:peerplaysblockchain/peerplays-fc.git + ignore = dirty diff --git a/docs b/docs index 8d8b69d8..cc4a57cf 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f +Subproject commit cc4a57cf18161d591bb2ad23a2cfa630fa2f9915 diff --git a/gui_version b/gui_version index 034bff7a..5f25c56d 100644 --- a/gui_version +++ b/gui_version @@ -1 +1 @@ -2.0.161031 +2.0.170224 diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index c96d4807..fe526354 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -79,6 +79,10 @@ namespace graphene { namespace app { { _database_api = std::make_shared< database_api >( std::ref( *_app.chain_database() ) ); } + else if( api_name == "block_api" ) + { + _block_api = std::make_shared< block_api >( std::ref( *_app.chain_database() ) ); + } else if( api_name == "network_broadcast_api" ) { _network_broadcast_api = std::make_shared< network_broadcast_api >( std::ref( _app ) ); @@ -95,6 +99,10 @@ namespace graphene { namespace app { { _crypto_api = std::make_shared< crypto_api >(); } + else if( api_name == "asset_api" ) + { + _asset_api = std::make_shared< asset_api >( std::ref( *_app.chain_database() ) ); + } else if( api_name == "debug_api" ) { // can only enable this API if the plugin was loaded @@ -104,6 +112,20 @@ namespace graphene { namespace app { return; } + // block_api + block_api::block_api(graphene::chain::database& db) : _db(db) { } + block_api::~block_api() { } + + vector> block_api::get_blocks(uint32_t block_num_from, uint32_t block_num_to)const + { + FC_ASSERT( block_num_to >= block_num_from ); + vector> res; + for(uint32_t block_num=block_num_from; block_num<=block_num_to; block_num++) { + res.push_back(_db.fetch_block_by_number(block_num)); + } + return res; + } + network_broadcast_api::network_broadcast_api(application& a):_app(a) { _applied_block_connection = _app.chain_database()->applied_block.connect([this](const signed_block& b){ on_applied_block(b); }); @@ -193,6 +215,12 @@ namespace graphene { namespace app { return *_network_broadcast_api; } + fc::api login_api::block()const + { + FC_ASSERT(_block_api); + return *_block_api; + } + fc::api login_api::network_node()const { FC_ASSERT(_network_node_api); @@ -217,6 +245,12 @@ namespace graphene { namespace app { return *_crypto_api; } + fc::api login_api::asset() const + { + FC_ASSERT(_asset_api); + return *_asset_api; + } + fc::api login_api::debug() const { FC_ASSERT(_debug_api); @@ -437,6 +471,37 @@ namespace graphene { namespace app { return result; } + vector history_api::get_account_history_operations( account_id_type account, + int operation_id, + operation_history_id_type start, + operation_history_id_type stop, + unsigned limit) const + { + FC_ASSERT( _app.chain_database() ); + const auto& db = *_app.chain_database(); + FC_ASSERT( limit <= 100 ); + vector result; + const auto& stats = account(db).statistics(db); + if( stats.most_recent_op == account_transaction_history_id_type() ) return result; + const account_transaction_history_object* node = &stats.most_recent_op(db); + if( start == operation_history_id_type() ) + start = node->operation_id; + + while(node && node->operation_id.instance.value > stop.instance.value && result.size() < limit) + { + if( node->operation_id.instance.value <= start.instance.value ) { + + if(node->operation_id(db).op.which() == operation_id) + result.push_back( node->operation_id(db) ); + } + if( node->next == account_transaction_history_id_type() ) + node = nullptr; + else node = &node->next(db); + } + return result; + } + + vector history_api::get_relative_account_history( account_id_type account, uint32_t stop, unsigned limit, @@ -568,4 +633,69 @@ namespace graphene { namespace app { return fc::ecc::range_get_info( proof ); } + // asset_api + asset_api::asset_api(graphene::chain::database& db) : _db(db) { } + asset_api::~asset_api() { } + + vector asset_api::get_asset_holders( asset_id_type asset_id ) const { + + const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); + auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); + + vector result; + + for( const account_balance_object& bal : boost::make_iterator_range( range.first, range.second ) ) + { + if( bal.balance.value == 0 ) continue; + + auto account = _db.find(bal.owner); + + account_asset_balance aab; + aab.name = account->name; + aab.account_id = account->id; + aab.amount = bal.balance.value; + + result.push_back(aab); + } + + return result; + } + // get number of asset holders. + int asset_api::get_asset_holders_count( asset_id_type asset_id ) const { + + const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); + auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); + + int count = boost::distance(range) - 1; + + return count; + } + // function to get vector of system assets with holders count. + vector asset_api::get_all_asset_holders() const { + + vector result; + + vector total_assets; + for( const asset_object& asset_obj : _db.get_index_type().indices() ) + { + const auto& dasset_obj = asset_obj.dynamic_asset_data_id(_db); + + asset_id_type asset_id; + asset_id = dasset_obj.id; + + const auto& bal_idx = _db.get_index_type< account_balance_index >().indices().get< by_asset_balance >(); + auto range = bal_idx.equal_range( boost::make_tuple( asset_id ) ); + + int count = boost::distance(range) - 1; + + asset_holders ah; + ah.asset_id = asset_id; + ah.count = count; + + result.push_back(ah); + } + + return result; + } + } } // graphene::app diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 823a5bfe..4c965082 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -46,10 +46,12 @@ #include #include #include +#include #include #include #include +#include #include @@ -174,7 +176,8 @@ namespace detail { "seed04.bitsharesnodes.com:1776", // Thom "seed05.bitsharesnodes.com:1776", // Thom "seed06.bitsharesnodes.com:1776", // Thom - "seed07.bitsharesnodes.com:1776" // Thom + "seed07.bitsharesnodes.com:1776", // Thom + "51.15.61.160:1776" // lafona }; for( const string& endpoint_string : seeds ) { @@ -232,21 +235,46 @@ namespace detail { FC_CAPTURE_AND_RETHROW((endpoint_string)) } + void new_connection( const fc::http::websocket_connection_ptr& c ) + { + auto wsc = std::make_shared(*c); + auto login = std::make_shared( std::ref(*_self) ); + login->enable_api("database_api"); + + wsc->register_api(login->database()); + wsc->register_api(fc::api(login)); + c->set_session_data( wsc ); + + std::string username = "*"; + std::string password = "*"; + + // Try to extract login information from "Authorization" header if present + std::string auth = c->get_request_header("Authorization"); + if( boost::starts_with(auth, "Basic ") ) { + + FC_ASSERT( auth.size() > 6 ); + auto user_pass = fc::base64_decode(auth.substr(6)); + + std::vector parts; + boost::split( parts, user_pass, boost::is_any_of(":") ); + + FC_ASSERT(parts.size() == 2); + + username = parts[0]; + password = parts[1]; + } + + login->login(username, password); + } + void reset_websocket_server() { try { if( !_options->count("rpc-endpoint") ) return; _websocket_server = std::make_shared(); + _websocket_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) ); - _websocket_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ - auto wsc = std::make_shared(*c); - auto login = std::make_shared( std::ref(*_self) ); - auto db_api = std::make_shared( std::ref(*_self->chain_database()) ); - wsc->register_api(fc::api(db_api)); - wsc->register_api(fc::api(login)); - c->set_session_data( wsc ); - }); ilog("Configured websocket rpc to listen on ${ip}", ("ip",_options->at("rpc-endpoint").as())); _websocket_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-endpoint").as()) ); _websocket_server->start_accept(); @@ -265,15 +293,8 @@ namespace detail { string password = _options->count("server-pem-password") ? _options->at("server-pem-password").as() : ""; _websocket_tls_server = std::make_shared( _options->at("server-pem").as(), password ); + _websocket_tls_server->on_connection( std::bind(&application_impl::new_connection, this, std::placeholders::_1) ); - _websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ - auto wsc = std::make_shared(*c); - auto login = std::make_shared( std::ref(*_self) ); - auto db_api = std::make_shared( std::ref(*_self->chain_database()) ); - wsc->register_api(fc::api(db_api)); - wsc->register_api(fc::api(login)); - c->set_session_data( wsc ); - }); ilog("Configured websocket TLS rpc to listen on ${ip}", ("ip",_options->at("rpc-tls-endpoint").as())); _websocket_tls_server->listen( fc::ip::endpoint::from_string(_options->at("rpc-tls-endpoint").as()) ); _websocket_tls_server->start_accept(); diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index d8c410ff..9bf3bfee 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -76,6 +76,7 @@ class database_api_impl : public std::enable_shared_from_this // Keys vector> get_key_references( vector key )const; + bool is_public_key_registered(string public_key) const; // Accounts vector> get_accounts(const vector& account_ids)const; @@ -503,6 +504,35 @@ vector> database_api_impl::get_key_references( vectoris_public_key_registered(public_key); +} + +bool database_api_impl::is_public_key_registered(string public_key) const +{ + // Short-circuit + if (public_key.empty()) { + return false; + } + + // Search among all keys using an existing map of *current* account keys + public_key_type key; + try { + key = public_key_type(public_key); + } catch ( ... ) { + // An invalid public key was detected + return false; + } + 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); + bool is_known = itr != refs.account_to_key_memberships.end(); + + return is_known; +} + ////////////////////////////////////////////////////////////////////// // // // Accounts // @@ -618,6 +648,22 @@ std::map database_api_impl::get_full_accounts( const [&acnt] (const call_order_object& call) { acnt.call_orders.emplace_back(call); }); + + // get assets issued by user + auto asset_range = _db.get_index_type().indices().get().equal_range(account->id); + std::for_each(asset_range.first, asset_range.second, + [&acnt] (const asset_object& asset) { + acnt.assets.emplace_back(asset.id); + }); + + // get withdraws permissions + auto withdraw_range = _db.get_index_type().indices().get().equal_range(account->id); + std::for_each(withdraw_range.first, withdraw_range.second, + [&acnt] (const withdraw_permission_object& withdraw) { + acnt.withdraws.emplace_back(withdraw); + }); + + results[account_name_or_id] = acnt; } return results; diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index eef2b6d9..be511d3a 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -71,6 +71,18 @@ namespace graphene { namespace app { string message_out; }; + struct account_asset_balance + { + string name; + account_id_type account_id; + share_type amount; + }; + struct asset_holders + { + asset_id_type asset_id; + int count; + }; + /** * @brief The history_api class implements the RPC API for account history * @@ -93,6 +105,22 @@ namespace graphene { namespace app { operation_history_id_type stop = operation_history_id_type(), unsigned limit = 100, operation_history_id_type start = operation_history_id_type())const; + + /** + * @brief Get only asked operations relevant to the specified account + * @param account The account whose history should be queried + * @param operation_id The ID of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...) + * @param stop ID of the earliest operation to retrieve + * @param limit Maximum number of operations to retrieve (must not exceed 100) + * @param start ID of the most recent operation to retrieve + * @return A list of operations performed by account, ordered from most recent to oldest. + */ + vector get_account_history_operations(account_id_type account, + int operation_id, + operation_history_id_type start = operation_history_id_type(), + operation_history_id_type stop = operation_history_id_type(), + unsigned limit = 100)const; + /** * @breif Get operations relevant to the specified account referenced * by an event numbering specific to the account. The current number of operations @@ -118,6 +146,22 @@ namespace graphene { namespace app { application& _app; }; + /** + * @brief Block api + */ + class block_api + { + public: + block_api(graphene::chain::database& db); + ~block_api(); + + vector> get_blocks(uint32_t block_num_from, uint32_t block_num_to)const; + + private: + graphene::chain::database& _db; + }; + + /** * @brief The network_broadcast_api class allows broadcasting of transactions. */ @@ -252,6 +296,23 @@ namespace graphene { namespace app { range_proof_info range_get_info( const std::vector& proof ); }; + /** + * @brief + */ + class asset_api + { + public: + asset_api(graphene::chain::database& db); + ~asset_api(); + + vector get_asset_holders( asset_id_type asset_id )const; + int get_asset_holders_count( asset_id_type asset_id )const; + vector get_all_asset_holders() const; + + private: + graphene::chain::database& _db; + }; + /** * @brief The login_api class implements the bottom layer of the RPC API * @@ -273,6 +334,8 @@ namespace graphene { namespace app { * has sucessfully authenticated. */ bool login(const string& user, const string& password); + /// @brief Retrieve the network block API + fc::api block()const; /// @brief Retrieve the network broadcast API fc::api network_broadcast()const; /// @brief Retrieve the database API @@ -283,19 +346,23 @@ namespace graphene { namespace app { fc::api network_node()const; /// @brief Retrieve the cryptography API fc::api crypto()const; + /// @brief Retrieve the asset API + fc::api asset()const; /// @brief Retrieve the debug API (if available) fc::api debug()const; - private: /// @brief Called to enable an API, not reflected. void enable_api( const string& api_name ); + private: application& _app; + optional< fc::api > _block_api; optional< fc::api > _database_api; optional< fc::api > _network_broadcast_api; optional< fc::api > _network_node_api; optional< fc::api > _history_api; optional< fc::api > _crypto_api; + optional< fc::api > _asset_api; optional< fc::api > _debug_api; }; @@ -310,13 +377,20 @@ FC_REFLECT( graphene::app::verify_range_proof_rewind_result, //FC_REFLECT_TYPENAME( fc::ecc::compact_signature ); //FC_REFLECT_TYPENAME( fc::ecc::commitment_type ); +FC_REFLECT( graphene::app::account_asset_balance, (name)(account_id)(amount) ); +FC_REFLECT( graphene::app::asset_holders, (asset_id)(count) ); + FC_API(graphene::app::history_api, (get_account_history) + (get_account_history_operations) (get_relative_account_history) (get_fill_order_history) (get_market_history) (get_market_history_buckets) ) +FC_API(graphene::app::block_api, + (get_blocks) + ) FC_API(graphene::app::network_broadcast_api, (broadcast_transaction) (broadcast_transaction_with_callback) @@ -341,12 +415,19 @@ FC_API(graphene::app::crypto_api, (verify_range_proof_rewind) (range_get_info) ) +FC_API(graphene::app::asset_api, + (get_asset_holders) + (get_asset_holders_count) + (get_all_asset_holders) + ) FC_API(graphene::app::login_api, (login) + (block) (network_broadcast) (database) (history) (network_node) (crypto) + (asset) (debug) ) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index e9f91ef4..99b1cd89 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -217,6 +217,15 @@ class database_api vector> get_key_references( vector key )const; + /** + * Determine whether a textual representation of a public key + * (in Base-58 format) is *currently* linked + * to any *registered* (i.e. non-stealth) account on the blockchain + * @param public_key Public key + * @return Whether a public key is known + */ + bool is_public_key_registered(string public_key) const; + ////////////// // Accounts // ////////////// @@ -626,6 +635,7 @@ FC_API(graphene::app::database_api, // Keys (get_key_references) + (is_public_key_registered) // Accounts (get_accounts) diff --git a/libraries/app/include/graphene/app/full_account.hpp b/libraries/app/include/graphene/app/full_account.hpp index 74724630..a8c87844 100644 --- a/libraries/app/include/graphene/app/full_account.hpp +++ b/libraries/app/include/graphene/app/full_account.hpp @@ -26,6 +26,7 @@ #include #include #include +#include namespace graphene { namespace app { using namespace graphene::chain; @@ -44,6 +45,8 @@ namespace graphene { namespace app { vector limit_orders; vector call_orders; vector proposals; + vector assets; + vector withdraws; }; } } @@ -61,4 +64,6 @@ FC_REFLECT( graphene::app::full_account, (limit_orders) (call_orders) (proposals) + (assets) + (withdraws) ) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 28d5974e..d93993fe 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -232,11 +232,13 @@ namespace graphene { namespace chain { struct by_symbol; struct by_type; + struct by_issuer; typedef multi_index_container< asset_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_unique< tag, member >, + ordered_non_unique< tag, member >, ordered_unique< tag, composite_key< asset_object, const_mem_fun, diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 329d03e2..fe669f6c 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -264,7 +264,6 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions)) -FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing) FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing, (no_listing)(white_listed)(black_listed)(white_and_black_listed)) diff --git a/libraries/chain/include/graphene/chain/protocol/authority.hpp b/libraries/chain/include/graphene/chain/protocol/authority.hpp index b6ef60d7..145c24b5 100644 --- a/libraries/chain/include/graphene/chain/protocol/authority.hpp +++ b/libraries/chain/include/graphene/chain/protocol/authority.hpp @@ -132,5 +132,4 @@ void add_authority_accounts( } } // namespace graphene::chain FC_REFLECT( graphene::chain::authority, (weight_threshold)(account_auths)(key_auths)(address_auths) ) -FC_REFLECT_TYPENAME( graphene::chain::authority::classification ) FC_REFLECT_ENUM( graphene::chain::authority::classification, (owner)(active)(key) ) diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index b93b9d03..215d4902 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -146,7 +146,6 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo ); } // fc -FC_REFLECT_TYPENAME( graphene::chain::vote_id_type::vote_type ) FC_REFLECT_TYPENAME( fc::flat_set ) FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) ) diff --git a/libraries/fc b/libraries/fc index 7a44b21a..772c2b28 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 7a44b21acfea6728367a0cf56b1f3f7f8104aa63 +Subproject commit 772c2b28226736b455e31b1fdf51e4f18bf33cee diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index 021080dc..01160272 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -96,7 +96,8 @@ void witness_plugin::plugin_initialize(const boost::program_options::variables_m for (const std::string& key_id_to_wif_pair_string : key_id_to_wif_pair_strings) { auto key_id_to_wif_pair = graphene::app::dejsonify >(key_id_to_wif_pair_string); - idump((key_id_to_wif_pair)); + //idump((key_id_to_wif_pair)); + ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first)); fc::optional private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second); if (!private_key) { diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 63156712..21b177ff 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -258,6 +258,26 @@ namespace detail { class wallet_api_impl; } +/*** + * A utility class for performing various state-less actions that are related to wallets + */ +class utility { + public: + /** + * Derive any number of *possible* owner keys from a given brain key. + * + * NOTE: These keys may or may not match with the owner keys of any account. + * This function is merely intended to assist with account or key recovery. + * + * @see suggest_brain_key() + * + * @param brain_key Brain key + * @param number_of_desired_keys Number of desired keys + * @return A list of keys that are deterministically derived from the brainkey + */ + static vector derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys = 1); +}; + struct operation_detail { string memo; string description; @@ -330,8 +350,6 @@ class wallet_api * * This returns a list of operation history objects, which describe activity on the account. * - * @note this API doesn't give a way to retrieve more than the most recent 100 transactions, - * you can interface directly with the blockchain to get more history * @param name the name or id of the account * @param limit the number of entries to return (starting from the most recent) (max 100) * @returns a list of \c operation_history_objects @@ -339,7 +357,7 @@ class wallet_api vector get_account_history(string name, int limit)const; - vector get_market_history(string symbol, string symbol2, uint32_t bucket)const; + vector get_market_history(string symbol, string symbol2, uint32_t bucket, fc::time_point_sec start, fc::time_point_sec end)const; vector get_limit_orders(string a, string b, uint32_t limit)const; vector get_call_orders(string a, uint32_t limit)const; vector get_settle_orders(string a, uint32_t limit)const; @@ -578,6 +596,29 @@ class wallet_api */ brain_key_info suggest_brain_key()const; + /** + * Derive any number of *possible* owner keys from a given brain key. + * + * NOTE: These keys may or may not match with the owner keys of any account. + * This function is merely intended to assist with account or key recovery. + * + * @see suggest_brain_key() + * + * @param brain_key Brain key + * @param numberOfDesiredKeys Number of desired keys + * @return A list of keys that are deterministically derived from the brainkey + */ + vector derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys = 1) const; + + /** + * Determine whether a textual representation of a public key + * (in Base-58 format) is *currently* linked + * to any *registered* (i.e. non-stealth) account on the blockchain + * @param public_key Public key + * @return Whether a public key is known + */ + bool is_public_key_registered(string public_key) const; + /** Converts a signed_transaction in JSON form to its binary representation. * * TODO: I don't see a broadcast_transaction() function, do we need one? @@ -1632,6 +1673,7 @@ FC_API( graphene::wallet::wallet_api, (import_account_keys) (import_balance) (suggest_brain_key) + (derive_owner_keys_from_brain_key) (register_account) (upgrade_account) (create_account_with_brain_key) @@ -1676,6 +1718,7 @@ FC_API( graphene::wallet::wallet_api, (get_block) (get_account_count) (get_account_history) + (is_public_key_registered) (get_market_history) (get_global_properties) (get_dynamic_global_properties) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 9c7a36e1..8d89788e 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2717,7 +2717,29 @@ std::string operation_result_printer::operator()(const asset& a) }}} +namespace graphene { namespace wallet { + vector utility::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys) + { + // Safety-check + FC_ASSERT( number_of_desired_keys >= 1 ); + // Create as many derived owner keys as requested + vector results; + brain_key = graphene::wallet::detail::normalize_brain_key(brain_key); + for (int i = 0; i < number_of_desired_keys; ++i) { + fc::ecc::private_key priv_key = graphene::wallet::detail::derive_private_key( brain_key, i ); + + brain_key_info result; + result.brain_priv_key = brain_key; + result.wif_priv_key = key_to_wif( priv_key ); + result.pub_key = priv_key.get_public_key(); + + results.push_back(result); + } + + return results; + } +}} namespace graphene { namespace wallet { @@ -2797,9 +2819,9 @@ vector wallet_api::get_account_history(string name, int limit) } -vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket )const +vector wallet_api::get_market_history( string symbol1, string symbol2, uint32_t bucket , fc::time_point_sec start, fc::time_point_sec end )const { - return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, fc::time_point_sec(), fc::time_point::now() ); + return my->_remote_hist->get_market_history( get_asset_id(symbol1), get_asset_id(symbol2), bucket, start, end ); } vector wallet_api::get_limit_orders(string a, string b, uint32_t limit)const @@ -2847,6 +2869,18 @@ brain_key_info wallet_api::suggest_brain_key()const return result; } +vector wallet_api::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys) const +{ + return graphene::wallet::utility::derive_owner_keys_from_brain_key(brain_key, number_of_desired_keys); +} + +bool wallet_api::is_public_key_registered(string public_key) const +{ + bool is_known = my->_remote_db->is_public_key_registered(public_key); + return is_known; +} + + string wallet_api::serialize_transaction( signed_transaction tx )const { return fc::to_hex(fc::raw::pack(tx)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9b42051b..1fe7b589 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,7 +8,7 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) -target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp new file mode 100644 index 00000000..e2453176 --- /dev/null +++ b/tests/tests/database_api_tests.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture) + + BOOST_AUTO_TEST_CASE(is_registered) { + try { + /*** + * Arrange + */ + auto nathan_private_key = generate_private_key("nathan"); + public_key_type nathan_public = nathan_private_key.get_public_key(); + + auto dan_private_key = generate_private_key("dan"); + public_key_type dan_public = dan_private_key.get_public_key(); + + auto unregistered_private_key = generate_private_key("unregistered"); + public_key_type unregistered_public = unregistered_private_key.get_public_key(); + + + /*** + * Act + */ + create_account("dan", dan_private_key.get_public_key()).id; + create_account("nathan", nathan_private_key.get_public_key()).id; + // Unregistered key will not be registered with any account + + + /*** + * Assert + */ + graphene::app::database_api db_api(db); + + BOOST_CHECK(db_api.is_public_key_registered((string) nathan_public)); + BOOST_CHECK(db_api.is_public_key_registered((string) dan_public)); + BOOST_CHECK(!db_api.is_public_key_registered((string) unregistered_public)); + + } FC_LOG_AND_RETHROW() + } + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp new file mode 100644 index 00000000..554b7297 --- /dev/null +++ b/tests/tests/wallet_tests.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Cryptonomex, Inc., and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; +using namespace graphene::wallet; + +BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) + + /*** + * Check the basic behavior of deriving potential owner keys from a brain key + */ + BOOST_AUTO_TEST_CASE(derive_owner_keys_from_brain_key) { + try { + /*** + * Act + */ + int nbr_keys_desired = 3; + vector derived_keys = graphene::wallet::utility::derive_owner_keys_from_brain_key("SOME WORDS GO HERE", nbr_keys_desired); + + + /*** + * Assert: Check the number of derived keys + */ + BOOST_CHECK_EQUAL(nbr_keys_desired, derived_keys.size()); + + /*** + * Assert: Check that each derived key is unique + */ + set set_derived_public_keys; + for (auto info : derived_keys) { + string description = (string) info.pub_key; + set_derived_public_keys.emplace(description); + } + BOOST_CHECK_EQUAL(nbr_keys_desired, set_derived_public_keys.size()); + + /*** + * Assert: Check whether every public key begins with the expected prefix + */ + string expected_prefix = GRAPHENE_ADDRESS_PREFIX; + for (auto info : derived_keys) { + string description = (string) info.pub_key; + BOOST_CHECK_EQUAL(0, description.find(expected_prefix)); + } + + } FC_LOG_AND_RETHROW() + } + +BOOST_AUTO_TEST_SUITE_END()