diff --git a/Dockerfile b/Dockerfile index fc785aaf70..f8245ae254 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,12 +10,13 @@ RUN \ cmake \ git \ libbz2-dev \ - libreadline6-dev \ + libreadline-dev \ libboost-all-dev \ libcurl4-openssl-dev \ libssl-dev \ libncurses-dev \ doxygen \ + libcurl4-openssl-dev \ && \ apt-get update -y && \ apt-get install -y fish && \ @@ -49,9 +50,6 @@ RUN chown bitshares:bitshares -R /var/lib/bitshares # Volume VOLUME ["/var/lib/bitshares", "/etc/bitshares"] -# default settings -RUN ln -f -s /etc/bitshares/config.ini /var/lib/bitshares - # rpc service: EXPOSE 8090 # p2p service: diff --git a/README.md b/README.md index 95202d45f2..4c4850a21f 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ To build after all dependencies are installed: **NOTE:** BitShares requires an [OpenSSL](https://www.openssl.org/) version in the 1.0.x series. OpenSSL 1.1.0 and newer are NOT supported. If your system OpenSSL version is newer, then you will need to manually provide an older version of OpenSSL and specify it to CMake using `-DOPENSSL_INCLUDE_DIR`, `-DOPENSSL_SSL_LIBRARY`, and `-DOPENSSL_CRYPTO_LIBRARY`. -**NOTE:** BitShares requires a [Boost](http://www.boost.org/) version in the range [1.57, 1.60]. Versions earlier than -1.57 or newer than 1.60 are NOT supported. If your system Boost version is newer, then you will need to manually build +**NOTE:** BitShares requires a [Boost](http://www.boost.org/) version in the range [1.57, 1.63]. Versions earlier than +1.57 or newer than 1.63 are NOT supported. If your system Boost version is newer, then you will need to manually build an older version of Boost and specify it to CMake using `DBOOST_ROOT`. After building, the witness node can be launched with: diff --git a/docker/bitsharesentry.sh b/docker/bitsharesentry.sh index d4184c59c0..65361bbe82 100644 --- a/docker/bitsharesentry.sh +++ b/docker/bitsharesentry.sh @@ -4,32 +4,82 @@ BITSHARESD="/usr/local/bin/witness_node" # For blockchain download VERSION=`cat /etc/bitshares/version` -## seed nodes come from doc/seednodes.txt which is -## installed by docker into /etc/bitsharesd/seednodes.txt -# SEED_NODES="$(cat /etc/bitsharesd/seednodes.txt | awk -F' ' '{print $1}')" - -## if user did not pass in any desired -## seed nodes, use the ones above: -#if [[ -z "$BITSHARESD_SEED_NODES" ]]; then -# for NODE in $SEED_NODES ; do -# ARGS+=" --seed-node=$NODE" -# done -#fi +## Supported Environmental Variables +# +# * $BITSHARESD_SEED_NODES +# * $BITSHARESD_RPC_ENDPOINT +# * $BITSHARESD_PLUGINS +# * $BITSHARESD_REPLAY +# * $BITSHARESD_RESYNC +# * $BITSHARESD_P2P_ENDPOINT +# * $BITSHARESD_WITNESS_ID +# * $BITSHARESD_PRIVATE_KEY +# * $BITSHARESD_TRACK_ACCOUNTS +# * $BITSHARESD_PARTIAL_OPERATIONS +# * $BITSHARESD_MAX_OPS_PER_ACCOUNT +# * $BITSHARESD_ES_NODE_URL +# * $BITSHARESD_TRUSTED_NODE +# -## Link the bitshares config file into home -## This link has been created in Dockerfile, already -#ln -f -s /etc/bitshares/config.ini /var/lib/bitshares +ARGS="" +# Translate environmental variables +if [[ ! -z "$BITSHARESD_SEED_NODES" ]]; then + for NODE in $BITSHARESD_SEED_NODES ; do + ARGS+=" --seed-node=$NODE" + done +fi +if [[ ! -z "$BITSHARESD_RPC_ENDPOINT" ]]; then + ARGS+=" --rpc-endpoint=${BITSHARESD_RPC_ENDPOINT}" +fi + +if [[ ! -z "$BITSHARESD_PLUGINS" ]]; then + ARGS+=" --plugins=\"${BITSHARESD_PLUGINS}\"" +fi + +if [[ ! -z "$BITSHARESD_REPLAY" ]]; then + ARGS+=" --replay-blockchain" +fi + +if [[ ! -z "$BITSHARESD_RESYNC" ]]; then + ARGS+=" --resync-blockchain" +fi + +if [[ ! -z "$BITSHARESD_P2P_ENDPOINT" ]]; then + ARGS+=" --p2p-endpoint=${BITSHARESD_P2P_ENDPOINT}" +fi + +if [[ ! -z "$BITSHARESD_WITNESS_ID" ]]; then + ARGS+=" --witness-id=$BITSHARESD_WITNESS_ID" +fi -## get blockchain state from an S3 bucket -# echo bitsharesd: beginning download and decompress of s3://$S3_BUCKET/blockchain-$VERSION-latest.tar.bz2 +if [[ ! -z "$BITSHARESD_PRIVATE_KEY" ]]; then + ARGS+=" --private-key=$BITSHARESD_PRIVATE_KEY" +fi -## get blockchain state from an S3 bucket -#s3cmd get s3://$S3_BUCKET/blockchain-$VERSION-latest.tar.bz2 - | pbzip2 -m2000dc | tar x -#if [[ $? -ne 0 ]]; then -# echo unable to pull blockchain state from S3 - exiting -# exit 1 -#fi +if [[ ! -z "$BITSHARESD_TRACK_ACCOUNTS" ]]; then + for ACCOUNT in $BITSHARESD_TRACK_ACCOUNTS ; do + ARGS+=" --track-account=$ACCOUNT" + done +fi -## Deploy Healthcheck daemon +if [[ ! -z "$BITSHARESD_PARTIAL_OPERATIONS" ]]; then + ARGS+=" --partial-operations=${BITSHARESD_PARTIAL_OPERATIONS}" +fi + +if [[ ! -z "$BITSHARESD_MAX_OPS_PER_ACCOUNT" ]]; then + ARGS+=" --max-ops-per-account=${BITSHARESD_MAX_OPS_PER_ACCOUNT}" +fi + +if [[ ! -z "$BITSHARESD_ES_NODE_URL" ]]; then + ARGS+=" --elasticsearch-node-url=${BITSHARESD_ES_NODE_URL}" +fi + +if [[ ! -z "$BITSHARESD_TRUSTED_NODE" ]]; then + ARGS+=" --trusted-node=${BITSHARESD_TRUSTED_NODE}" +fi + +## Link the bitshares config file into home +## This link has been created in Dockerfile, already +ln -f -s /etc/bitshares/config.ini /var/lib/bitshares $BITSHARESD --data-dir ${HOME} ${ARGS} ${BITSHARESD_ARGS} diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index d491677240..d8bf6efbc7 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -58,6 +58,8 @@ #include +#include + namespace graphene { namespace app { using net::item_hash_t; using net::item_id; @@ -943,6 +945,7 @@ void application::set_program_options(boost::program_options::options_descriptio ("resync-blockchain", "Delete all blocks and re-sync with network from scratch") ("force-validate", "Force validation of all transactions") ("genesis-timestamp", bpo::value(), "Replace timestamp from genesis.json with current time plus this many seconds (experts only!)") + ("version,v", "Display version information") ; command_line_options.add(_cli_options); configuration_file_options.add(_cfg_options); @@ -953,6 +956,14 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti my->_data_dir = data_dir; my->_options = &options; + if( options.count("version") ) + { + std::cout << "Version: " << graphene::utilities::git_revision_description << "\n"; + std::cout << "SHA: " << graphene::utilities::git_revision_sha << "\n"; + std::cout << "Timestamp: " << fc::get_approximate_relative_time_string(fc::time_point_sec(graphene::utilities::git_revision_unix_timestamp)) << "\n"; + std::exit(EXIT_SUCCESS); + } + if( options.count("create-genesis-json") ) { fc::path genesis_out = options.at("create-genesis-json").as(); diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 1739a7625e..96e100b6d7 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -111,7 +111,7 @@ class database_api_impl : public std::enable_shared_from_this vector get_collateral_bids(const asset_id_type asset, uint32_t limit, uint32_t start)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); - market_ticker get_ticker( const string& base, const string& quote )const; + market_ticker get_ticker( const string& base, const string& quote, bool skip_order_book = false )const; market_volume get_24_volume( const string& base, const string& quote )const; order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const; vector get_trade_history( const string& base, const string& quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit = 100 )const; @@ -1154,7 +1154,7 @@ market_ticker database_api::get_ticker( const string& base, const string& quote return my->get_ticker( base, quote ); } -market_ticker database_api_impl::get_ticker( const string& base, const string& quote )const +market_ticker database_api_impl::get_ticker( const string& base, const string& quote, bool skip_order_book )const { const auto assets = lookup_asset_symbols( {base, quote} ); FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) ); @@ -1178,11 +1178,6 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q auto quote_id = assets[1]->id; if( base_id > quote_id ) std::swap( base_id, quote_id ); - history_key hkey; - hkey.base = base_id; - hkey.quote = quote_id; - hkey.sequence = std::numeric_limits::min(); - // TODO: move following duplicate code out // TODO: using pow is a bit inefficient here, optimization is possible auto asset_to_real = [&]( const asset& a, int p ) { return double(a.amount.value)/pow( 10, p ); }; @@ -1194,44 +1189,31 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision ); }; - const auto& history_idx = _db.get_index_type().indices().get(); - auto itr = history_idx.lower_bound( hkey ); - - bool is_latest = true; - price latest_price; fc::uint128 base_volume; fc::uint128 quote_volume; - while( itr != history_idx.end() && itr->key.base == base_id && itr->key.quote == quote_id ) + + const auto& ticker_idx = _db.get_index_type().indices().get(); + auto itr = ticker_idx.find( std::make_tuple( base_id, quote_id ) ); + if( itr != ticker_idx.end() ) { - if( is_latest ) + price latest_price = asset( itr->latest_base, itr->base ) / asset( itr->latest_quote, itr->quote ); + result.latest = price_to_real( latest_price ); + if( itr->last_day_base != 0 && itr->last_day_quote != 0 // has trade data before 24 hours + && ( itr->last_day_base != itr->latest_base || itr->last_day_quote != itr->latest_quote ) ) // price changed { - is_latest = false; - latest_price = itr->op.fill_price; - result.latest = price_to_real( latest_price ); + price last_day_price = asset( itr->last_day_base, itr->base ) / asset( itr->last_day_quote, itr->quote ); + result.percent_change = ( result.latest / price_to_real( last_day_price ) - 1 ) * 100; } - - if( itr->time < yesterday ) + if( assets[0]->id == itr->base ) { - if( itr->op.fill_price != latest_price ) - result.percent_change = ( result.latest / price_to_real( itr->op.fill_price ) - 1 ) * 100; - break; + base_volume = itr->base_volume; + quote_volume = itr->quote_volume; } - - if( itr->op.is_maker ) + else { - if( assets[0]->id == itr->op.receives.asset_id ) - { - base_volume += itr->op.receives.amount.value; - quote_volume += itr->op.pays.amount.value; - } - else - { - base_volume += itr->op.pays.amount.value; - quote_volume += itr->op.receives.amount.value; - } + base_volume = itr->quote_volume; + quote_volume = itr->base_volume; } - - ++itr; } auto uint128_to_double = []( const fc::uint128& n ) @@ -1242,9 +1224,12 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q result.base_volume = uint128_to_double( base_volume ) / pow( 10, assets[0]->precision ); result.quote_volume = uint128_to_double( quote_volume ) / pow( 10, assets[1]->precision ); - const auto orders = get_order_book( base, quote, 1 ); - if( !orders.asks.empty() ) result.lowest_ask = orders.asks[0].price; - if( !orders.bids.empty() ) result.highest_bid = orders.bids[0].price; + if( !skip_order_book ) + { + const auto orders = get_order_book( base, quote, 1 ); + if( !orders.asks.empty() ) result.lowest_ask = orders.asks[0].price; + if( !orders.bids.empty() ) result.highest_bid = orders.bids[0].price; + } return result; } @@ -1256,7 +1241,7 @@ market_volume database_api::get_24_volume( const string& base, const string& quo market_volume database_api_impl::get_24_volume( const string& base, const string& quote )const { - const auto& ticker = get_ticker( base, quote ); + const auto& ticker = get_ticker( base, quote, true ); market_volume result; result.time = ticker.time; @@ -1348,11 +1333,6 @@ vector database_api_impl::get_trade_history( const string& base, auto quote_id = assets[1]->id; if( base_id > quote_id ) std::swap( base_id, quote_id ); - const auto& history_idx = _db.get_index_type().indices().get(); - history_key hkey; - hkey.base = base_id; - hkey.quote = quote_id; - hkey.sequence = std::numeric_limits::min(); auto asset_to_real = [&]( const asset& a, int p ) { return double( a.amount.value ) / pow( 10, p ); }; auto price_to_real = [&]( const price& p ) @@ -1367,13 +1347,12 @@ vector database_api_impl::get_trade_history( const string& base, start = fc::time_point_sec( fc::time_point::now() ); uint32_t count = 0; - uint32_t skipped = 0; - auto itr = history_idx.lower_bound( hkey ); + const auto& history_idx = _db.get_index_type().indices().get(); + auto itr = history_idx.lower_bound( std::make_tuple( base_id, quote_id, start ) ); vector result; while( itr != history_idx.end() && count < limit && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) ) { - if( itr->time < start ) { market_trade trade; @@ -1418,12 +1397,6 @@ vector database_api_impl::get_trade_history( const string& base, result.push_back( trade ); ++count; } - else // should skip - { - // TODO refuse to execute if need to skip too many entries - // ++skipped; - // FC_ASSERT( skipped <= 200 ); - } ++itr; } @@ -1867,6 +1840,9 @@ set database_api_impl::get_potential_signatures( const signed_t const auto& auth = id(_db).active; for( const auto& k : auth.get_keys() ) result.insert(k); + // Also insert owner keys since owner can authorize a trx that requires active only + for( const auto& k : id(_db).owner.get_keys() ) + result.insert(k); return &auth; }, [&]( account_id_type id ) @@ -1879,6 +1855,15 @@ set database_api_impl::get_potential_signatures( const signed_t _db.get_global_properties().parameters.max_authority_depth ); + // Insert keys in required "other" authories + flat_set required_active; + flat_set required_owner; + vector other; + trx.get_required_authorities( required_active, required_owner, other ); + for( const auto& auth : other ) + for( const auto& key : auth.get_keys() ) + result.insert( key ); + wdump((result)); return result; } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 83d4880c4f..684f06394a 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -85,7 +85,6 @@ void fork_database::_push_block(const item_ptr& item) auto& index = _index.get(); auto itr = index.find(item->previous_id()); GRAPHENE_ASSERT(itr != index.end(), unlinkable_block_exception, "block does not link to known chain"); - FC_ASSERT(!(*itr)->invalid); item->prev = *itr; } diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index cd46a60cf5..1eb80c8942 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -45,7 +45,7 @@ #define GRAPHENE_DEFAULT_BLOCK_INTERVAL 5 /* seconds */ #define GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE 2048 -#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE*GRAPHENE_DEFAULT_BLOCK_INTERVAL*200000) +#define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (2*1000*1000) /* < 2 MiB (less than MAX_MESSAGE_SIZE in graphene/net/config.hpp) */ #define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds, aka: 1 day #define GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL (60*60*24) // seconds, aka: 1 day #define GRAPHENE_DEFAULT_MAINTENANCE_SKIP_SLOTS 3 // number of slots to skip for maintenance interval diff --git a/libraries/chain/include/graphene/chain/fork_database.hpp b/libraries/chain/include/graphene/chain/fork_database.hpp index 8ca95b5e4b..be3991ed80 100644 --- a/libraries/chain/include/graphene/chain/fork_database.hpp +++ b/libraries/chain/include/graphene/chain/fork_database.hpp @@ -44,11 +44,6 @@ namespace graphene { namespace chain { weak_ptr< fork_item > prev; uint32_t num; // initialized in ctor - /** - * Used to flag a block as invalid and prevent other blocks from - * building on top of it. - */ - bool invalid = false; block_id_type id; signed_block data; }; diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index a0ebc24ce4..d7aa573a52 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -4564,7 +4564,7 @@ namespace graphene { namespace net { namespace detail { error_message_stream << "Unable to listen for connections on port " << listen_endpoint.port() << ", retrying in a few seconds\n"; error_message_stream << "You can wait for it to become available, or restart this program using\n"; - error_message_stream << "the --p2p-port option to specify another port\n"; + error_message_stream << "the --p2p-endpoint option to specify another port\n"; first = false; } else diff --git a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp index ef89c488f5..7bec37ddfe 100644 --- a/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp +++ b/libraries/plugins/account_history/include/graphene/account_history/account_history_plugin.hpp @@ -47,13 +47,12 @@ namespace graphene { namespace account_history { // time. // #ifndef ACCOUNT_HISTORY_SPACE_ID -#define ACCOUNT_HISTORY_SPACE_ID 5 +#define ACCOUNT_HISTORY_SPACE_ID 4 #endif enum account_history_object_type { - key_account_object_type = 0, - bucket_object_type = 1 ///< used in market_history_plugin + key_account_object_type = 0 }; diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index b4d1254ca0..f22e8ac0fc 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -27,6 +27,9 @@ #include #include +#include + +#include namespace graphene { namespace market_history { using namespace chain; @@ -41,10 +44,18 @@ using namespace chain; // various template automagic depends on them being known at compile // time. // -#ifndef ACCOUNT_HISTORY_SPACE_ID -#define ACCOUNT_HISTORY_SPACE_ID 5 +#ifndef MARKET_HISTORY_SPACE_ID +#define MARKET_HISTORY_SPACE_ID 5 #endif +enum market_history_object_type +{ + order_history_object_type = 0, + bucket_object_type = 1, + market_ticker_object_type = 2, + market_ticker_meta_object_type = 3 +}; + struct bucket_key { bucket_key( asset_id_type a, asset_id_type b, uint32_t s, fc::time_point_sec o ) @@ -68,8 +79,8 @@ struct bucket_key struct bucket_object : public abstract_object { - static const uint8_t space_id = ACCOUNT_HISTORY_SPACE_ID; - static const uint8_t type_id = 1; // market_history_plugin type, referenced from account_history_plugin.hpp + static const uint8_t space_id = MARKET_HISTORY_SPACE_ID; + static const uint8_t type_id = bucket_object_type; price high()const { return asset( high_base, key.base ) / asset( high_quote, key.quote ); } price low()const { return asset( low_base, key.base ) / asset( low_quote, key.quote ); } @@ -101,31 +112,106 @@ struct history_key { }; struct order_history_object : public abstract_object { - history_key key; - fc::time_point_sec time; - fill_order_operation op; + static const uint8_t space_id = MARKET_HISTORY_SPACE_ID; + static const uint8_t type_id = order_history_object_type; + + history_key key; + fc::time_point_sec time; + fill_order_operation op; +}; +struct order_history_object_key_base_extractor +{ + typedef asset_id_type result_type; + result_type operator()(const order_history_object& o)const { return o.key.base; } +}; +struct order_history_object_key_quote_extractor +{ + typedef asset_id_type result_type; + result_type operator()(const order_history_object& o)const { return o.key.quote; } +}; +struct order_history_object_key_sequence_extractor +{ + typedef int64_t result_type; + result_type operator()(const order_history_object& o)const { return o.key.sequence; } +}; + +struct market_ticker_object : public abstract_object +{ + static const uint8_t space_id = MARKET_HISTORY_SPACE_ID; + static const uint8_t type_id = market_ticker_object_type; + + asset_id_type base; + asset_id_type quote; + share_type last_day_base; + share_type last_day_quote; + share_type latest_base; + share_type latest_quote; + fc::uint128 base_volume; + fc::uint128 quote_volume; +}; + +struct market_ticker_meta_object : public abstract_object +{ + static const uint8_t space_id = MARKET_HISTORY_SPACE_ID; + static const uint8_t type_id = market_ticker_meta_object_type; + + object_id_type rolling_min_order_his_id; + bool skip_min_order_his_id = false; }; struct by_key; typedef multi_index_container< bucket_object, indexed_by< - hashed_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< object, object_id_type, &object::id > >, ordered_unique< tag, member< bucket_object, bucket_key, &bucket_object::key > > > > bucket_object_multi_index_type; +struct by_market_time; typedef multi_index_container< order_history_object, indexed_by< - hashed_unique< tag, member< object, object_id_type, &object::id > >, - ordered_unique< tag, member< order_history_object, history_key, &order_history_object::key > > + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< order_history_object, history_key, &order_history_object::key > >, + ordered_unique< + tag, + composite_key< + order_history_object, + order_history_object_key_base_extractor, + order_history_object_key_quote_extractor, + member, + order_history_object_key_sequence_extractor + >, + composite_key_compare< + std::less< asset_id_type >, + std::less< asset_id_type >, + std::greater< time_point_sec >, + std::less< int64_t > + > + > > > order_history_multi_index_type; +struct by_market; +typedef multi_index_container< + market_ticker_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< + tag, + composite_key< + market_ticker_object, + member, + member + > + > + > +> market_ticker_object_multi_index_type; typedef generic_index bucket_index; typedef generic_index history_index; +typedef generic_index market_ticker_index; namespace detail @@ -154,6 +240,8 @@ class market_history_plugin : public graphene::app::plugin uint32_t max_history()const; const flat_set& tracked_buckets()const; + uint32_t max_order_his_records_per_market()const; + uint32_t max_order_his_seconds_per_market()const; private: friend class detail::market_history_plugin_impl; @@ -165,11 +253,17 @@ class market_history_plugin : public graphene::app::plugin FC_REFLECT( graphene::market_history::history_key, (base)(quote)(sequence) ) FC_REFLECT_DERIVED( graphene::market_history::order_history_object, (graphene::db::object), (key)(time)(op) ) FC_REFLECT( graphene::market_history::bucket_key, (base)(quote)(seconds)(open) ) -FC_REFLECT_DERIVED( graphene::market_history::bucket_object, (graphene::db::object), +FC_REFLECT_DERIVED( graphene::market_history::bucket_object, (graphene::db::object), (key) (high_base)(high_quote) (low_base)(low_quote) (open_base)(open_quote) (close_base)(close_quote) (base_volume)(quote_volume) ) - +FC_REFLECT_DERIVED( graphene::market_history::market_ticker_object, (graphene::db::object), + (base)(quote) + (last_day_base)(last_day_quote) + (latest_base)(latest_quote) + (base_volume)(quote_volume) ) +FC_REFLECT_DERIVED( graphene::market_history::market_ticker_meta_object, (graphene::db::object), + (rolling_min_order_his_id)(skip_min_order_his_id) ) diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index 8f5feba8a9..8d973d1c7a 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -61,16 +61,21 @@ class market_history_plugin_impl market_history_plugin& _self; flat_set _tracked_buckets; uint32_t _maximum_history_per_bucket_size = 1000; + uint32_t _max_order_his_records_per_market = 1000; + uint32_t _max_order_his_seconds_per_market = 259200; + + const market_ticker_meta_object* _meta = nullptr; }; struct operation_process_fill_order { - market_history_plugin& _plugin; - fc::time_point_sec _now; + market_history_plugin& _plugin; + fc::time_point_sec _now; + const market_ticker_meta_object*& _meta; - operation_process_fill_order( market_history_plugin& mhp, fc::time_point_sec n ) - :_plugin(mhp),_now(n) {} + operation_process_fill_order( market_history_plugin& mhp, fc::time_point_sec n, const market_ticker_meta_object*& meta ) + :_plugin(mhp),_now(n),_meta(meta) {} typedef void result_type; @@ -81,13 +86,12 @@ struct operation_process_fill_order void operator()( const fill_order_operation& o )const { //ilog( "processing ${o}", ("o",o) ); - const auto& buckets = _plugin.tracked_buckets(); auto& db = _plugin.database(); - const auto& bucket_idx = db.get_index_type(); - const auto& history_idx = db.get_index_type().indices().get(); - - auto time = db.head_block_time(); + const auto& order_his_idx = db.get_index_type().indices(); + const auto& history_idx = order_his_idx.get(); + const auto& his_time_idx = order_his_idx.get(); + // To save new filled order data history_key hkey; hkey.base = o.pays.asset_id; hkey.quote = o.receives.asset_id; @@ -102,38 +106,60 @@ struct operation_process_fill_order else hkey.sequence = 0; - db.create( [&]( order_history_object& ho ) { + const auto& new_order_his_obj = db.create( [&]( order_history_object& ho ) { ho.key = hkey; - ho.time = time; + ho.time = _now; ho.op = o; }); - /* - hkey.sequence += 200; + // save a reference to market ticker meta object + if( _meta == nullptr ) + { + const auto& meta_idx = db.get_index_type>(); + if( meta_idx.size() == 0 ) + _meta = &db.create( [&]( market_ticker_meta_object& mtm ) { + mtm.rolling_min_order_his_id = new_order_his_obj.id; + mtm.skip_min_order_his_id = false; + }); + else + _meta = &( *meta_idx.begin() ); + } + + // To remove old filled order data + const auto max_records = _plugin.max_order_his_records_per_market(); + hkey.sequence += max_records; itr = history_idx.lower_bound( hkey ); - while( itr != history_idx.end() ) + if( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote ) { - if( itr->key.base == hkey.base && itr->key.quote == hkey.quote ) + const auto max_seconds = _plugin.max_order_his_seconds_per_market(); + fc::time_point_sec min_time; + if( min_time + max_seconds < _now ) + min_time = _now - max_seconds; + auto time_itr = his_time_idx.lower_bound( std::make_tuple( hkey.base, hkey.quote, min_time ) ); + if( time_itr != his_time_idx.end() && time_itr->key.base == hkey.base && time_itr->key.quote == hkey.quote ) { - db.remove( *itr ); - itr = history_idx.lower_bound( hkey ); + if( itr->key.sequence >= time_itr->key.sequence ) + { + while( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote ) + { + auto old_itr = itr; + ++itr; + db.remove( *old_itr ); + } + } + else + { + while( time_itr != his_time_idx.end() && time_itr->key.base == hkey.base && time_itr->key.quote == hkey.quote ) + { + auto old_itr = time_itr; + ++time_itr; + db.remove( *old_itr ); + } + } } - else break; } - */ - /* Note: below is not true, because global settlement creates only one fill_order_op. - * for every matched order there are two fill order operations created, one for - * each side. We can filter the duplicates by only considering the fill operations where - * the base > quote - */ - /* - if( o.pays.asset_id > o.receives.asset_id ) - { - //ilog( " skipping because base > quote" ); - return; - } - */ + // To update ticker data and buckets data, only update for maker orders if( !o.is_maker ) return; @@ -153,23 +179,59 @@ struct operation_process_fill_order if( fill_price.base.asset_id > fill_price.quote.asset_id ) fill_price = ~fill_price; - auto max_history = _plugin.max_history(); + // To update ticker data + const auto& ticker_idx = db.get_index_type().indices().get(); + auto ticker_itr = ticker_idx.find( std::make_tuple( key.base, key.quote ) ); + if( ticker_itr == ticker_idx.end() ) + { + db.create( [&]( market_ticker_object& mt ) { + mt.base = key.base; + mt.quote = key.quote; + mt.last_day_base = 0; + mt.last_day_quote = 0; + mt.latest_base = fill_price.base.amount; + mt.latest_quote = fill_price.quote.amount; + mt.base_volume = trade_price.base.amount.value; + mt.quote_volume = trade_price.quote.amount.value; + }); + } + else + { + db.modify( *ticker_itr, [&]( market_ticker_object& mt ) { + mt.latest_base = fill_price.base.amount; + mt.latest_quote = fill_price.quote.amount; + mt.base_volume += trade_price.base.amount.value; // ignore overflow + mt.quote_volume += trade_price.quote.amount.value; // ignore overflow + }); + } + + // To update buckets data + const auto max_history = _plugin.max_history(); + if( max_history == 0 ) return; + + const auto& buckets = _plugin.tracked_buckets(); + if( buckets.size() == 0 ) return; + + const auto& bucket_idx = db.get_index_type(); for( auto bucket : buckets ) { - auto cutoff = (fc::time_point() + fc::seconds( bucket * max_history)); + auto bucket_num = _now.sec_since_epoch() / bucket; + fc::time_point_sec cutoff; + if( bucket_num > max_history ) + cutoff = cutoff + ( bucket * ( bucket_num - max_history ) ); key.seconds = bucket; - key.open = fc::time_point() + fc::seconds((_now.sec_since_epoch() / key.seconds) * key.seconds); + key.open = fc::time_point_sec() + ( bucket_num * bucket ); const auto& by_key_idx = bucket_idx.indices().get(); - auto itr = by_key_idx.find( key ); - if( itr == by_key_idx.end() ) + auto bucket_itr = by_key_idx.find( key ); + if( bucket_itr == by_key_idx.end() ) { // create new bucket /* const auto& obj = */ db.create( [&]( bucket_object& b ){ b.key = key; - b.quote_volume += trade_price.quote.amount; - b.base_volume += trade_price.base.amount; + b.base_volume = trade_price.base.amount; + b.quote_volume = trade_price.quote.amount; b.open_base = fill_price.base.amount; b.open_quote = fill_price.quote.amount; b.close_base = fill_price.base.amount; @@ -183,10 +245,18 @@ struct operation_process_fill_order } else { // update existing bucket - //wlog( " before updating bucket ${b}", ("b",*itr) ); - db.modify( *itr, [&]( bucket_object& b ){ - b.base_volume += trade_price.base.amount; - b.quote_volume += trade_price.quote.amount; + //wlog( " before updating bucket ${b}", ("b",*bucket_itr) ); + db.modify( *bucket_itr, [&]( bucket_object& b ){ + try { + b.base_volume += trade_price.base.amount; + } catch( fc::overflow_exception ) { + b.base_volume = std::numeric_limits::max(); + } + try { + b.quote_volume += trade_price.quote.amount; + } catch( fc::overflow_exception ) { + b.quote_volume = std::numeric_limits::max(); + } b.close_base = fill_price.base.amount; b.close_quote = fill_price.quote.amount; if( b.high() < fill_price ) @@ -200,24 +270,23 @@ struct operation_process_fill_order b.low_quote = b.close_quote; } }); - //wlog( " after bucket bucket ${b}", ("b",*itr) ); + //wlog( " after bucket bucket ${b}", ("b",*bucket_itr) ); } - if( max_history != 0 ) { key.open = fc::time_point_sec(); - auto itr = by_key_idx.lower_bound( key ); + bucket_itr = by_key_idx.lower_bound( key ); - while( itr != by_key_idx.end() && - itr->key.base == key.base && - itr->key.quote == key.quote && - itr->key.seconds == bucket && - itr->key.open < cutoff ) + while( bucket_itr != by_key_idx.end() && + bucket_itr->key.base == key.base && + bucket_itr->key.quote == key.quote && + bucket_itr->key.seconds == bucket && + bucket_itr->key.open < cutoff ) { - // elog( " removing old bucket ${b}", ("b", *itr) ); - auto old_itr = itr; - ++itr; - db.remove( *old_itr ); + // elog( " removing old bucket ${b}", ("b", *bucket_itr) ); + auto old_bucket_itr = bucket_itr; + ++bucket_itr; + db.remove( *old_bucket_itr ); } } } @@ -229,9 +298,6 @@ market_history_plugin_impl::~market_history_plugin_impl() void market_history_plugin_impl::update_market_histories( const signed_block& b ) { - if( _maximum_history_per_bucket_size == 0 ) return; - if( _tracked_buckets.size() == 0 ) return; - graphene::chain::database& db = database(); const vector >& hist = db.get_applied_operations(); for( const optional< operation_history_object >& o_op : hist ) @@ -240,10 +306,79 @@ void market_history_plugin_impl::update_market_histories( const signed_block& b { try { - o_op->op.visit( operation_process_fill_order( _self, b.timestamp ) ); + o_op->op.visit( operation_process_fill_order( _self, b.timestamp, _meta ) ); } FC_CAPTURE_AND_LOG( (o_op) ) } } + // roll out expired data from ticker + if( _meta != nullptr ) + { + time_point_sec last_day = b.timestamp - 86400; + object_id_type last_min_his_id = _meta->rolling_min_order_his_id; + bool skip = _meta->skip_min_order_his_id; + + const auto& ticker_idx = db.get_index_type().indices().get(); + const auto& history_idx = db.get_index_type().indices().get(); + auto history_itr = history_idx.lower_bound( _meta->rolling_min_order_his_id ); + while( history_itr != history_idx.end() && history_itr->time < last_day ) + { + const fill_order_operation& o = history_itr->op; + if( skip && history_itr->id == _meta->rolling_min_order_his_id ) + skip = false; + else if( o.is_maker ) + { + bucket_key key; + key.base = o.pays.asset_id; + key.quote = o.receives.asset_id; + + price trade_price = o.pays / o.receives; + + if( key.base > key.quote ) + { + std::swap( key.base, key.quote ); + trade_price = ~trade_price; + } + + price fill_price = o.fill_price; + if( fill_price.base.asset_id > fill_price.quote.asset_id ) + fill_price = ~fill_price; + + auto ticker_itr = ticker_idx.find( std::make_tuple( key.base, key.quote ) ); + if( ticker_itr != ticker_idx.end() ) // should always be true + { + db.modify( *ticker_itr, [&]( market_ticker_object& mt ) { + mt.last_day_base = fill_price.base.amount; + mt.last_day_quote = fill_price.quote.amount; + mt.base_volume -= trade_price.base.amount.value; // ignore underflow + mt.quote_volume -= trade_price.quote.amount.value; // ignore underflow + }); + } + } + last_min_his_id = history_itr->id; + ++history_itr; + } + // update meta + if( history_itr != history_idx.end() ) // if still has some data rolling + { + if( history_itr->id != _meta->rolling_min_order_his_id ) // if rolled out some + { + db.modify( *_meta, [&]( market_ticker_meta_object& mtm ) { + mtm.rolling_min_order_his_id = history_itr->id; + mtm.skip_min_order_his_id = false; + }); + } + } + else // if all data are rolled out + { + if( last_min_his_id != _meta->rolling_min_order_his_id ) // if rolled out some + { + db.modify( *_meta, [&]( market_ticker_meta_object& mtm ) { + mtm.rolling_min_order_his_id = last_min_his_id; + mtm.skip_min_order_his_id = true; + }); + } + } + } } } // end namespace detail @@ -275,8 +410,12 @@ void market_history_plugin::plugin_set_program_options( cli.add_options() ("bucket-size", boost::program_options::value()->default_value("[60,300,900,1800,3600,14400,86400]"), "Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers") - ("history-per-size", boost::program_options::value()->default_value(1000), + ("history-per-size", boost::program_options::value()->default_value(1000), "How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)") + ("max-order-his-records-per-market", boost::program_options::value()->default_value(1000), + "Will only store this amount of matched orders for each market in order history for querying, or those meet the other option, which has more data (default: 1000)") + ("max-order-his-seconds-per-market", boost::program_options::value()->default_value(259200), + "Will only store matched orders in last X seconds for each market in order history for querying, or those meet the other option, which has more data (default: 259200 (3 days))") ; cfg.add(cli); } @@ -286,14 +425,21 @@ void market_history_plugin::plugin_initialize(const boost::program_options::vari database().applied_block.connect( [&]( const signed_block& b){ my->update_market_histories(b); } ); database().add_index< primary_index< bucket_index > >(); database().add_index< primary_index< history_index > >(); + database().add_index< primary_index< market_ticker_index > >(); + database().add_index< primary_index< simple_index< market_ticker_meta_object > > >(); if( options.count( "bucket-size" ) ) { const std::string& buckets = options["bucket-size"].as(); my->_tracked_buckets = fc::json::from_string(buckets).as>(); + my->_tracked_buckets.erase( 0 ); } if( options.count( "history-per-size" ) ) my->_maximum_history_per_bucket_size = options["history-per-size"].as(); + if( options.count( "max-order-his-records-per-market" ) ) + my->_max_order_his_records_per_market = options["max-order-his-records-per-market"].as(); + if( options.count( "max-order-his-seconds-per-market" ) ) + my->_max_order_his_seconds_per_market = options["max-order-his-seconds-per-market"].as(); } FC_CAPTURE_AND_RETHROW() } void market_history_plugin::plugin_startup() @@ -310,4 +456,14 @@ uint32_t market_history_plugin::max_history()const return my->_maximum_history_per_bucket_size; } +uint32_t market_history_plugin::max_order_his_records_per_market()const +{ + return my->_max_order_his_records_per_market; +} + +uint32_t market_history_plugin::max_order_his_seconds_per_market()const +{ + return my->_max_order_his_seconds_per_market; +} + } } diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 615c95ecd3..736467a108 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -786,6 +786,22 @@ class wallet_api transaction_id_type get_transaction_id( const signed_transaction& trx )const { return trx.id(); } + /** Sign a memo message. + * + * @param from the name or id of signing account; or a public key. + * @param to the name or id of receiving account; or a public key. + * @param memo text to sign. + */ + memo_data sign_memo(string from, string to, string memo); + + /** Read a memo. + * + * @param memo JSON-enconded memo. + * @returns string with decrypted message.. + */ + string read_memo(const memo_data& memo); + + /** These methods are used for stealth transfers */ ///@{ /** @@ -1718,6 +1734,8 @@ FC_API( graphene::wallet::wallet_api, (flood_network) (network_add_nodes) (network_get_connected_peers) + (sign_memo) + (read_memo) (set_key_label) (get_key_label) (get_public_key) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 7017808d5c..9b24f95bf5 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1877,6 +1877,56 @@ class wallet_api_impl return tx; } + memo_data sign_memo(string from, string to, string memo) + { + FC_ASSERT( !self.is_locked() ); + + memo_data md = memo_data(); + + // get account memo key, if that fails, try a pubkey + try { + account_object from_account = get_account(from); + md.from = from_account.options.memo_key; + } catch (const fc::exception& e) { + md.from = self.get_public_key( from ); + } + // same as above, for destination key + try { + account_object to_account = get_account(to); + md.to = to_account.options.memo_key; + } catch (const fc::exception& e) { + md.to = self.get_public_key( to ); + } + + md.set_message(get_private_key(md.from), md.to, memo); + return md; + } + + string read_memo(const memo_data& md) + { + FC_ASSERT(!is_locked()); + std::string clear_text; + + const memo_data *memo = &md; + + try { + FC_ASSERT(_keys.count(memo->to) || _keys.count(memo->from), "Memo is encrypted to a key ${to} or ${from} not in this wallet.", ("to", memo->to)("from",memo->from)); + if( _keys.count(memo->to) ) { + auto my_key = wif_to_key(_keys.at(memo->to)); + FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); + clear_text = memo->get_message(*my_key, memo->from); + } else { + auto my_key = wif_to_key(_keys.at(memo->from)); + FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); + clear_text = memo->get_message(*my_key, memo->to); + } + } catch (const fc::exception& e) { + elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string())); + } + + return clear_text; + } + signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, @@ -3783,6 +3833,18 @@ signed_transaction wallet_api::cancel_order(object_id_type order_id, bool broadc return my->cancel_order(order_id, broadcast); } +memo_data wallet_api::sign_memo(string from, string to, string memo) +{ + FC_ASSERT(!is_locked()); + return my->sign_memo(from, to, memo); +} + +string wallet_api::read_memo(const memo_data& memo) +{ + FC_ASSERT(!is_locked()); + return my->read_memo(memo); +} + string wallet_api::get_key_label( public_key_type key )const { auto key_itr = my->_wallet.labeled_keys.get().find(key); @@ -3928,6 +3990,9 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke ilog( "about to validate" ); conf.trx.validate(); + ilog( "about to broadcast" ); + conf.trx = sign_transaction( conf.trx, broadcast ); + if( broadcast && conf.outputs.size() == 2 ) { // Save the change @@ -3945,9 +4010,6 @@ blind_confirmation wallet_api::transfer_from_blind( string from_blind_account_ke //} catch ( ... ){} } - ilog( "about to broadcast" ); - conf.trx = sign_transaction( conf.trx, broadcast ); - return conf; } FC_CAPTURE_AND_RETHROW( (from_blind_account_key_or_label)(to_account_id_or_name)(amount_in)(symbol) ) } @@ -4020,7 +4082,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, my->_wallet.blind_receipts.modify( itr, []( blind_receipt& r ){ r.used = true; } ); } - FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficent Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) ); + FC_ASSERT( total_amount >= amount+blind_tr.fee, "Insufficient Balance", ("available",total_amount)("amount",amount)("fee",blind_tr.fee) ); auto one_time_key = fc::ecc::private_key::generate(); auto secret = one_time_key.get_shared_secret( to_key ); diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 040b6fac40..1ec5914e16 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -50,6 +50,8 @@ #include #include +#include + #ifdef WIN32 # include #else @@ -79,7 +81,9 @@ int main( int argc, char** argv ) ("rpc-http-endpoint,H", bpo::value()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on") ("daemon,d", "Run the wallet in daemon mode" ) ("wallet-file,w", bpo::value()->implicit_value("wallet.json"), "wallet to load") - ("chain-id", bpo::value(), "chain ID to connect to"); + ("chain-id", bpo::value(), "chain ID to connect to") + ("version,v", "Display version information"); + bpo::variables_map options; @@ -90,6 +94,13 @@ int main( int argc, char** argv ) std::cout << opts << "\n"; return 0; } + if( options.count("version") ) + { + std::cout << "Version: " << graphene::utilities::git_revision_description << "\n"; + std::cout << "SHA: " << graphene::utilities::git_revision_sha << "\n"; + std::cout << "Timestamp: " << fc::get_approximate_relative_time_string(fc::time_point_sec(graphene::utilities::git_revision_unix_timestamp)) << "\n"; + return 0; + } fc::path data_dir; fc::logging_config cfg; @@ -209,8 +220,6 @@ int main( int argc, char** argv ) if( options.count("rpc-endpoint") ) { _websocket_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ - std::cout << "here... \n"; - wlog("." ); auto wsc = std::make_shared(*c); wsc->register_api(wapi); c->set_session_data( wsc ); diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index e2453176f4..c638aa9eb7 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -26,6 +26,8 @@ #include +#include + #include "../common/database_fixture.hpp" using namespace graphene::chain; @@ -33,39 +35,93 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture) - BOOST_AUTO_TEST_CASE(is_registered) { +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_CASE( get_potential_signatures_owner_and_active ) { + try { + fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); + fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2")); + public_key_type pub_key1( nathan_key1.get_public_key() ); + public_key_type pub_key2( nathan_key2.get_public_key() ); + const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() ); + try { - /*** - * Arrange - */ - auto nathan_private_key = generate_private_key("nathan"); - public_key_type nathan_public = nathan_private_key.get_public_key(); + account_update_operation op; + op.account = nathan.id; + op.active = authority(1, pub_key1, 1); + op.owner = authority(1, pub_key2, 1); + trx.operations.push_back(op); + sign(trx, nathan_key1); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.operations.clear(); + trx.signatures.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + transfer_operation op; + op.from = nathan.id; + op.to = account_id_type(); + trx.operations.push_back(op); + + graphene::app::database_api db_api(db); + set pub_keys = db_api.get_potential_signatures( trx ); - auto dan_private_key = generate_private_key("dan"); - public_key_type dan_public = dan_private_key.get_public_key(); + BOOST_CHECK( pub_keys.find( pub_key1 ) != pub_keys.end() ); + BOOST_CHECK( pub_keys.find( pub_key2 ) != pub_keys.end() ); - auto unregistered_private_key = generate_private_key("unregistered"); - public_key_type unregistered_public = unregistered_private_key.get_public_key(); + } FC_LOG_AND_RETHROW() +} +BOOST_AUTO_TEST_CASE( get_potential_signatures_other ) { + try { + fc::ecc::private_key priv_key1 = fc::ecc::private_key::regenerate(fc::digest("key1")); + public_key_type pub_key1( priv_key1.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 + const account_object& nathan = create_account( "nathan" ); + balance_claim_operation op; + op.deposit_to_account = nathan.id; + op.balance_owner_key = pub_key1; + trx.operations.push_back(op); - /*** - * Assert - */ - graphene::app::database_api db_api(db); + graphene::app::database_api db_api(db); + set pub_keys = db_api.get_potential_signatures( trx ); - 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)); + BOOST_CHECK( pub_keys.find( pub_key1 ) != pub_keys.end() ); - } FC_LOG_AND_RETHROW() - } + } FC_LOG_AND_RETHROW() +} BOOST_AUTO_TEST_SUITE_END()