diff --git a/.travis.yml b/.travis.yml index 77e0407a33..bff35d3e29 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,19 +24,9 @@ env: - CCACHE_MAXSIZE=1Gi - CCACHE_SLOPPINESS=include_file_ctime,include_file_mtime,time_macros -script: - - programs/build_helpers/buildstep -s 3500 - - ccache -s - - programs/build_helpers/buildstep Prepare 1 "sed -i '/tests/d' libraries/fc/CMakeLists.txt" - - programs/build_helpers/buildstep cmake 5 "cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON ." - - programs/build_helpers/buildstep make.cli_wallet 2200 "programs/build_helpers/make_with_sonar bw-output -j 2 cli_wallet witness_node js_operation_serializer get_dev_key network_mapper" - - programs/build_helpers/buildstep make.chain_test 1000 "make -j 2 chain_test" - - programs/build_helpers/buildstep make.cli_test 200 "make -j 2 cli_test" - - programs/build_helpers/buildstep make.perf_test 120 "make -j 2 performance_test" - - set -o pipefail - - programs/build_helpers/buildstep run.chain_test 240 "libraries/fc/tests/run-parallel-tests.sh tests/chain_test" - - programs/build_helpers/buildstep run.cli_test 60 "libraries/fc/tests/run-parallel-tests.sh tests/cli_test" - - 'programs/build_helpers/buildstep prepare.sonar 20 "find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d | while read d; do gcov -o \"\$d\" \"\${d/CMakeFiles*.dir//}\"/*.cpp; done >/dev/null; programs/build_helpers/set_sonar_branch sonar-project.properties" || true' - - 'programs/build_helpers/buildstep run.sonar 1200 "which sonar-scanner && sonar-scanner" || true' - - programs/build_helpers/buildstep end 0 - - ccache -s +jobs: + include: + - stage: build for cache + script: ./programs/build_helpers/build_protocol + - stage: build and test + script: ./programs/build_helpers/build_and_test diff --git a/CMakeLists.txt b/CMakeLists.txt index 6052748601..4540350f8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ # Defines BitShares library target. -project( BitShares ) -cmake_minimum_required( VERSION 3.1 ) +cmake_minimum_required( VERSION 3.2 FATAL_ERROR ) +project( BitShares LANGUAGES CXX C) set( BLOCKCHAIN_NAME "BitShares" ) @@ -26,6 +26,87 @@ endif() list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" ) +include(CheckCCompilerFlag) +include(Utils) + +# function to help with cUrl +macro(FIND_CURL) + if (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB) + set (OLD_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set (CMAKE_FIND_LIBRARY_SUFFIXES .a) + find_package(CURL REQUIRED) + list(APPEND CURL_LIBRARIES ssl crypto) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_SUFFIXES}) + else (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB) + find_package(CURL REQUIRED) + endif (NOT WIN32 AND NOT APPLE AND CURL_STATICLIB) +endmacro() + +# Fortify source +if (CMAKE_COMPILER_IS_GNUCXX) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + message ("-- Setting optimizations for clang++") + set(CMAKE_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1 -g") + + # check and add data execution prevention + message ("-- Enabling data execution prevention") + add_linker_flag("-fsanitize=safe-stack") + + # check and add Stack-based buffer overrun detection + set(CMAKE_REQUIRED_FLAGS "-fstack-protector") + check_c_compiler_flag("" HAVE_STACKPROTECTOR) + if(HAVE_STACKPROTECTOR) + message ("-- Enabling stack-based buffer overrun detection") + add_flag_append(CMAKE_C_FLAGS "-fstack-protector") + add_flag_append(CMAKE_CXX_FLAGS "-fstack-protector") + endif() + else () + message ("-- Setting optimizations for g++") + set(CMAKE_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-D_FORTIFY_SOURCE=2 -O3 -DNDEBUG=1 -g") + + # check and add data execution prevention + set(CMAKE_REQUIRED_FLAGS "-Wl,-znoexecstack") + check_c_compiler_flag("" HAVE_NOEXECSTACK) + if(HAVE_NOEXECSTACK) + message ("-- Enabling data execution prevention") + add_linker_flag("-znoexecstack") + endif() + + # check and add Stack-based buffer overrun detection + set(CMAKE_REQUIRED_FLAGS "-fstack-protector-strong") + check_c_compiler_flag("" HAVE_STACKPROTECTOR) + if(HAVE_STACKPROTECTOR) + message ("-- Enabling stack-based buffer overrun detection") + add_flag_append(CMAKE_C_FLAGS "-fstack-protector-strong") + add_flag_append(CMAKE_CXX_FLAGS "-fstack-protector-strong") + endif() + + endif () +endif () + +# check for Data relocation and Protection (RELRO) +set(CMAKE_REQUIRED_FLAGS "-Wl,-zrelro,-znow") +check_c_compiler_flag("" HAVE_RELROFULL) +if(HAVE_RELROFULL) + message ("-- Enabling full data relocation and protection") + add_linker_flag("-zrelro") + add_linker_flag("-znow") +else() + #if full relro is not available, try partial relro + set(CMAKE_REQUIRED_FLAGS "-Wl,-zrelro") + check_c_compiler_flag("" HAVE_RELROPARTIAL) + if(HAVE_RELROPARTIAL) + message ("-- Enabling partial data relocation and protection") + add_linker_flag("-zrelro") + endif() +endif() + +# position independent executetable (PIE) +# position independent code (PIC) +add_definitions (-fPIC) + set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") set(GRAPHENE_EGENESIS_JSON "${CMAKE_CURRENT_SOURCE_DIR}/libraries/egenesis/genesis.json" ) diff --git a/CMakeModules/Utils.cmake b/CMakeModules/Utils.cmake new file mode 100644 index 0000000000..4fdd6bb9f4 --- /dev/null +++ b/CMakeModules/Utils.cmake @@ -0,0 +1,13 @@ +macro(add_flag_append _VAR_NAME _FLAG) + set(${_VAR_NAME} "${${_VAR_NAME}} ${_FLAG}") +endmacro(add_flag_append _VAR_NAME _FLAG) + +macro(add_linker_flag _FLAG) + #executables + add_flag_append(CMAKE_C_LINK_FLAGS "-Wl,${_FLAG}") + add_flag_append(CMAKE_CXX_LINK_FLAGS "-Wl,${_FLAG}") + #libraries + add_flag_append(CMAKE_SHARED_LIBRARY_C_FLAGS "-Wl,${_FLAG}") + add_flag_append(CMAKE_SHARED_LIBRARY_CXX_FLAGS "-Wl,${_FLAG}") +endmacro(add_linker_flag _FLAG) + diff --git a/libraries/README.md b/libraries/README.md index 256d15029b..06f7fc9b8a 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -10,11 +10,12 @@ Code in libraries is the most important part of **bitshares-core** project and i Folder | Name | Description | Status ---|---|---|--- [app](app) | Application | Bundles component libraries (chain, network, plugins) into a useful application. Also provides API access. | Active -[chain](chain) | Blockchain | Defines all objects, operations and types. This include the consensus protocol, defines the whole blockchain behaviour. | Active +[chain](chain) | Blockchain | Blockchain implementation and business logic. Database structure in the form of objects and updates to the blockchain in the form of evaluators are implemented here. | Active [db](db) | Database | Defines the internal database graphene uses. | Active [egenesis](egenesis) | Genesis | Hardcodes the `genesis.json` file into the `witness_node` executable.| Active [fc](fc) | Fast-compiling C++ library | https://github.com/bitshares/bitshares-fc | Active [net](net) | Network | The graphene p2p layer. | Active [plugins](plugins) | Plugins | Collection of singleton designed modules used for extending the bitshares-core. | Active +[protocol](protocol) | Protocol | Fundamental structure of the data that will be transmitted on the wire. Operations are defined and basic data integrity checks are done for each. | Active [utilities](utilities) | Utilities | Common utility calls used in applications or other libraries. | Active [wallet](wallet) | Wallet | Wallet definition for the `cli_wallet` software. | Active diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index bf4f5c2b00..22f2dc2540 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -13,7 +13,10 @@ add_library( graphene_app ) # need to link graphene_debug_witness because plugins aren't sufficiently isolated #246 -target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_grouped_orders graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) +target_link_libraries( graphene_app + graphene_market_history graphene_account_history graphene_grouped_orders + graphene_api_helper_indexes + graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" ) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index cdb75f57fe..601afc2904 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -165,25 +165,7 @@ void application_impl::reset_p2p_node(const fc::path& data_dir) { // https://bitsharestalk.org/index.php/topic,23715.0.html vector seeds = { - "seed01.liondani.com:1776", // liondani (GERMANY) - "104.236.144.84:1777", // puppies (USA) - "128.199.143.47:2015", // Harvey (Singapore) - "209.105.239.13:1776", // sahkan (USA) - "45.35.12.22:1776", // sahkan (USA) - "51.15.61.160:1776", // lafona (France) - "bts-seed1.abit-more.com:62015", // abit (China) - "node.blckchnd.com:4243", // blckchnd (Germany) - "seed.bitsharesdex.com:50696", // iHashFury (Europe) - "seed.bitsharesnodes.com:1776", // wackou (Netherlands) - "seed.blocktrades.us:1776", // BlockTrades (USA) - "seed.cubeconnex.com:1777", // cube (USA) - "seed.roelandp.nl:1776", // roelandp (Canada) - "seed04.bts-nodes.net:1776", // Thom (Australia) - "seed05.bts-nodes.net:1776", // Thom (USA) - "seed06.bts-nodes.net:1776", // Thom (USA) - "seed07.bts-nodes.net:1776", // Thom (Singapore) - "seed.bts.bangzi.info:55501", // Bangzi (Germany) - "seeds.bitshares.eu:1776" // pc (http://seeds.quisquis.de/bitshares.html) + #include "../egenesis/seed-nodes.txt" }; for( const string& endpoint_string : seeds ) { @@ -364,6 +346,42 @@ void application_impl::set_api_limit() { if(_options->count("api-limit-get-order-book")){ _app_options.api_limit_get_order_book = _options->at("api-limit-get-order-book").as(); } + if(_options->count("api-limit-list-htlcs")){ + _app_options.api_limit_list_htlcs = _options->at("api-limit-list-htlcs").as(); + } + if(_options->count("api-limit-lookup-accounts")) { + _app_options.api_limit_lookup_accounts = _options->at("api-limit-lookup-accounts").as(); + } + if(_options->count("api-limit-lookup-witness-accounts")) { + _app_options.api_limit_lookup_witness_accounts = _options->at("api-limit-lookup-witness-accounts").as(); + } + if(_options->count("api-limit-lookup-committee-member-accounts")) { + _app_options.api_limit_lookup_committee_member_accounts = _options->at("api-limit-lookup-committee-member-accounts").as(); + } + if(_options->count("api-limit-lookup-vote-ids")) { + _app_options.api_limit_lookup_vote_ids = _options->at("api-limit-lookup-vote-ids").as(); + } + if(_options->count("api-limit-get-account-limit-orders")) { + _app_options.api_limit_get_account_limit_orders = _options->at("api-limit-get-account-limit-orders").as(); + } + if(_options->count("api-limit-get-collateral-bids")) { + _app_options.api_limit_get_collateral_bids = _options->at("api-limit-get-collateral-bids").as(); + } + if(_options->count("api-limit-get-top-markets")) { + _app_options.api_limit_get_top_markets = _options->at("api-limit-get-top-markets").as(); + } + if(_options->count("api-limit-get-trade-history")) { + _app_options.api_limit_get_trade_history = _options->at("api-limit-get-trade-history").as(); + } + if(_options->count("api-limit-get-trade-history-by-sequence")) { + _app_options.api_limit_get_trade_history_by_sequence = _options->at("api-limit-get-trade-history-by-sequence").as(); + } + if(_options->count("api-limit-get-withdraw-permissions-by-giver")) { + _app_options.api_limit_get_withdraw_permissions_by_giver = _options->at("api-limit-get-withdraw-permissions-by-giver").as(); + } + if(_options->count("api-limit-get-withdraw-permissions-by-recipient")) { + _app_options.api_limit_get_withdraw_permissions_by_recipient = _options->at("api-limit-get-withdraw-permissions-by-recipient").as(); + } } void application_impl::startup() @@ -583,7 +601,9 @@ bool application_impl::handle_block(const graphene::net::block_message& blk_msg, ("w",witness_account.name) ("i",last_irr)("d",blk_msg.block.block_num()-last_irr) ); } - FC_ASSERT( (latency.count()/1000) > -5000, "Rejecting block with timestamp in the future" ); + GRAPHENE_ASSERT( latency.count()/1000 > -5000, + graphene::net::block_timestamp_in_future_exception, + "Rejecting block with timestamp in the future", ); try { const uint32_t skip = (_is_block_producer | _force_validate) ? @@ -1057,6 +1077,28 @@ void application::set_program_options(boost::program_options::options_descriptio "For database_api_impl::get_limit_orders to set its default limit value as 300") ("api-limit-get-order-book",boost::program_options::value()->default_value(50), "For database_api_impl::get_order_book to set its default limit value as 50") + ("api-limit-lookup-accounts",boost::program_options::value()->default_value(1000), + "For database_api_impl::lookup_accounts to set its default limit values as 1000") + ("api-limit-lookup-witness-accounts",boost::program_options::value()->default_value(1000), + "For database_api_impl::lookup_witness_accounts to set its default limit values as 1000") + ("api-limit-lookup-committee-member-accounts",boost::program_options::value()->default_value(1000), + "For database_api_impl::lookup_committee_member_accounts to set its default limit values as 1000") + ("api-limit-lookup-vote-ids",boost::program_options::value()->default_value(1000), + "For database_api_impl::lookup_vote_ids to set its default limit values as 1000") + ("api-limit-get-account-limit-orders",boost::program_options::value()->default_value(101), + "For database_api_impl::get_account_limit_orders to set its default limit values as 101") + ("api-limit-get-collateral-bids",boost::program_options::value()->default_value(100), + "For database_api_impl::get_collateral_bids to set its default limit values as 100") + ("api-limit-get-top-markets",boost::program_options::value()->default_value(100), + "For database_api_impl::get_top_markets to set its default limit values as 100") + ("api-limit-get-trade-history",boost::program_options::value()->default_value(100), + "For database_api_impl::get_trade_history to set its default limit values as 100") + ("api-limit-get-trade-history-by-sequence",boost::program_options::value()->default_value(100), + "For database_api_impl::get_trade_history_by_sequence to set its default limit values as 100") + ("api-limit-get-withdraw-permissions-by-giver",boost::program_options::value()->default_value(101), + "For database_api_impl::get_withdraw_permissions_by_giver to set its default limit values as 101") + ("api-limit-get-withdraw-permissions-by-recipient",boost::program_options::value()->default_value(101), + "For database_api_impl::get_withdraw_permissions_by_recipient to set its default limit values as 101") ; command_line_options.add(configuration_file_options); command_line_options.add_options() diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6c12eeecf5..79f668566c 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -103,13 +103,13 @@ class database_api_impl : public std::enable_shared_from_this vector get_vesting_balances( const std::string account_id_or_name )const; // Assets + uint64_t get_asset_count()const; asset_id_type get_asset_id_from_string(const std::string& symbol_or_id)const; - vector> get_assets(const vector& asset_symbols_or_ids)const; - vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; - vector> lookup_asset_symbols(const vector& symbols_or_ids)const; - uint64_t get_asset_count()const; - vector get_assets_by_issuer(const std::string& issuer_name_or_id, - asset_id_type start, uint32_t limit)const; + vector> get_assets(const vector& asset_symbols_or_ids)const; + vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; + vector> lookup_asset_symbols(const vector& symbols_or_ids)const; + vector get_assets_by_issuer(const std::string& issuer_name_or_id, + asset_id_type start, uint32_t limit)const; // Markets / feeds vector get_limit_orders(const std::string& a, const std::string& b, uint32_t limit)const; @@ -183,6 +183,7 @@ class database_api_impl : public std::enable_shared_from_this optional get_htlc(htlc_id_type id) const; vector get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const; vector get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const; + vector list_htlcs(const htlc_id_type lower_bound_id, uint32_t limit) const; //private: static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); @@ -262,6 +263,20 @@ class database_api_impl : public std::enable_shared_from_this return account; } + template + extended_asset_object extend_asset( ASSET&& a )const + { + asset_id_type id = a.id; + extended_asset_object result = extended_asset_object( std::forward( a ) ); + if( amount_in_collateral_index ) + { + result.total_in_collateral = amount_in_collateral_index->get_amount_in_collateral( id ); + if( result.bitasset_data_id.valid() ) + result.total_backing_collateral = amount_in_collateral_index->get_backing_collateral( id ); + } + return result; + } + const asset_object* get_asset_from_string( const std::string& symbol_or_id, bool throw_if_not_found = true ) const { // TODO cache the result to avoid repeatly fetching from db @@ -280,16 +295,16 @@ class database_api_impl : public std::enable_shared_from_this FC_ASSERT( asset, "no such asset" ); return asset; } - vector> get_assets(const vector& asset_ids)const + vector> get_assets(const vector& asset_ids)const { - vector> result; result.reserve(asset_ids.size()); + 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 { + [this](asset_id_type id) -> optional { if(auto o = _db.find(id)) { if( _enabled_auto_subscription ) subscribe_to_item( id ); - return *o; + return extend_asset( *o ); } return {}; }); @@ -374,6 +389,7 @@ class database_api_impl : public std::enable_shared_from_this map< pair, std::function > _market_subscriptions; graphene::chain::database& _db; const application_options* _app_options = nullptr; + const graphene::api_helper_indexes::amount_in_collateral_index* amount_in_collateral_index; }; ////////////////////////////////////////////////////////////////////// @@ -405,6 +421,16 @@ database_api_impl::database_api_impl( graphene::chain::database& db, const appli _pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){ if( _pending_trx_callback ) _pending_trx_callback( fc::variant(trx, GRAPHENE_MAX_NESTED_OBJECTS) ); }); + try + { + amount_in_collateral_index = &_db.get_index_type< primary_index< call_order_index > >() + .get_secondary_index(); + } + catch( fc::assert_exception& e ) + { + wlog( "amount_in_collateral_index not found - please enable api_helper_indexes plugin!" ); + amount_in_collateral_index = nullptr; + } } database_api_impl::~database_api_impl() @@ -838,7 +864,8 @@ vector database_api::get_account_limit_orders( const string& vector database_api_impl::get_account_limit_orders( const string& account_name_or_id, const string &base, const string "e, uint32_t limit, optional ostart_id, optional ostart_price) { - FC_ASSERT( limit <= 101 ); + uint64_t api_limit_get_account_limit_orders =_app_options->api_limit_get_account_limit_orders; + FC_ASSERT( limit <= api_limit_get_account_limit_orders ); vector results; uint32_t count = 0; @@ -1143,7 +1170,8 @@ map database_api::lookup_accounts(const string& lower_bo map database_api_impl::lookup_accounts(const string& lower_bound_name, uint32_t limit)const { - FC_ASSERT( limit <= 1000 ); + uint64_t api_limit_lookup_accounts = _app_options->api_limit_lookup_accounts; + FC_ASSERT( limit <= api_limit_lookup_accounts ); const auto& accounts_by_name = _db.get_index_type().indices().get(); map result; @@ -1291,39 +1319,39 @@ asset_id_type database_api::get_asset_id_from_string(const std::string& symbol_o return my->get_asset_from_string( symbol_or_id )->id; } -vector> database_api::get_assets(const vector& asset_symbols_or_ids)const +vector> database_api::get_assets(const vector& asset_symbols_or_ids)const { return my->get_assets( asset_symbols_or_ids ); } -vector> database_api_impl::get_assets(const vector& asset_symbols_or_ids)const +vector> database_api_impl::get_assets(const vector& asset_symbols_or_ids)const { - vector> result; result.reserve(asset_symbols_or_ids.size()); + vector> result; result.reserve(asset_symbols_or_ids.size()); std::transform(asset_symbols_or_ids.begin(), asset_symbols_or_ids.end(), std::back_inserter(result), - [this](std::string id_or_name) -> optional { + [this](std::string id_or_name) -> optional { const asset_object* asset_obj = get_asset_from_string( id_or_name, false ); if( asset_obj == nullptr ) return {}; if( _enabled_auto_subscription ) subscribe_to_item( asset_obj->id ); - return *asset_obj; + return extend_asset( *asset_obj ); }); return result; } -vector database_api::list_assets(const string& lower_bound_symbol, uint32_t limit)const +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 +vector database_api_impl::list_assets(const string& lower_bound_symbol, uint32_t limit)const { uint64_t api_limit_get_assets = _app_options->api_limit_get_assets; FC_ASSERT( limit <= api_limit_get_assets ); const auto& assets_by_symbol = _db.get_index_type().indices().get(); - vector result; + vector result; result.reserve(limit); auto itr = assets_by_symbol.lower_bound(lower_bound_symbol); @@ -1332,7 +1360,7 @@ vector database_api_impl::list_assets(const string& lower_bound_sy itr = assets_by_symbol.begin(); while(limit-- && itr != assets_by_symbol.end()) - result.emplace_back(*itr++); + result.emplace_back( extend_asset( *itr++ ) ); return result; } @@ -1347,50 +1375,50 @@ uint64_t database_api_impl::get_asset_count()const return _db.get_index_type().indices().size(); } -vector database_api::get_assets_by_issuer(const std::string& issuer_name_or_id, - asset_id_type start, uint32_t limit)const +vector database_api::get_assets_by_issuer(const std::string& issuer_name_or_id, + asset_id_type start, uint32_t limit)const { return my->get_assets_by_issuer(issuer_name_or_id, start, limit); } -vector database_api_impl::get_assets_by_issuer(const std::string& issuer_name_or_id, - asset_id_type start, uint32_t limit)const +vector database_api_impl::get_assets_by_issuer(const std::string& issuer_name_or_id, + asset_id_type start, uint32_t limit)const { uint64_t api_limit_get_assets = _app_options->api_limit_get_assets; FC_ASSERT( limit <= api_limit_get_assets ); - vector result; + vector result; const account_id_type account = get_account_from_string(issuer_name_or_id)->id; const auto& asset_idx = _db.get_index_type().indices().get(); auto asset_index_end = asset_idx.end(); auto asset_itr = asset_idx.lower_bound(boost::make_tuple(account, start)); while(asset_itr != asset_index_end && asset_itr->issuer == account && result.size() < limit) { - result.push_back(*asset_itr); + result.emplace_back( extend_asset( *asset_itr ) ); ++asset_itr; } return result; } -vector> database_api::lookup_asset_symbols(const vector& symbols_or_ids)const +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 +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; + 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 { + [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, 1).as(1)); - return ptr == nullptr? optional() : *ptr; + return ptr == nullptr? optional() : extend_asset( *ptr ); } auto itr = assets_by_symbol.find(symbol_or_id); - return itr == assets_by_symbol.end()? optional() : *itr; + return itr == assets_by_symbol.end()? optional() : extend_asset( *itr ); }); return result; } @@ -1552,7 +1580,8 @@ vector database_api::get_collateral_bids(const std::strin vector database_api_impl::get_collateral_bids(const std::string& asset, uint32_t limit, uint32_t skip)const { try { - FC_ASSERT( limit <= 100 ); + uint64_t api_limit_get_collateral_bids=_app_options->api_limit_get_collateral_bids; + FC_ASSERT( limit <= api_limit_get_collateral_bids ); const asset_id_type asset_id = get_asset_from_string(asset)->id; const asset_object& swan = asset_id(_db); FC_ASSERT( swan.is_market_issued() ); @@ -1563,7 +1592,14 @@ vector database_api_impl::get_collateral_bids(const std:: auto start = aidx.lower_bound( boost::make_tuple( asset_id, price::max(back.id, asset_id), collateral_bid_id_type() ) ); auto end = aidx.lower_bound( boost::make_tuple( asset_id, price::min(back.id, asset_id), collateral_bid_id_type(GRAPHENE_DB_MAX_INSTANCE_ID) ) ); vector result; - while( skip-- > 0 && start != end ) { ++start; } + if(skip 0) { result.push_back(*start); @@ -1720,7 +1756,7 @@ vector database_api_impl::get_top_markets(uint32_t limit)const { FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." ); - FC_ASSERT( limit <= 100 ); + FC_ASSERT( limit <= _app_options->api_limit_get_top_markets ); const auto& volume_idx = _db.get_index_type().indices().get(); auto itr = volume_idx.rbegin(); @@ -1758,7 +1794,7 @@ vector database_api_impl::get_trade_history( const string& base, { FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." ); - FC_ASSERT( limit <= 100 ); + FC_ASSERT( limit <= _app_options->api_limit_get_trade_history ); auto assets = lookup_asset_symbols( {base, quote} ); FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) ); @@ -1849,7 +1885,7 @@ vector database_api_impl::get_trade_history_by_sequence( { FC_ASSERT( _app_options && _app_options->has_market_history_plugin, "Market history plugin is not enabled." ); - FC_ASSERT( limit <= 100 ); + FC_ASSERT( limit <= _app_options->api_limit_get_trade_history_by_sequence ); FC_ASSERT( start >= 0 ); int64_t start_seq = -start; @@ -1980,7 +2016,8 @@ map database_api::lookup_witness_accounts(const string& map database_api_impl::lookup_witness_accounts(const string& lower_bound_name, uint32_t limit)const { - FC_ASSERT( limit <= 1000 ); + uint64_t api_limit_lookup_witness_accounts = _app_options->api_limit_lookup_witness_accounts; + FC_ASSERT( limit <= api_limit_lookup_witness_accounts ); 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 @@ -2056,7 +2093,8 @@ map database_api::lookup_committee_member_acco map database_api_impl::lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const { - FC_ASSERT( limit <= 1000 ); + uint64_t api_limit_lookup_committee_member_accounts = _app_options->api_limit_lookup_committee_member_accounts; + FC_ASSERT( limit <= api_limit_lookup_committee_member_accounts ); 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 @@ -2154,7 +2192,8 @@ vector database_api::lookup_vote_ids( const vector& votes vector database_api_impl::lookup_vote_ids( const vector& votes )const { - FC_ASSERT( votes.size() < 1000, "Only 1000 votes can be queried at a time" ); + uint64_t api_limit_lookup_vote_ids = _app_options->api_limit_lookup_vote_ids; + FC_ASSERT( votes.size() < api_limit_lookup_vote_ids, "Only 1000 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(); @@ -2537,7 +2576,7 @@ vector database_api::get_withdraw_permissions_by_giv vector database_api_impl::get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const { - FC_ASSERT( limit <= 101 ); + FC_ASSERT( limit <= _app_options->api_limit_get_withdraw_permissions_by_giver ); vector result; const auto& withdraw_idx = _db.get_index_type().indices().get(); @@ -2559,7 +2598,7 @@ vector database_api::get_withdraw_permissions_by_rec vector database_api_impl::get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const { - FC_ASSERT( limit <= 101 ); + FC_ASSERT( limit <= _app_options->api_limit_get_withdraw_permissions_by_recipient ); vector result; const auto& withdraw_idx = _db.get_index_type().indices().get(); @@ -2642,6 +2681,26 @@ vector database_api_impl::get_htlc_by_to(const std::string account_ return result; } +vector database_api::list_htlcs(const htlc_id_type start, uint32_t limit)const +{ + return my->list_htlcs(start, limit); +} + +vector database_api_impl::list_htlcs(const htlc_id_type start, uint32_t limit) const +{ + FC_ASSERT( limit <= _app_options->api_limit_list_htlcs ); + + vector result; + const auto& htlc_idx = _db.get_index_type().indices().get(); + auto itr = htlc_idx.lower_bound(start); + while(itr != htlc_idx.end() && result.size() < limit) + { + result.push_back(*itr); + ++itr; + } + return result; +} + ////////////////////////////////////////////////////////////////////// // // // Private methods // diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index c7e0f7f9bc..c85fab14b2 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -65,7 +65,7 @@ namespace graphene { namespace app { uint64_t min_val; uint64_t max_val; }; - + struct verify_range_proof_rewind_result { bool success; @@ -109,7 +109,7 @@ namespace graphene { namespace app { price max_price; ///< possible highest price in the group share_type total_for_sale; ///< total amount of asset for sale, asset id is min_price.base.asset_id }; - + /** * @brief The history_api class implements the RPC API for account history * @@ -171,7 +171,7 @@ namespace graphene { namespace app { )const; /** - * @breif Get operations relevant to the specified account referenced + * @brief Get operations relevant to the specified account referenced * by an event numbering specific to the account. The current number of operations * for the account can be found in the account statistics (or use 0 for start). * @param account_id_or_name The account ID or name whose history should be queried @@ -200,11 +200,11 @@ namespace graphene { namespace app { * @brief Get OHLCV data of a trading pair in a time range * @param a Asset symbol or ID in a trading pair * @param b The other asset symbol or ID in the trading pair - * @param bucket_seconds Length of each time bucket in seconds. + * @param bucket_seconds Length of each time bucket in seconds. * Note: it need to be within result of get_market_history_buckets() API, otherwise no data will be returned * @param start The start of a time range, E.G. "2018-01-01T00:00:00" * @param end The end of the time range - * @return A list of OHLCV data, in "least recent first" order. + * @return A list of OHLCV data, in "least recent first" order. * If there are more than 200 records in the specified time range, the first 200 records will be returned. */ vector get_market_history( std::string a, std::string b, uint32_t bucket_seconds, @@ -212,9 +212,9 @@ namespace graphene { namespace app { /** * @brief Get OHLCV time bucket lengths supported (configured) by this API server - * @return A list of time bucket lengths in seconds. E.G. if the result contains a number "300", + * @return A list of time bucket lengths in seconds. E.G. if the result contains a number "300", * it means this API server supports OHLCV data aggregated in 5-minute buckets. - */ + */ flat_set get_market_history_buckets()const; private: application& _app; @@ -270,15 +270,18 @@ namespace graphene { namespace app { */ void broadcast_transaction(const precomputable_transaction& trx); - /** this version of broadcast transaction registers a callback method that will be called when the transaction is - * included into a block. The callback method includes the transaction id, block number, and transaction number in the - * block. + /** This version of broadcast transaction registers a callback method that will be called when the + * transaction is included into a block. The callback method includes the transaction id, block number, + * and transaction number in the block. + * @param cb the callback method + * @param trx the transaction */ void broadcast_transaction_with_callback( confirmation_callback cb, const precomputable_transaction& trx); - /** this version of broadcast transaction registers a callback method that will be called when the transaction is - * included into a block. The callback method includes the transaction id, block number, and transaction number in the - * block. + /** This version of broadcast transaction waits until the transaction is included into a block, + * then the transaction id, block number, and transaction number in the block will be returned. + * @param trx the transaction + * @return info about the block including the transaction */ fc::variant broadcast_transaction_synchronous(const precomputable_transaction& trx); @@ -348,22 +351,25 @@ namespace graphene { namespace app { private: application& _app; }; - + + /** + * @brief The crypto_api class allows computations related to blinded transfers. + */ class crypto_api { public: crypto_api(); - + /** - * @brief Generates a pedersen commitment: *commit = blind * G + value * G2. + * @brief Generates a pedersen commitment: *commit = blind * G + value * G2. * The commitment is 33 bytes, the blinding factor is 32 bytes. - * For more information about pederson commitment check next url https://en.wikipedia.org/wiki/Commitment_scheme + * For more information about pederson commitment check url https://en.wikipedia.org/wiki/Commitment_scheme * @param blind Sha-256 blind factor type * @param value Positive 64-bit integer value * @return A 33-byte pedersen commitment: *commit = blind * G + value * G2 */ fc::ecc::commitment_type blind( const fc::ecc::blind_factor_type& blind, uint64_t value ); - + /** * @brief Get sha-256 blind factor type * @param blinds_in List of sha-256 blind factor types @@ -371,18 +377,21 @@ namespace graphene { namespace app { * @return A blind factor type */ fc::ecc::blind_factor_type blind_sum( const std::vector& blinds_in, uint32_t non_neg ); - + /** * @brief Verifies that commits + neg_commits + excess == 0 * @param commits_in List of 33-byte pedersen commitments * @param neg_commits_in List of 33-byte pedersen commitments - * @param excess Sum of two list of 33-byte pedersen commitments where sums the first set and subtracts the second - * @return Boolean - true in event of commits + neg_commits + excess == 0, otherwise false + * @param excess Sum of two list of 33-byte pedersen commitments + * where sums the first set and subtracts the second + * @return Boolean - true in event of commits + neg_commits + excess == 0, otherwise false */ - bool verify_sum( - const std::vector& commits_in, const std::vector& neg_commits_in, int64_t excess + bool verify_sum( + const std::vector& commits_in, + const std::vector& neg_commits_in, + int64_t excess ); - + /** * @brief Verifies range proof for 33-byte pedersen commitment * @param commit 33-byte pedersen commitment @@ -390,27 +399,27 @@ namespace graphene { namespace app { * @return A structure with success, min and max values */ verify_range_result verify_range( const fc::ecc::commitment_type& commit, const std::vector& proof ); - + /** - * @brief Proves with respect to min_value the range for pedersen + * @brief Proves with respect to min_value the range for pedersen * commitment which has the provided blinding factor and value * @param min_value Positive 64-bit integer value * @param commit 33-byte pedersen commitment * @param commit_blind Sha-256 blind factor type for the correct digits * @param nonce Sha-256 blind factor type for our non-forged signatures - * @param exp Exponents base 10 in range [-1 ; 18] inclusively + * @param base10_exp Exponents base 10 in range [-1 ; 18] inclusively * @param min_bits 8-bit positive integer, must be in range [0 ; 64] inclusively * @param actual_value 64-bit positive integer, must be greater or equal min_value * @return A list of characters as proof in proof */ - std::vector range_proof_sign( uint64_t min_value, - const commitment_type& commit, - const blind_factor_type& commit_blind, + std::vector range_proof_sign( uint64_t min_value, + const commitment_type& commit, + const blind_factor_type& commit_blind, const blind_factor_type& nonce, int8_t base10_exp, uint8_t min_bits, uint64_t actual_value ); - + /** * @brief Verifies range proof rewind for 33-byte pedersen commitment * @param nonce Sha-256 blind refactor type @@ -419,23 +428,23 @@ namespace graphene { namespace app { * @return A structure with success, min, max, value_out, blind_out and message_out values */ verify_range_proof_rewind_result verify_range_proof_rewind( const blind_factor_type& nonce, - const fc::ecc::commitment_type& commit, + const fc::ecc::commitment_type& commit, const std::vector& proof ); - + /** - * @brief Gets "range proof" info. The cli_wallet includes functionality for sending blind transfers + * @brief Gets "range proof" info. The cli_wallet includes functionality for sending blind transfers * in which the values of the input and outputs amounts are “blinded.” - * In the case where a transaction produces two or more outputs, (e.g. an amount to the intended - * recipient plus “change” back to the sender), + * In the case where a transaction produces two or more outputs, (e.g. an amount to the intended + * recipient plus “change” back to the sender), * a "range proof" must be supplied to prove that none of the outputs commit to a negative value. * @param proof List of proof's characters * @return A range proof info structure with exponent, mantissa, min and max values - */ + */ range_proof_info range_get_info( const std::vector& proof ); }; /** - * @brief + * @brief The asset_api class allows query of info about asset holders. */ class asset_api { @@ -477,17 +486,18 @@ namespace graphene { namespace app { class orders_api { public: - orders_api(application& app):_app(app), database_api( std::ref(*app.chain_database()), &(app.get_options()) ){} + orders_api(application& app) + :_app(app), database_api( std::ref(*app.chain_database()), &(app.get_options()) ){} //virtual ~orders_api() {} /** - * @breif Get tracked groups configured by the server. + * @brief Get tracked groups configured by the server. * @return A list of numbers which indicate configured groups, of those, 1 means 0.01% diff on price. */ flat_set get_tracked_groups()const; /** - * @breif Get grouped limit orders in given market. + * @brief Get grouped limit orders in given market. * * @param base_asset ID or symbol of asset being sold * @param quote_asset ID or symbol of asset being purchased @@ -535,8 +545,8 @@ namespace graphene { namespace app { * @param password Password to login with * @return True if logged in successfully; false otherwise * - * @note This must be called prior to requesting other APIs. Other APIs may not be accessible until the client - * has sucessfully authenticated. + * @note This must be called prior to requesting other APIs. + * Other APIs may not be accessible until the client has sucessfully authenticated. */ bool login(const string& user, const string& password); /// @brief Retrieve the network block API @@ -588,11 +598,11 @@ FC_REFLECT( graphene::app::history_operation_detail, (total_count)(operation_history_objs) ) FC_REFLECT( graphene::app::limit_order_group, (min_price)(max_price)(total_for_sale) ) -//FC_REFLECT_TYPENAME( fc::ecc::compact_signature ); -//FC_REFLECT_TYPENAME( fc::ecc::commitment_type ); +//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_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) diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index 30dfc53f2a..eae2929e58 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -55,6 +55,18 @@ namespace graphene { namespace app { uint64_t api_limit_get_assets = 101; uint64_t api_limit_get_limit_orders = 300; uint64_t api_limit_get_order_book = 50; + uint64_t api_limit_list_htlcs = 100; + uint64_t api_limit_lookup_accounts = 1000; + uint64_t api_limit_lookup_witness_accounts = 1000; + uint64_t api_limit_lookup_committee_member_accounts = 1000; + uint64_t api_limit_lookup_vote_ids = 1000; + uint64_t api_limit_get_account_limit_orders = 101; + uint64_t api_limit_get_collateral_bids = 100; + uint64_t api_limit_get_top_markets = 100; + uint64_t api_limit_get_trade_history = 100; + uint64_t api_limit_get_trade_history_by_sequence = 100; + uint64_t api_limit_get_withdraw_permissions_by_giver = 101; + uint64_t api_limit_get_withdraw_permissions_by_recipient = 101; }; class application diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 13734e751b..af48fe2de9 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -125,6 +126,16 @@ struct market_trade account_id_type side2_account_id = GRAPHENE_NULL_ACCOUNT; }; +struct extended_asset_object : asset_object +{ + extended_asset_object() {} + explicit extended_asset_object( const asset_object& a ) : asset_object( a ) {} + explicit extended_asset_object( asset_object&& a ) : asset_object( std::move(a) ) {} + + optional total_in_collateral; + optional total_backing_collateral; +}; + /** * @brief The database_api class implements the RPC API for the chain database. * @@ -158,13 +169,13 @@ class database_api /** * @brief Register a callback handle which then can be used to subscribe to object database changes * @param cb The callback handle to register - * @param nofity_remove_create Whether subscribe to universal object creation and removal events. + * @param notify_remove_create Whether subscribe to universal object creation and removal events. * If this is set to true, the API server will notify all newly created objects and ID of all * newly removed objects to the client, no matter whether client subscribed to the objects. * By default, API servers don't allow subscribing to universal events, which can be changed * on server startup. * - * Note: auto-subscription is enabled by default and can be disabled with "set_auto_subscription" API. + * Note: auto-subscription is enabled by default and can be disabled with @ref set_auto_subscription API. */ void set_subscribe_callback( std::function cb, bool notify_remove_create ); /** @@ -215,12 +226,11 @@ class database_api /** * @brief Retrieve multiple block header by block numbers - * @param block_num vector containing heights of the block whose header should be returned + * @param block_nums vector containing heights of the block whose header should be returned * @return array of headers of the referenced blocks, or null if no matching block was found */ map> get_block_header_batch(const vector block_nums)const; - /** * @brief Retrieve a full, signed block * @param block_num Height of the block to be returned @@ -230,6 +240,9 @@ class database_api /** * @brief used to fetch an individual transaction. + * @param block_num height of the block to fetch + * @param trx_in_block the index (sequence number) of the transaction in the block, starts from 0 + * @return the transaction at the given position */ processed_transaction get_transaction( uint32_t block_num, uint32_t trx_in_block )const; @@ -237,20 +250,23 @@ class database_api * If the transaction has not expired, this method will return the transaction for the given ID or * it will return NULL if it is not known. Just because it is not known does not mean it wasn't * included in the blockchain. + * + * @param txid hash of the transaction + * @return the corresponding transaction if found, or null if not found */ - optional get_recent_transaction_by_id( const transaction_id_type& id )const; + optional get_recent_transaction_by_id( const transaction_id_type& txid )const; ///////////// // Globals // ///////////// /** - * @brief Retrieve the @ref chain_property_object associated with the chain + * @brief Retrieve the @ref graphene::chain::chain_property_object associated with the chain */ chain_property_object get_chain_properties()const; /** - * @brief Retrieve the current @ref global_property_object + * @brief Retrieve the current @ref graphene::chain::global_property_object */ global_property_object get_global_properties()const; @@ -265,7 +281,7 @@ class database_api chain_id_type get_chain_id()const; /** - * @brief Retrieve the current @ref dynamic_global_property_object + * @brief Retrieve the current @ref graphene::chain::dynamic_global_property_object */ dynamic_global_property_object get_dynamic_global_properties()const; @@ -273,86 +289,70 @@ class database_api // Keys // ////////// - vector> get_key_references( vector key )const; + /** + * @brief Get all accounts that refer to the specified public keys in their owner authority, active authorities + * or memo key + * @param keys a list of public keys to query + * @return ID of all accounts that refer to the specified keys + */ + vector> get_key_references( vector keys )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; + /** + * 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 // ////////////// - /** - * @brief Get account object from a name or ID - * @param account_name_or_id ID or name of the accounts - * @return Account ID - * - */ - account_id_type get_account_id_from_string(const std::string& name_or_id) const; - /** - * @brief Get a list of accounts by ID - * @param account_names_or_ids IDs or names of the accounts to retrieve - * @return The accounts corresponding to the provided IDs + * @brief Get account object from a name or ID + * @param name_or_id name or ID of the account + * @return Account ID * - * This function has semantics identical to @ref get_objects */ - vector> get_accounts(const vector& account_names_or_ids)const; + account_id_type get_account_id_from_string(const std::string& name_or_id) const; /** - * @brief Fetch all orders relevant to the specified account and specified market, result orders - * are sorted descendingly by price + * @brief Get a list of accounts by names or IDs + * @param account_names_or_ids names or IDs of the accounts to retrieve + * @return The accounts corresponding to the provided names or IDs * - * @param account_name_or_id The name or ID of an account to retrieve - * @param base Base asset - * @param quote Quote asset - * @param limit The limitation of items each query can fetch, not greater than 101 - * @param start_id Start order id, fetch orders which price lower than this order, or price equal to this order - * but order ID greater than this order - * @param start_price Fetch orders with price lower than or equal to this price - * - * @return List of orders from @ref account_name_or_id to the corresponding account - * - * @note - * 1. if @ref account_name_or_id cannot be tied to an account, empty result will be returned - * 2. @ref start_id and @ref start_price can be empty, if so the api will return the "first page" of orders; - * if start_id is specified, its price will be used to do page query preferentially, otherwise the start_price - * will be used; start_id and start_price may be used cooperatively in case of the order specified by start_id - * was just canceled accidentally, in such case, the result orders' price may lower or equal to start_price, - * but orders' id greater than start_id + * This function has semantics identical to @ref get_objects */ - vector get_account_limit_orders( const string& account_name_or_id, - const string &base, - const string "e, - uint32_t limit = 101, - optional ostart_id = optional(), - optional ostart_price = optional()); + vector> get_accounts(const vector& account_names_or_ids)const; /** - * @brief Fetch all objects relevant to the specified accounts and subscribe to updates - * @param callback Function to call with updates + * @brief Fetch all objects relevant to the specified accounts and optionally subscribe to 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 + * @param subscribe whether subscribe to updates + * @return Map of string from @p 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 + * accounts. If any of the strings in @p 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 info of an account by name + * @param name Name of the account to retrieve + * @return The account holding the provided name + */ 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. + * @brief Get all accounts that refer to the specified account in their owner or active authorities + * @param account_name_or_id Account name or ID to query + * @return all accounts that refer to the specified account in their owner or active authorities */ - vector get_account_references( const std::string account_id_or_name )const; + vector get_account_references( const std::string account_name_or_id )const; /** * @brief Get a list of accounts by name @@ -377,21 +377,37 @@ class database_api /** * @brief Get an account's balances in various assets - * @param account_name_or_id ID or name of the account to get balances for + * @param account_name_or_id name or 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(const std::string& account_name_or_id, const flat_set& assets)const; + vector get_account_balances( const std::string& account_name_or_id, + const flat_set& assets )const; - /// Semantically equivalent to @ref get_account_balances, but takes a name instead of an ID. + /// Semantically equivalent to @ref get_account_balances. vector get_named_account_balances(const std::string& name, const flat_set& assets)const; - /** @return all unclaimed balance objects for a set of addresses */ + /** + * @brief Return all unclaimed balance objects for a list of addresses + * @param addrs a list of addresses + * @return all unclaimed balance objects for the addresses + */ vector get_balance_objects( const vector
& addrs )const; + /** + * @brief Calculate how much assets in the given balance objects are able to be claimed at current head + * block time + * @param objs a list of balance object IDs + * @return a list indicating how much asset in each balance object is available to be claimed + */ vector get_vested_balances( const vector& objs )const; - vector get_vesting_balances( const std::string account_id_or_name )const; + /** + * @brief Return all vesting balance objects owned by an account + * @param account_name_or_id name or ID of an account + * @return all vesting balance objects owned by the account + */ + vector get_vesting_balances( const std::string account_name_or_id )const; /** * @brief Get the total number of accounts registered with the blockchain @@ -402,21 +418,21 @@ class database_api // Assets // //////////// - /** - * @brief Get asset id from a symbol or ID - * @param symbol_or_id ID or symbol of the asset - * @return asset id - */ + /** + * @brief Get asset ID from an asset symbol or ID + * @param symbol_or_id symbol name or ID of the asset + * @return asset ID + */ asset_id_type get_asset_id_from_string(const std::string& symbol_or_id) const; /** - * @brief Get a list of assets by ID - * @param asset_symbols_or_ids Symbol names or IDs of the assets to retrieve - * @return The assets corresponding to the provided IDs + * @brief Get a list of assets by symbol names or IDs + * @param asset_symbols_or_ids symbol names or IDs of the assets to retrieve + * @return The assets corresponding to the provided symbol names or IDs * * This function has semantics identical to @ref get_objects */ - vector> get_assets(const vector& asset_symbols_or_ids)const; + vector> get_assets(const vector& asset_symbols_or_ids)const; /** * @brief Get assets alphabetically by symbol name @@ -424,16 +440,16 @@ class database_api * @param limit Maximum number of assets to fetch (must not exceed 101) * @return The assets found */ - vector list_assets(const string& lower_bound_symbol, uint32_t limit)const; + 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 + * @brief Get a list of assets by symbol names or IDs + * @param symbols_or_ids symbol names or 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 + * This function has semantics identical to @ref get_objects, but doesn't subscribe */ - vector> lookup_asset_symbols(const vector& symbols_or_ids)const; + vector> lookup_asset_symbols(const vector& symbols_or_ids)const; /** * @brief Get assets count @@ -442,14 +458,14 @@ class database_api uint64_t get_asset_count()const; /** - * @brief Get asset objects issued from a given account - * @param account_name_or_id Account name or ID to get objects from + * @brief Get assets issued (owned) by a given account + * @param issuer_name_or_id Account name or ID to get objects from * @param start Asset objects(1.3.X) before this ID will be skipped in results. Pagination purposes. * @param limit Maximum number of orders to retrieve - * @return The assets issued by the account + * @return The assets issued (owned) by the account */ - vector get_assets_by_issuer(const std::string& issuer_name_or_id, - asset_id_type start, uint32_t limit)const; + vector get_assets_by_issuer(const std::string& issuer_name_or_id, + asset_id_type start, uint32_t limit)const; ///////////////////// // Markets / feeds // @@ -457,23 +473,53 @@ class database_api /** * @brief Get limit orders in a given market - * @param a Symbol or ID of asset being sold - * @param b Symbol or ID of asset being purchased + * @param a symbol or ID of asset being sold + * @param b symbol or 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(std::string a, std::string b, uint32_t limit)const; /** - * @brief Get call orders in a given asset - * @param a Symbol or ID of asset being called + * @brief Fetch all orders relevant to the specified account and specified market, result orders + * are sorted descendingly by price + * + * @param account_name_or_id The name or ID of an account to retrieve + * @param base Base asset + * @param quote Quote asset + * @param limit The limitation of items each query can fetch, not greater than 101 + * @param ostart_id Start order id, fetch orders which price lower than this order, + * or price equal to this order but order ID greater than this order + * @param ostart_price Fetch orders with price lower than or equal to this price + * + * @return List of orders from @p account_name_or_id to the corresponding account + * + * @note + * 1. if @p account_name_or_id cannot be tied to an account, empty result will be returned + * 2. @p ostart_id and @p ostart_price can be empty, if so the api will return the "first page" of orders; + * if @p ostart_id is specified, its price will be used to do page query preferentially, + * otherwise the @p ostart_price will be used; + * @p ostart_id and @p ostart_price may be used cooperatively in case of the order specified by @p ostart_id + * was just canceled accidentally, in such case, the result orders' price may lower or equal to + * @p ostart_price, but orders' id greater than @p ostart_id + */ + vector get_account_limit_orders( const string& account_name_or_id, + const string &base, + const string "e, + uint32_t limit = 101, + optional ostart_id = optional(), + optional ostart_price = optional()); + + /** + * @brief Get call orders (aka margin positions) for a given asset + * @param a symbol name or ID of the debt asset * @param limit Maximum number of orders to retrieve * @return The call orders, ordered from earliest to be called to latest */ vector get_call_orders(const std::string& a, uint32_t limit)const; /** - * @brief Get call orders from a given account + * @brief Get call orders (aka margin positions) of a given account * @param account_name_or_id Account name or ID to get objects from * @param start Asset objects(1.3.X) before this ID will be skipped in results. Pagination purposes. * @param limit Maximum number of objects to retrieve @@ -497,8 +543,9 @@ class database_api * @param limit Maximum number of orders to retrieve * @return The settle orders of the account */ - vector get_settle_orders_by_account(const std::string& account_name_or_id, - force_settlement_id_type start, uint32_t limit)const; + vector get_settle_orders_by_account( const std::string& account_name_or_id, + force_settlement_id_type start, + uint32_t limit )const; /** * @brief Get collateral_bid_objects for a given asset @@ -510,15 +557,19 @@ class database_api vector get_collateral_bids(const std::string& a, uint32_t limit, uint32_t start)const; /** - * @return all open margin positions for a given account id or name. + * @brief Get all open margin positions of a given account + * @param account_name_or_id name or ID of an account + * @return all open margin positions of the account + * + * Similar to @ref get_call_orders_by_account, but without pagination. */ - vector get_margin_positions( const std::string account_id_or_name )const; + vector get_margin_positions( const std::string account_name_or_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 Symbol or ID - * @param b Second asset Symbol or ID + * @param a symbol name or ID of the first asset + * @param b symbol name or ID of the second asset * * Callback will be passed a variant containing a vector>. The vector will * contain, in order, the operations which changed the market, and their results. @@ -528,32 +579,32 @@ class database_api /** * @brief Unsubscribe from updates to a given market - * @param a First asset Symbol ID - * @param b Second asset Symbol ID + * @param a symbol name or ID of the first asset + * @param b symbol name or ID of the second asset */ void unsubscribe_from_market( const std::string& a, const std::string& b ); /** * @brief Returns the ticker for the market assetA:assetB - * @param a String name of the first asset - * @param b String name of the second asset + * @param base symbol name or ID of the base asset + * @param quote symbol name or ID of the quote asset * @return The market ticker for the past 24 hours. */ market_ticker get_ticker( const string& base, const string& quote )const; /** * @brief Returns the 24 hour volume for the market assetA:assetB - * @param a String name of the first asset - * @param b String name of the second asset + * @param base symbol name or ID of the base asset + * @param quote symbol name or ID of the quote asset * @return The market volume over the past 24 hours */ market_volume get_24_volume( const string& base, const string& quote )const; /** * @brief Returns the order book for the market base:quote - * @param base String name of the first asset - * @param quote String name of the second asset - * @param depth of the order book. Up to depth of each asks and bids, capped at 50. Prioritizes most moderate of each + * @param base symbol name or ID of the base asset + * @param quote symbol name or ID of the quote asset + * @param limit depth of the order book to retrieve, for bids and asks each, capped at 50 * @return Order book of the market */ order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const; @@ -570,7 +621,7 @@ class database_api * @brief Returns recent trades for the market base:quote, ordered by time, most recent first. * Note: Currently, timezone offsets are not supported. The time must be UTC. The range is [stop, start). * In case when there are more than 100 trades occurred in the same second, this API only returns - * the first 100 records, can use another API `get_trade_history_by_sequence` to query for the rest. + * the first 100 records, can use another API @ref get_trade_history_by_sequence to query for the rest. * @param base symbol or ID of the base asset * @param quote symbol or ID of the quote asset * @param start Start time as a UNIX timestamp, the latest trade to retrieve @@ -613,10 +664,10 @@ class database_api /** * @brief Get the witness owned by a given account - * @param account_id_or_name The ID of the account whose witness should be retrieved + * @param account_name_or_id The name or 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(const std::string account_id_or_name)const; + fc::optional get_witness_by_account(const std::string account_name_or_id)const; /** * @brief Get names and IDs for registered witnesses @@ -642,14 +693,15 @@ class database_api * * This function has semantics identical to @ref get_objects */ - vector> get_committee_members(const vector& committee_member_ids)const; + vector> get_committee_members( + const vector& committee_member_ids)const; /** * @brief Get the committee_member owned by a given account - * @param account The ID or name of the account whose committee_member should be retrieved + * @param account_name_or_id The name or 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(const std::string account_id_or_name)const; + fc::optional get_committee_member_by_account( const string account_name_or_id )const; /** * @brief Get names and IDs for registered committee_members @@ -657,7 +709,8 @@ class database_api * @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; + map lookup_committee_member_accounts( const string& lower_bound_name, + uint32_t limit )const; /** * @brief Get the total number of committee registered with the blockchain @@ -678,10 +731,10 @@ class database_api /** * @brief Get the workers owned by a given account - * @param account_id_or_name The ID or name of the account whose worker should be retrieved - * @return The worker object, or null if the account does not have a worker + * @param account_name_or_id The name or ID of the account whose worker should be retrieved + * @return A list of worker objects owned by the account */ - vector> get_workers_by_account(const std::string account_id_or_name)const; + vector> get_workers_by_account(const std::string account_name_or_id)const; /** * @brief Get the total number of workers registered with the blockchain @@ -695,12 +748,14 @@ class database_api /////////// /** - * @brief Given a set of votes, return the objects they are voting for. + * @brief Given a set of votes, return the objects they are voting for + * @param votes a list of vote IDs + * @return the referenced objects * - * This will be a mixture of committee_member_object, witness_objects, and worker_objects + * This will be a mixture of committee_member_objects, 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. + * 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; @@ -708,65 +763,103 @@ class database_api // Authority / validation // //////////////////////////// - /// @brief Get a hexdump of the serialized binary form of a transaction + /** + * @brief Get a hexdump of the serialized binary form of a transaction + * @param trx a transaction to get hexdump from + * @return the hexdump of the transaction + */ std::string get_transaction_hex(const signed_transaction& trx)const; - /// @brief Get a hexdump of the serialized binary form of a - /// signatures-stripped transaction + /** + * @brief Get a hexdump of the serialized binary form of a signatures-stripped transaction + * @param trx a transaction to get hexdump from + * @return the hexdump of the transaction without the signatures + */ std::string get_transaction_hex_without_sig( 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. + * 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. + * + * @param trx the transaction to be signed + * @param available_keys a set of public keys + * @return a subset of @p available_keys that could sign for the given transaction */ - set get_required_signatures( const signed_transaction& trx, const flat_set& available_keys )const; + 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. + * 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. + * + * @param trx the transaction to be signed + * @return a set of public keys that could possibly sign for the given transaction */ set get_potential_signatures( const signed_transaction& trx )const; + + /** + * This method will return the set of all addresses that could possibly sign for a given transaction. + * + * @param trx the transaction to be signed + * @return a set of addresses that could possibly sign for the given transaction + */ set
get_potential_address_signatures( const signed_transaction& trx )const; /** - * @return true of the @ref trx has all of the required signatures, otherwise throws an exception + * Check whether a transaction has all of the required signatures + * @param trx a transaction to be verified + * @return true if the @p trx has all of the required signatures, otherwise throws an exception */ bool verify_authority( const signed_transaction& trx )const; /** * @brief Verify that the public keys have enough authority to approve an operation for this account - * @param account_name_or_id the account to check + * @param account_name_or_id name or ID of an account to check * @param signers the public keys * @return true if the passed in keys have enough authority to approve an operation for this account */ - bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; + bool verify_account_authority( const string& account_name_or_id, + const flat_set& signers )const; /** - * Validates a transaction against the current state without broadcasting it on the network. + * @brief Validates a transaction against the current state without broadcasting it on the network + * @param trx a transaction to be validated + * @return a processed_transaction object if the transaction passes the validation, + otherwise an exception will be thrown */ processed_transaction validate_transaction( const signed_transaction& trx )const; /** - * For each operation calculate the required fee in the specified asset type. + * @brief For each operation calculate the required fee in the specified asset type + * @param ops a list of operations to be query for required fees + * @param asset_symbol_or_id symbol name or ID of an asset that to be used to pay the fees + * @return a list of objects which indicates required fees of each operation */ - vector< fc::variant > get_required_fees( const vector& ops, const std::string& asset_id_or_symbol )const; + vector< fc::variant > get_required_fees( const vector& ops, + const std::string& asset_symbol_or_id )const; /////////////////////////// // Proposed transactions // /////////////////////////// /** - * @return the set of proposed transactions relevant to the specified account id. + * @brief return a set of proposed transactions (aka proposals) that the specified account + * can add approval to or remove approval from + * @param account_name_or_id The name or ID of an account + * @return a set of proposed transactions that the specified account can act on */ - vector get_proposed_transactions( const std::string account_id_or_name )const; + vector get_proposed_transactions( const std::string account_name_or_id )const; ////////////////////// // Blinded balances // ////////////////////// /** - * @return the set of blinded balance objects by commitment ID + * @brief return the set of blinded balance objects by commitment ID + * @param commitments a set of commitments to query for + * @return the set of blinded balance objects by commitment ID */ vector get_blinded_balances( const flat_set& commitments )const; @@ -776,21 +869,27 @@ class database_api /** * @brief Get non expired withdraw permission objects for a giver(ex:recurring customer) - * @param account Account ID or name to get objects from - * @param start Withdraw permission objects(1.12.X) before this ID will be skipped in results. Pagination purposes. + * @param account_name_or_id Account name or ID to get objects from + * @param start Withdraw permission objects(1.12.X) before this ID will be skipped in results. + * Pagination purposes. * @param limit Maximum number of objects to retrieve * @return Withdraw permission objects for the account */ - vector get_withdraw_permissions_by_giver(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; + vector get_withdraw_permissions_by_giver( const std::string account_name_or_id, + withdraw_permission_id_type start, + uint32_t limit )const; /** * @brief Get non expired withdraw permission objects for a recipient(ex:service provider) - * @param account Account ID or name to get objects from - * @param start Withdraw permission objects(1.12.X) before this ID will be skipped in results. Pagination purposes. + * @param account_name_or_id Account name or ID to get objects from + * @param start Withdraw permission objects(1.12.X) before this ID will be skipped in results. + * Pagination purposes. * @param limit Maximum number of objects to retrieve * @return Withdraw permission objects for the account */ - vector get_withdraw_permissions_by_recipient(const std::string account_id_or_name, withdraw_permission_id_type start, uint32_t limit)const; + vector get_withdraw_permissions_by_recipient( const std::string account_name_or_id, + withdraw_permission_id_type start, + uint32_t limit )const; ////////// // HTLC // @@ -805,21 +904,34 @@ class database_api /** * @brief Get non expired HTLC objects using the sender account - * @param account_id_or_name Account ID or name to get objects from + * @param account_name_or_id Account name or ID to get objects from * @param start htlc objects before this ID will be skipped in results. Pagination purposes. * @param limit Maximum number of objects to retrieve * @return HTLC objects for the account */ - vector get_htlc_by_from(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const; + vector get_htlc_by_from( const std::string account_name_or_id, + htlc_id_type start, + uint32_t limit ) const; /** * @brief Get non expired HTLC objects using the receiver account - * @param account_id_or_name Account ID or name to get objects from + * @param account_name_or_id Account name or ID to get objects from * @param start htlc objects before this ID will be skipped in results. Pagination purposes. * @param limit Maximum number of objects to retrieve * @return HTLC objects for the account */ - vector get_htlc_by_to(const std::string account_id_or_name, htlc_id_type start, uint32_t limit) const; + vector get_htlc_by_to( const std::string account_name_or_id, + htlc_id_type start, + uint32_t limit ) const; + + /** + * @brief Get all HTLCs + * @param start Lower bound of htlc id to start getting results + * @param limit Maximum number of htlc objects to fetch + * @return The htlc object list + */ + vector list_htlcs(const htlc_id_type start, uint32_t limit) const; + private: std::shared_ptr< database_api_impl > my; @@ -836,6 +948,9 @@ FC_REFLECT( graphene::app::market_ticker, FC_REFLECT( graphene::app::market_volume, (time)(base)(quote)(base_volume)(quote_volume) ); FC_REFLECT( graphene::app::market_trade, (sequence)(date)(price)(amount)(value)(side1_account_id)(side2_account_id) ); +FC_REFLECT_DERIVED( graphene::app::extended_asset_object, (graphene::chain::asset_object), + (total_in_collateral)(total_backing_collateral) ); + FC_API(graphene::app::database_api, // Objects (get_objects) @@ -953,4 +1068,5 @@ FC_API(graphene::app::database_api, (get_htlc) (get_htlc_by_from) (get_htlc_by_to) + (list_htlcs) ) diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 549159da72..2244043920 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -194,16 +194,6 @@ FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_bitasset_data_object, (gr (feed_cer_updated) ) -FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_object, (graphene::db::object), - (symbol) - (precision) - (issuer) - (options) - (dynamic_asset_data_id) - (bitasset_data_id) - (buyback_account) - ) - GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_object ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object ) GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object ) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index ace071ec0d..7eb518d181 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -636,7 +636,12 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx auto& trx_idx = get_mutable_index_type(); const chain_id_type& chain_id = get_chain_id(); if( !(skip & skip_transaction_dupe_check) ) - FC_ASSERT( trx_idx.indices().get().find(trx.id()) == trx_idx.indices().get().end() ); + { + GRAPHENE_ASSERT( trx_idx.indices().get().find(trx.id()) == trx_idx.indices().get().end(), + duplicate_transaction, + "Transaction '${txid}' is already in the database", + ("txid",trx.id()) ); + } transaction_evaluation_state eval_state(this); const chain_parameters& chain_parameters = get_global_properties().parameters; eval_state._trx = &trx; diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 3a8f038f52..9ca657ef6f 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -65,8 +65,7 @@ void database::reindex( fc::path data_dir ) ilog( "reindexing blockchain" ); auto start = fc::time_point::now(); const auto last_block_num = last_block->block_num(); - uint32_t flush_point = last_block_num < 10000 ? 0 : last_block_num - 10000; - uint32_t undo_point = last_block_num < 50 ? 0 : last_block_num - 50; + uint32_t undo_point = last_block_num < GRAPHENE_MAX_UNDO_HISTORY ? 0 : last_block_num - GRAPHENE_MAX_UNDO_HISTORY; ilog( "Replaying blocks, starting at ${next}...", ("next",head_block_num() + 1) ); if( head_block_num() >= undo_point ) @@ -138,7 +137,7 @@ void database::reindex( fc::path data_dir ) ("last", last_block_num) ); } - if( i == flush_point ) + if( i == undo_point ) { ilog( "Writing database to disk at block ${i}", ("i",i) ); flush(); diff --git a/libraries/chain/exceptions.cpp b/libraries/chain/exceptions.cpp index 68606a91f6..37f72607dd 100644 --- a/libraries/chain/exceptions.cpp +++ b/libraries/chain/exceptions.cpp @@ -29,7 +29,7 @@ namespace graphene { namespace chain { // Internal exceptions - FC_IMPLEMENT_DERIVED_EXCEPTION( internal_exception, graphene::chain::chain_exception, 3990000, "internal exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( internal_exception, chain_exception, 3990000, "internal exception" ) GRAPHENE_IMPLEMENT_INTERNAL_EXCEPTION( verify_auth_max_auth_exceeded, 1, "Exceeds max authority fan-out" ) GRAPHENE_IMPLEMENT_INTERNAL_EXCEPTION( verify_auth_account_not_found, 2, "Auth account not found" ) @@ -39,35 +39,66 @@ namespace graphene { namespace chain { FC_IMPLEMENT_EXCEPTION( chain_exception, 3000000, "blockchain exception" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( database_query_exception, chain_exception, 3010000, "database query exception" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( block_validate_exception, chain_exception, 3020000, "block validation exception" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( operation_validate_exception, chain_exception, 3040000, "operation validation exception" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( operation_evaluate_exception, chain_exception, 3050000, "operation evaluation exception" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( utility_exception, chain_exception, 3060000, "utility method exception" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( undo_database_exception, chain_exception, 3070000, "undo database exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( database_query_exception, chain_exception, 3010000, + "database query exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( block_validate_exception, chain_exception, 3020000, + "block validation exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( transaction_process_exception,chain_exception, 3030000, + "transaction processing exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( operation_validate_exception, chain_exception, 3040000, + "operation validation exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( operation_evaluate_exception, chain_exception, 3050000, + "operation evaluation exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( utility_exception, chain_exception, 3060000, + "utility method exception" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( undo_database_exception, chain_exception, 3070000, + "undo database exception" ) FC_IMPLEMENT_DERIVED_EXCEPTION( unlinkable_block_exception, chain_exception, 3080000, "unlinkable block" ) FC_IMPLEMENT_DERIVED_EXCEPTION( black_swan_exception, chain_exception, 3090000, "black swan" ) FC_IMPLEMENT_DERIVED_EXCEPTION( plugin_exception, chain_exception, 3100000, "plugin exception" ) FC_IMPLEMENT_DERIVED_EXCEPTION( insufficient_feeds, chain_exception, 37006, "insufficient feeds" ) - FC_IMPLEMENT_DERIVED_EXCEPTION( pop_empty_chain, undo_database_exception, 3070001, "there are no blocks to pop" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( duplicate_transaction, transaction_process_exception, 3030001, + "duplicate transaction" ) + + FC_IMPLEMENT_DERIVED_EXCEPTION( pop_empty_chain, undo_database_exception, 3070001, + "there are no blocks to pop" ) GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( transfer ); GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1, "owner mismatch" ) GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2, "owner mismatch" ) GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( restricted_transfer_asset, transfer, 3, "restricted transfer asset" ) - //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( limit_order_create ); - //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( limit_order_cancel ); + GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( limit_order_create ); + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( kill_unfilled, limit_order_create, 1, + "Killing limit order due to unable to fill" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( market_not_whitelisted, limit_order_create, 2, + "The market has not been whitelisted by the selling asset" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( market_blacklisted, limit_order_create, 3, + "The market has been blacklisted by the selling asset" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( selling_asset_unauthorized, limit_order_create, 4, + "The account is not allowed to transact the selling asset" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( receiving_asset_unauthorized, limit_order_create, 5, + "The account is not allowed to transact the receiving asset" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( insufficient_balance, limit_order_create, 6, + "Insufficient balance" ) + + GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( limit_order_cancel ); + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( nonexist_order, limit_order_cancel, 1, "Order does not exist" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( owner_mismatch, limit_order_cancel, 2, "Order owned by someone else" ) + GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( call_order_update ); - GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1, "Updating call order would trigger a margin call that cannot be fully filled" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1, + "Updating call order would trigger a margin call that cannot be fully filled" ) GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_create ); GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1, "Exceeds max authority fan-out" ) GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2, "Auth account not found" ) - GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3, "Incorrect issuer specified for account" ) - GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4, "Cannot create buyback for asset which already has buyback" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3, + "Incorrect issuer specified for account" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4, + "Cannot create buyback for asset which already has buyback" ) GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( buyback_too_many_markets, account_create, 5, "Too many buyback markets" ) GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( account_update ); @@ -95,7 +126,8 @@ namespace graphene { namespace chain { GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( proposal_create ); GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1, "review_period required" ) - GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2, "review_period insufficient" ) + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2, + "review_period insufficient" ) //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( proposal_update ); //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( proposal_delete ); @@ -120,7 +152,8 @@ namespace graphene { namespace chain { GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" ) GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( blind_transfer ); - GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" ); + GRAPHENE_IMPLEMENT_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, + "Attempting to claim an unknown prior commitment" ); //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( transfer_from_blind_operation ) //GRAPHENE_IMPLEMENT_OP_BASE_EXCEPTIONS( asset_claim_fees_operation ) diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 1a4ad3e47d..3541f87f5b 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -323,7 +323,16 @@ MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_object) MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_dynamic_data_object) MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_bitasset_data_object) -FC_REFLECT_TYPENAME( graphene::chain::asset_object ) +FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object), + (symbol) + (precision) + (issuer) + (options) + (dynamic_asset_data_id) + (bitasset_data_id) + (buyback_account) + ) + FC_REFLECT_TYPENAME( graphene::chain::asset_bitasset_data_object ) FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_object ) diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index 0aa9b3db4a..e2100bdfe8 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -55,7 +55,7 @@ #op_name "_operation evaluation exception" \ ) -#define GRAPHENE_DECLARE_OP_VALIDATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \ +#define GRAPHENE_DECLARE_OP_VALIDATE_EXCEPTION( exc_name, op_name, seqnum ) \ FC_DECLARE_DERIVED_EXCEPTION( \ op_name ## _ ## exc_name, \ graphene::chain::op_name ## _validate_exception, \ @@ -72,7 +72,7 @@ msg \ ) -#define GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( exc_name, op_name, seqnum, msg ) \ +#define GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( exc_name, op_name, seqnum ) \ FC_DECLARE_DERIVED_EXCEPTION( \ op_name ## _ ## exc_name, \ graphene::chain::op_name ## _evaluate_exception, \ @@ -110,6 +110,7 @@ namespace graphene { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( database_query_exception, chain_exception, 3010000 ) FC_DECLARE_DERIVED_EXCEPTION( block_validate_exception, chain_exception, 3020000 ) + FC_DECLARE_DERIVED_EXCEPTION( transaction_process_exception,chain_exception, 3030000 ) FC_DECLARE_DERIVED_EXCEPTION( operation_validate_exception, chain_exception, 3040000 ) FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, chain_exception, 3050000 ) FC_DECLARE_DERIVED_EXCEPTION( utility_exception, chain_exception, 3060000 ) @@ -120,28 +121,40 @@ namespace graphene { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, chain_exception, 37006 ) + FC_DECLARE_DERIVED_EXCEPTION( duplicate_transaction, transaction_process_exception, 3030001 ) + FC_DECLARE_DERIVED_EXCEPTION( pop_empty_chain, undo_database_exception, 3070001 ) GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1, "owner mismatch" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2, "owner mismatch" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( restricted_transfer_asset, transfer, 3, "restricted transfer asset" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( from_account_not_whitelisted, transfer, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( to_account_not_whitelisted, transfer, 2 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( restricted_transfer_asset, transfer, 3 ) + + GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_create ); + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( kill_unfilled, limit_order_create, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( market_not_whitelisted, limit_order_create, 2 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( market_blacklisted, limit_order_create, 3 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( selling_asset_unauthorized, limit_order_create, 4 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( receiving_asset_unauthorized, limit_order_create, 5 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( insufficient_balance, limit_order_create, 6 ) + + GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_cancel ); + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( nonexist_order, limit_order_cancel, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, limit_order_cancel, 2 ) - //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_create ); - //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( limit_order_cancel ); GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( call_order_update ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1, "Updating call order would trigger a margin call that cannot be fully filled" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1 ) GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1, "Exceeds max authority fan-out" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2, "Auth account not found" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3, "Incorrect issuer specified for account" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4, "Cannot create buyback for asset which already has buyback" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_too_many_markets, account_create, 5, "Too many buyback markets" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_create, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_create, 2 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_incorrect_issuer, account_create, 3 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_already_exists, account_create, 4 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( buyback_too_many_markets, account_create, 5 ) GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_update, 1, "Exceeds max authority fan-out" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_update, 2, "Auth account not found" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( max_auth_exceeded, account_update, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( auth_account_not_found, account_update, 2 ) //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_whitelist ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_upgrade ); @@ -153,7 +166,7 @@ namespace graphene { namespace chain { //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_issue ); GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_on_mia, asset_reserve, 1, "invalid on mia" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_on_mia, asset_reserve, 1 ) //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_fund_fee_pool ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_settle ); @@ -163,8 +176,8 @@ namespace graphene { namespace chain { //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( witness_create ); GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_create ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1, "review_period required" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2, "review_period insufficient" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_required, proposal_create, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( review_period_insufficient, proposal_create, 2 ) //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_update ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( proposal_delete ); @@ -181,15 +194,15 @@ namespace graphene { namespace chain { //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( assert ); GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( balance_claim ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( claimed_too_often, balance_claim, 1, "balance claimed too often" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_claim_amount, balance_claim, 2, "invalid claim amount" ) - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, balance_claim, 3, "owner mismatch" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( claimed_too_often, balance_claim, 1 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_claim_amount, balance_claim, 2 ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( owner_mismatch, balance_claim, 3 ) GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( override_transfer ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1, "not permitted" ) + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( not_permitted, override_transfer, 1 ) GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( blind_transfer ); - GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1, "Attempting to claim an unknown prior commitment" ); + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unknown_commitment, blind_transfer, 1 ) //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( transfer_from_blind_operation ) //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_claim_fees_operation ) diff --git a/libraries/chain/market_evaluator.cpp b/libraries/chain/market_evaluator.cpp index 18ebbe0180..4b6d9af928 100644 --- a/libraries/chain/market_evaluator.cpp +++ b/libraries/chain/market_evaluator.cpp @@ -48,19 +48,32 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o _receive_asset = &op.min_to_receive.asset_id(d); if( _sell_asset->options.whitelist_markets.size() ) - FC_ASSERT( _sell_asset->options.whitelist_markets.find(_receive_asset->id) - != _sell_asset->options.whitelist_markets.end(), - "This market has not been whitelisted." ); + { + GRAPHENE_ASSERT( _sell_asset->options.whitelist_markets.find(_receive_asset->id) + != _sell_asset->options.whitelist_markets.end(), + limit_order_create_market_not_whitelisted, + "This market has not been whitelisted by the selling asset", ); + } if( _sell_asset->options.blacklist_markets.size() ) - FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) - == _sell_asset->options.blacklist_markets.end(), - "This market has been blacklisted." ); + { + GRAPHENE_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) + == _sell_asset->options.blacklist_markets.end(), + limit_order_create_market_blacklisted, + "This market has been blacklisted by the selling asset", ); + } - FC_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ) ); - FC_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ) ); + GRAPHENE_ASSERT( is_authorized_asset( d, *_seller, *_sell_asset ), + limit_order_create_selling_asset_unauthorized, + "The account is not allowed to transact the selling asset", ); - FC_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell, "insufficient balance", - ("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) ); + GRAPHENE_ASSERT( is_authorized_asset( d, *_seller, *_receive_asset ), + limit_order_create_receiving_asset_unauthorized, + "The account is not allowed to transact the receiving asset", ); + + GRAPHENE_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell, + limit_order_create_insufficient_balance, + "insufficient balance", + ("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) ); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -119,7 +132,10 @@ object_id_type limit_order_create_evaluator::do_apply(const limit_order_create_o else filled = db().apply_order( new_order_object ); - FC_ASSERT( !op.fill_or_kill || filled ); + GRAPHENE_ASSERT( !op.fill_or_kill || filled, + limit_order_create_kill_unfilled, + "Killing limit order ${op} due to unable to fill", + ("op",op) ); return order_id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -128,8 +144,17 @@ void_result limit_order_cancel_evaluator::do_evaluate(const limit_order_cancel_o { try { database& d = db(); - _order = &o.order(d); - FC_ASSERT( _order->seller == o.fee_paying_account ); + _order = d.find( o.order ); + + GRAPHENE_ASSERT( _order != nullptr, + limit_order_cancel_nonexist_order, + "Limit order ${oid} does not exist", + ("oid", o.order) ); + + GRAPHENE_ASSERT( _order->seller == o.fee_paying_account, + limit_order_cancel_owner_mismatch, + "Limit order ${oid} is owned by someone else", + ("oid", o.order) ); return void_result(); } FC_CAPTURE_AND_RETHROW( (o) ) } diff --git a/libraries/egenesis/genesis-dev.json b/libraries/egenesis/genesis-dev.json index 7d6f03a46c..dcc2cdd913 100644 --- a/libraries/egenesis/genesis-dev.json +++ b/libraries/egenesis/genesis-dev.json @@ -237,7 +237,12 @@ "accounts_per_fee_scale": 1000, "account_fee_scale_bitshifts": 4, "max_authority_depth": 2, - "extensions": [] + "extensions": { + "updatable_htlc_options": { + "max_timeout_secs": 2592000, + "max_preimage_size": 1024000 + } + } }, "initial_accounts": [{ "name": "init0", diff --git a/libraries/egenesis/seed-nodes.txt b/libraries/egenesis/seed-nodes.txt new file mode 100644 index 0000000000..a372b79e43 --- /dev/null +++ b/libraries/egenesis/seed-nodes.txt @@ -0,0 +1,15 @@ +// https://bitsharestalk.org/index.php/topic,23715.0.html +"seed01.liondani.com:1776", // liondani (GERMANY) +"209.105.239.13:1776", // sahkan (USA) +"45.35.12.22:1776", // sahkan (USA) +"bts.lafona.net:1776", // lafona (France) +"bts-seed1.abit-more.com:62015", // abit (China) +"node.blckchnd.com:4243", // blckchnd (Germany) +"seed.bitsharesdex.com:50696", // iHashFury (Europe) +"seed.roelandp.nl:1776", // roelandp (Canada) +"seed04.bts-nodes.net:1776", // Thom (Australia) +"seed05.bts-nodes.net:1776", // Thom (USA) +"seed06.bts-nodes.net:1776", // Thom (USA) +"seed07.bts-nodes.net:1776", // Thom (Singapore) +"seed.bts.bangzi.info:55501", // Bangzi (Germany) +"seeds.bitshares.eu:1776", // pc (http://seeds.quisquis.de/bitshares.html) diff --git a/libraries/net/exceptions.cpp b/libraries/net/exceptions.cpp index 7b6e23dc5d..5900eec66f 100644 --- a/libraries/net/exceptions.cpp +++ b/libraries/net/exceptions.cpp @@ -37,5 +37,7 @@ namespace graphene { namespace net { FC_IMPLEMENT_DERIVED_EXCEPTION( peer_is_on_an_unreachable_fork, net_exception, 90005, "peer is on another fork" ) FC_IMPLEMENT_DERIVED_EXCEPTION( unlinkable_block_exception, net_exception, 90006, "unlinkable block" ) + FC_IMPLEMENT_DERIVED_EXCEPTION( block_timestamp_in_future_exception, net_exception, 90007, + "block timestamp in the future" ) } } diff --git a/libraries/net/include/graphene/net/exceptions.hpp b/libraries/net/include/graphene/net/exceptions.hpp index e95eea8759..2a47d333eb 100644 --- a/libraries/net/include/graphene/net/exceptions.hpp +++ b/libraries/net/include/graphene/net/exceptions.hpp @@ -34,5 +34,6 @@ namespace graphene { namespace net { FC_DECLARE_DERIVED_EXCEPTION( block_older_than_undo_history, net_exception, 90004 ) FC_DECLARE_DERIVED_EXCEPTION( peer_is_on_an_unreachable_fork, net_exception, 90005 ) FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, net_exception, 90006 ) + FC_DECLARE_DERIVED_EXCEPTION( block_timestamp_in_future_exception, net_exception, 90007 ) } } diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index f570dac5bf..fc2a086ee2 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -79,6 +79,7 @@ #include #include +#include // Nasty hack: A circular dependency around fee_schedule is resolved by fwd-declaring it and using a shared_ptr // to it in chain_parameters, which is used in an operation and thus must be serialized by the net library. // Resolving that forward declaration doesn't happen until now: @@ -2417,7 +2418,8 @@ namespace graphene { namespace net { namespace detail { } - void node_impl::on_closing_connection_message( peer_connection* originating_peer, const closing_connection_message& closing_connection_message_received ) + void node_impl::on_closing_connection_message( peer_connection* originating_peer, + const closing_connection_message& closing_connection_message_received ) { VERIFY_CORRECT_THREAD(); originating_peer->they_have_requested_close = true; @@ -2429,12 +2431,14 @@ namespace graphene { namespace net { namespace detail { ( "msg", closing_connection_message_received.reason_for_closing ) ( "error", closing_connection_message_received.error ) ); std::ostringstream message; - message << "Peer " << fc::variant( originating_peer->get_remote_endpoint(), GRAPHENE_NET_MAX_NESTED_OBJECTS ).as_string() << + message << "Peer " << fc::variant( originating_peer->get_remote_endpoint(), + GRAPHENE_NET_MAX_NESTED_OBJECTS ).as_string() << " disconnected us: " << closing_connection_message_received.reason_for_closing; - fc::exception detailed_error(FC_LOG_MESSAGE(warn, "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}", - ( "peer", originating_peer->get_remote_endpoint() ) - ( "msg", closing_connection_message_received.reason_for_closing ) - ( "error", closing_connection_message_received.error ) )); + fc::exception detailed_error(FC_LOG_MESSAGE(warn, + "Peer ${peer} is disconnecting us because of an error: ${msg}, exception: ${error}", + ( "peer", originating_peer->get_remote_endpoint() ) + ( "msg", closing_connection_message_received.reason_for_closing ) + ( "error", closing_connection_message_received.error ) )); _delegate->error_encountered( message.str(), detailed_error ); } @@ -2571,11 +2575,20 @@ namespace graphene { namespace net { namespace detail { } catch (const fc::exception& e) { + auto block_num = block_message_to_send.block.block_num(); wlog("Failed to push sync block ${num} (id:${id}): client rejected sync block sent by peer: ${e}", - ("num", block_message_to_send.block.block_num()) + ("num", block_num) ("id", block_message_to_send.block_id) ("e", e)); - handle_message_exception = e; + if( e.code() == block_timestamp_in_future_exception::code_enum::code_value ) + { + handle_message_exception = block_timestamp_in_future_exception( FC_LOG_MESSAGE( warn, "", + ("block_header", static_cast(block_message_to_send.block)) + ("block_num", block_num) + ("block_id", block_message_to_send.block_id) ) ); + } + else + handle_message_exception = e; } // build up lists for any potentially-blocking operations we need to do, then do them @@ -2667,7 +2680,8 @@ namespace graphene { namespace net { namespace detail { { ASSERT_TASK_NOT_PREEMPTED(); // don't yield while iterating over _active_connections - if (peer->ids_of_items_being_processed.find(block_message_to_send.block_id) != peer->ids_of_items_being_processed.end()) + if (peer->ids_of_items_being_processed.find(block_message_to_send.block_id) + != peer->ids_of_items_being_processed.end()) { if (discontinue_fetching_blocks_from_peer) { @@ -2676,7 +2690,9 @@ namespace graphene { namespace net { namespace detail { peer->inhibit_fetching_sync_blocks = true; } else - peers_to_disconnect[peer] = std::make_pair(std::string("You offered us a block that we reject as invalid"), fc::oexception(handle_message_exception)); + peers_to_disconnect[peer] = std::make_pair( + std::string("You offered us a block that we reject as invalid"), + fc::oexception(handle_message_exception)); } } } @@ -2985,11 +3001,21 @@ namespace graphene { namespace net { namespace detail { catch (const fc::exception& e) { // client rejected the block. Disconnect the client and any other clients that offered us this block - wlog("Failed to push block ${num} (id:${id}), client rejected block sent by peer", - ("num", block_message_to_process.block.block_num()) - ("id", block_message_to_process.block_id)); + auto block_num = block_message_to_process.block.block_num(); + wlog("Failed to push block ${num} (id:${id}), client rejected block sent by peer: ${e}", + ("num", block_num) + ("id", block_message_to_process.block_id) + ("e",e)); - disconnect_exception = e; + if( e.code() == block_timestamp_in_future_exception::code_enum::code_value ) + { + disconnect_exception = block_timestamp_in_future_exception( FC_LOG_MESSAGE( warn, "", + ("block_header", static_cast(block_message_to_process.block)) + ("block_num", block_num) + ("block_id", block_message_to_process.block_id) ) ); + } + else + disconnect_exception = e; disconnect_reason = "You offered me a block that I have deemed to be invalid"; peers_to_disconnect.insert( originating_peer->shared_from_this() ); @@ -3346,7 +3372,7 @@ namespace graphene { namespace net { namespace detail { } void node_impl::on_get_current_connections_reply_message(peer_connection* originating_peer, - const get_current_connections_reply_message& get_current_connections_reply_message_received) + const get_current_connections_reply_message& get_current_connections_reply_message_received) { VERIFY_CORRECT_THREAD(); } @@ -3358,19 +3384,22 @@ namespace graphene { namespace net { namespace detail { // this just passes the message to the client, and does the bookkeeping // related to requesting and rebroadcasting the message. void node_impl::process_ordinary_message( peer_connection* originating_peer, - const message& message_to_process, const message_hash_type& message_hash ) + const message& message_to_process, + const message_hash_type& message_hash ) { VERIFY_CORRECT_THREAD(); fc::time_point message_receive_time = fc::time_point::now(); // only process it if we asked for it - auto iter = originating_peer->items_requested_from_peer.find( item_id(message_to_process.msg_type.value(), message_hash) ); + auto iter = originating_peer->items_requested_from_peer.find( + item_id(message_to_process.msg_type.value(), message_hash) ); if( iter == originating_peer->items_requested_from_peer.end() ) { wlog( "received a message I didn't ask for from peer ${endpoint}, disconnecting from peer", ( "endpoint", originating_peer->get_remote_endpoint() ) ); - fc::exception detailed_error( FC_LOG_MESSAGE(error, "You sent me a message that I didn't ask for, message_hash: ${message_hash}", - ( "message_hash", message_hash ) ) ); + fc::exception detailed_error( FC_LOG_MESSAGE(error, + "You sent me a message that I didn't ask for, message_hash: ${message_hash}", + ( "message_hash", message_hash ) ) ); disconnect_from_peer( originating_peer, "You sent me a message that I didn't request", true, detailed_error ); return; } @@ -3387,7 +3416,8 @@ namespace graphene { namespace net { namespace detail { if (message_to_process.msg_type.value() == trx_message_type) { trx_message transaction_message_to_process = message_to_process.as(); - dlog("passing message containing transaction ${trx} to client", ("trx", transaction_message_to_process.trx.id())); + dlog( "passing message containing transaction ${trx} to client", + ("trx", transaction_message_to_process.trx.id()) ); _delegate->handle_transaction(transaction_message_to_process); } else @@ -3400,14 +3430,36 @@ namespace graphene { namespace net { namespace detail { } catch ( const fc::exception& e ) { - wlog( "client rejected message sent by peer ${peer}, ${e}", ("peer", originating_peer->get_remote_endpoint() )("e", e) ); + switch( e.code() ) + { + // log common exceptions in debug level + case graphene::chain::duplicate_transaction::code_enum::code_value : + case graphene::chain::limit_order_create_kill_unfilled::code_enum::code_value : + case graphene::chain::limit_order_create_market_not_whitelisted::code_enum::code_value : + case graphene::chain::limit_order_create_market_blacklisted::code_enum::code_value : + case graphene::chain::limit_order_create_selling_asset_unauthorized::code_enum::code_value : + case graphene::chain::limit_order_create_receiving_asset_unauthorized::code_enum::code_value : + case graphene::chain::limit_order_create_insufficient_balance::code_enum::code_value : + case graphene::chain::limit_order_cancel_nonexist_order::code_enum::code_value : + case graphene::chain::limit_order_cancel_owner_mismatch::code_enum::code_value : + dlog( "client rejected message sent by peer ${peer}, ${e}", + ("peer", originating_peer->get_remote_endpoint() )("e", e) ); + break; + // log rarer exceptions in warn level + default: + wlog( "client rejected message sent by peer ${peer}, ${e}", + ("peer", originating_peer->get_remote_endpoint() )("e", e) ); + break; + } // record it so we don't try to fetch this item again - _recently_failed_items.insert(peer_connection::timestamped_item_id(item_id(message_to_process.msg_type.value(), message_hash ), fc::time_point::now())); + _recently_failed_items.insert( peer_connection::timestamped_item_id( + item_id( message_to_process.msg_type.value(), message_hash ), fc::time_point::now() ) ); return; } // finally, if the delegate validated the message, broadcast it to our other peers - message_propagation_data propagation_data{message_receive_time, message_validated_time, originating_peer->node_id}; + message_propagation_data propagation_data { message_receive_time, message_validated_time, + originating_peer->node_id }; broadcast( message_to_process, propagation_data ); } } diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index caacb8bd53..412b185e08 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory( delayed_node ) add_subdirectory( debug_witness ) add_subdirectory( snapshot ) add_subdirectory( es_objects ) +add_subdirectory( api_helper_indexes ) diff --git a/libraries/plugins/api_helper_indexes/CMakeLists.txt b/libraries/plugins/api_helper_indexes/CMakeLists.txt new file mode 100644 index 0000000000..26c1f16699 --- /dev/null +++ b/libraries/plugins/api_helper_indexes/CMakeLists.txt @@ -0,0 +1,22 @@ +file(GLOB HEADERS "include/graphene/api_helper_indexes/*.hpp") + +add_library( graphene_api_helper_indexes + api_helper_indexes.cpp + ) + +target_link_libraries( graphene_api_helper_indexes graphene_chain graphene_app ) +target_include_directories( graphene_api_helper_indexes + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if(MSVC) + set_source_files_properties(api_helper_indexes.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) + +install( TARGETS + graphene_api_helper_indexes + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/api_helper_indexes" ) diff --git a/libraries/plugins/api_helper_indexes/api_helper_indexes.cpp b/libraries/plugins/api_helper_indexes/api_helper_indexes.cpp new file mode 100644 index 0000000000..e99e29b9c1 --- /dev/null +++ b/libraries/plugins/api_helper_indexes/api_helper_indexes.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018 api_helper_indexes 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 + +namespace graphene { namespace api_helper_indexes { + +void amount_in_collateral_index::object_inserted( const object& objct ) +{ try { + const call_order_object& o = static_cast( objct ); + + { + auto itr = in_collateral.find( o.collateral_type() ); + if( itr == in_collateral.end() ) + in_collateral[o.collateral_type()] = o.collateral; + else + itr->second += o.collateral; + } + + { + auto itr = backing_collateral.find( o.debt_type() ); + if( itr == backing_collateral.end() ) + backing_collateral[o.debt_type()] = o.collateral; + else + itr->second += o.collateral; + } + +} FC_CAPTURE_AND_RETHROW( (objct) ); } + +void amount_in_collateral_index::object_removed( const object& objct ) +{ try { + const call_order_object& o = static_cast( objct ); + + { + auto itr = in_collateral.find( o.collateral_type() ); + if( itr != in_collateral.end() ) // should always be true + itr->second -= o.collateral; + } + + { + auto itr = backing_collateral.find( o.debt_type() ); + if( itr != backing_collateral.end() ) // should always be true + itr->second -= o.collateral; + } + +} FC_CAPTURE_AND_RETHROW( (objct) ); } + +void amount_in_collateral_index::about_to_modify( const object& objct ) +{ try { + object_removed( objct ); +} FC_CAPTURE_AND_RETHROW( (objct) ); } + +void amount_in_collateral_index::object_modified( const object& objct ) +{ try { + object_inserted( objct ); +} FC_CAPTURE_AND_RETHROW( (objct) ); } + +share_type amount_in_collateral_index::get_amount_in_collateral( const asset_id_type& asset )const +{ try { + auto itr = in_collateral.find( asset ); + if( itr == in_collateral.end() ) return 0; + return itr->second; +} FC_CAPTURE_AND_RETHROW( (asset) ); } + +share_type amount_in_collateral_index::get_backing_collateral( const asset_id_type& asset )const +{ try { + auto itr = backing_collateral.find( asset ); + if( itr == backing_collateral.end() ) return 0; + return itr->second; +} FC_CAPTURE_AND_RETHROW( (asset) ); } + +namespace detail +{ + +class api_helper_indexes_impl +{ + public: + api_helper_indexes_impl(api_helper_indexes& _plugin) + : _self( _plugin ) + { } + + graphene::chain::database& database() + { + return _self.database(); + } + + api_helper_indexes& _self; + + private: + +}; + +} // end namespace detail + +api_helper_indexes::api_helper_indexes() : + my( new detail::api_helper_indexes_impl(*this) ) +{ +} + +api_helper_indexes::~api_helper_indexes() +{ +} + +std::string api_helper_indexes::plugin_name()const +{ + return "api_helper_indexes"; +} +std::string api_helper_indexes::plugin_description()const +{ + return "Provides some helper indexes used by various API calls"; +} + +void api_helper_indexes::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg + ) +{ +} + +void api_helper_indexes::plugin_initialize(const boost::program_options::variables_map& options) +{ +} + +void api_helper_indexes::plugin_startup() +{ + ilog("api_helper_indexes: plugin_startup() begin"); + amount_in_collateral = database().add_secondary_index< primary_index, amount_in_collateral_index >(); + for( const auto& call : database().get_index_type().indices() ) + amount_in_collateral->object_inserted( call ); +} + +} } diff --git a/libraries/plugins/api_helper_indexes/include/graphene/api_helper_indexes/api_helper_indexes.hpp b/libraries/plugins/api_helper_indexes/include/graphene/api_helper_indexes/api_helper_indexes.hpp new file mode 100644 index 0000000000..400b3000b1 --- /dev/null +++ b/libraries/plugins/api_helper_indexes/include/graphene/api_helper_indexes/api_helper_indexes.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018 api_helper_indexes 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. + */ +#pragma once + +#include +#include + +namespace graphene { namespace api_helper_indexes { +using namespace chain; + +/** + * @brief This secondary index tracks how much of each asset is locked up as collateral for MPAs, and how much + * collateral is backing an MPA in total. + */ +class amount_in_collateral_index : public secondary_index +{ + public: + virtual void object_inserted( const object& obj ) override; + virtual void object_removed( const object& obj ) override; + virtual void about_to_modify( const object& before ) override; + virtual void object_modified( const object& after ) override; + + share_type get_amount_in_collateral( const asset_id_type& asset )const; + share_type get_backing_collateral( const asset_id_type& asset )const; + + private: + flat_map in_collateral; + flat_map backing_collateral; +}; + +namespace detail +{ + class api_helper_indexes_impl; +} + +class api_helper_indexes : public graphene::app::plugin +{ + public: + api_helper_indexes(); + virtual ~api_helper_indexes(); + + std::string plugin_name()const override; + std::string plugin_description()const override; + virtual void plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map& options) override; + virtual void plugin_startup() override; + + friend class detail::api_helper_indexes_impl; + std::unique_ptr my; + + private: + amount_in_collateral_index* amount_in_collateral = nullptr; +}; + +} } //graphene::template diff --git a/libraries/plugins/elasticsearch/CMakeLists.txt b/libraries/plugins/elasticsearch/CMakeLists.txt index ce9c7c1d1d..c7810f4eb3 100644 --- a/libraries/plugins/elasticsearch/CMakeLists.txt +++ b/libraries/plugins/elasticsearch/CMakeLists.txt @@ -3,7 +3,9 @@ file(GLOB HEADERS "include/graphene/elasticsearch/*.hpp") add_library( graphene_elasticsearch elasticsearch_plugin.cpp ) -find_package(CURL REQUIRED) + +find_curl() + include_directories(${CURL_INCLUDE_DIRS}) if(MSVC) set_source_files_properties(elasticsearch_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) diff --git a/libraries/plugins/es_objects/CMakeLists.txt b/libraries/plugins/es_objects/CMakeLists.txt index 42d18a6580..2cae2ffde4 100644 --- a/libraries/plugins/es_objects/CMakeLists.txt +++ b/libraries/plugins/es_objects/CMakeLists.txt @@ -3,7 +3,9 @@ file(GLOB HEADERS "include/graphene/es_objects/*.hpp") add_library( graphene_es_objects es_objects.cpp ) -find_package(CURL REQUIRED) + +find_curl() + include_directories(${CURL_INCLUDE_DIRS}) if(CURL_STATICLIB) SET_TARGET_PROPERTIES(graphene_es_objects PROPERTIES diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index 9c01ea2149..6b4a3558c4 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -371,7 +371,8 @@ void market_history_plugin_impl::update_market_histories( const signed_block& b } else // if all data are rolled out { - if( last_min_his_id != _meta->rolling_min_order_his_id ) // if rolled out some + if( !_meta->skip_min_order_his_id + || 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; diff --git a/libraries/plugins/witness/include/graphene/witness/witness.hpp b/libraries/plugins/witness/include/graphene/witness/witness.hpp index 0d2eab27ac..8ca09a5b27 100644 --- a/libraries/plugins/witness/include/graphene/witness/witness.hpp +++ b/libraries/plugins/witness/include/graphene/witness/witness.hpp @@ -71,6 +71,7 @@ class witness_plugin : public graphene::app::plugin { void schedule_production_loop(); block_production_condition::block_production_condition_enum block_production_loop(); block_production_condition::block_production_condition_enum maybe_produce_block( fc::limited_mutable_variant_object& capture ); + void add_private_key(const std::string& key_id_to_wif_pair_string); /// Fetch signing keys of all witnesses in the cache from object database and update the cache accordingly void refresh_witness_key_cache(); diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index eda18b1ce8..05bbb9564d 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -29,6 +29,9 @@ #include #include +#include + +#include #include @@ -74,6 +77,10 @@ void witness_plugin::plugin_set_program_options( DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), "Tuple of [PublicKey, WIF private key] (may specify multiple times)") + ("private-key-file", bpo::value>()->composing()->multitoken(), + "Path to a file containing tuples of [PublicKey, WIF private key]." + " The file has to contain exactly one tuple (i.e. private - public key pair) per line." + " This option may be specified multiple times, thus multiple files can be provided.") ; config_file_options.add(command_line_options); } @@ -83,6 +90,32 @@ std::string witness_plugin::plugin_name()const return "witness"; } +void witness_plugin::add_private_key(const std::string& key_id_to_wif_pair_string) +{ + auto key_id_to_wif_pair = graphene::app::dejsonify> + (key_id_to_wif_pair_string, 5); + fc::optional private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second); + if (!private_key) + { + // the key isn't in WIF format; see if they are still passing the old native private key format. This is + // just here to ease the transition, can be removed soon + try + { + private_key = fc::variant(key_id_to_wif_pair.second, 2).as(1); + } + catch (const fc::exception&) + { + FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second)); + } + } + + if (_private_keys.find(key_id_to_wif_pair.first) == _private_keys.end()) + { + ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first)); + _private_keys[key_id_to_wif_pair.first] = *private_key; + } +} + void witness_plugin::plugin_initialize(const boost::program_options::variables_map& options) { try { ilog("witness plugin: plugin_initialize() begin"); @@ -94,24 +127,31 @@ void witness_plugin::plugin_initialize(const boost::program_options::variables_m const std::vector key_id_to_wif_pair_strings = options["private-key"].as>(); 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, 5); - 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) + add_private_key(key_id_to_wif_pair_string); + } + } + if (options.count("private-key-file")) + { + const std::vector key_id_to_wif_pair_files = + options["private-key-file"].as>(); + for (const boost::filesystem::path& key_id_to_wif_pair_file : key_id_to_wif_pair_files) + { + if (fc::exists(key_id_to_wif_pair_file)) { - // the key isn't in WIF format; see if they are still passing the old native private key format. This is - // just here to ease the transition, can be removed soon - try - { - private_key = fc::variant(key_id_to_wif_pair.second, 2).as(1); - } - catch (const fc::exception&) + std::string file_content; + fc::read_file_contents(key_id_to_wif_pair_file, file_content); + std::istringstream file_content_as_stream(file_content); + + std::string line; // key_id_to_wif_pair_string + while (std::getline(file_content_as_stream, line)) { - FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second)); + add_private_key(line); } } - _private_keys[key_id_to_wif_pair.first] = *private_key; + else + { + FC_THROW("Failed to load private key file from ${path}", ("path", key_id_to_wif_pair_file.string())); + } } } if(options.count("required-participation")) diff --git a/libraries/utilities/CMakeLists.txt b/libraries/utilities/CMakeLists.txt index 4311ef46cb..c4c01cd212 100644 --- a/libraries/utilities/CMakeLists.txt +++ b/libraries/utilities/CMakeLists.txt @@ -19,7 +19,9 @@ set(sources configure_file("${CMAKE_CURRENT_SOURCE_DIR}/git_revision.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp" @ONLY) list(APPEND sources "${CMAKE_CURRENT_BINARY_DIR}/git_revision.cpp") -find_package(CURL REQUIRED) + +find_curl() + include_directories(${CURL_INCLUDE_DIRS}) add_library( graphene_utilities ${sources} diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 1fc432ce1c..c3624419bf 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -70,7 +70,7 @@ struct brain_key_info * the meta data about the receipt that helps the sender identify which receipt is * for the receiver and which is for the change address. */ -struct blind_confirmation +struct blind_confirmation { struct output { @@ -101,7 +101,9 @@ struct blind_receipt { std::pair from_date()const { return std::make_pair(from_key,date); } std::pair to_date()const { return std::make_pair(to_key,date); } - std::tuple to_asset_used()const { return std::make_tuple(to_key,amount.asset_id,used); } + std::tuple to_asset_used()const + { return std::make_tuple(to_key,amount.asset_id,used); } + const commitment_type& commitment()const { return data.commitment; } fc::time_point date; @@ -124,10 +126,20 @@ struct by_commitment; typedef multi_index_container< blind_receipt, indexed_by< - ordered_unique< tag, const_mem_fun< blind_receipt, const commitment_type&, &blind_receipt::commitment > >, - ordered_unique< tag, const_mem_fun< blind_receipt, std::pair, &blind_receipt::to_date > >, - ordered_non_unique< tag, const_mem_fun< blind_receipt, std::tuple, &blind_receipt::to_asset_used > >, - ordered_unique< tag, const_mem_fun< blind_receipt, std::pair, &blind_receipt::from_date > > + ordered_unique< tag, + const_mem_fun< blind_receipt, const commitment_type&, &blind_receipt::commitment > >, + ordered_unique< tag, + const_mem_fun< blind_receipt, + std::pair, + &blind_receipt::to_date > >, + ordered_non_unique< tag, + const_mem_fun< blind_receipt, + std::tuple, + &blind_receipt::to_asset_used > >, + ordered_unique< tag, + const_mem_fun< blind_receipt, + std::pair, + &blind_receipt::from_date > > > > blind_receipt_index_type; @@ -255,6 +267,22 @@ struct vesting_balance_object_with_info : public vesting_balance_object fc::time_point_sec allowed_withdraw_time; }; +struct signed_message_meta { + string account; + public_key_type memo_key; + uint32_t block; + string time; +}; + +class signed_message { +public: + string message; + signed_message_meta meta; + fc::optional signature; + + fc::sha256 digest()const; +}; + namespace detail { class wallet_api_impl; } @@ -276,7 +304,8 @@ class utility { * @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); + static vector derive_owner_keys_from_brain_key( string brain_key, + int number_of_desired_keys = 1 ); /** Suggests a safe brain key to use for creating your account. * \c create_account_with_brain_key() requires you to specify a 'brain key', @@ -321,18 +350,26 @@ class wallet_api fc::ecc::private_key derive_private_key(const std::string& prefix_string, int sequence_number) const; + /** Returns info about head block, chain_id, maintenance, participation, current active witnesses and + * committee members. + * @returns runtime info about the blockchain + */ variant info(); /** Returns info such as client version, git version of graphene/fc, version of boost, openssl. * @returns compile time info and client and dependencies versions */ variant_object about() const; + /** Returns info about a specified block. + * @param num height of the block to retrieve + * @returns info about the block, or null if not found + */ optional get_block( uint32_t num ); /** Returns the number of accounts registered on the blockchain * @returns the number of registered accounts */ uint64_t get_account_count()const; /** Lists all accounts controlled by this wallet. - * This returns a list of the full account objects for all accounts whose private keys + * This returns a list of the full account objects for all accounts whose private keys * we possess. * @returns a list of account objects */ @@ -344,14 +381,14 @@ class wallet_api * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass * the last account name returned as the \c lowerbound for the next \c list_accounts() call. * - * @param lowerbound the name of the first account to return. If the named account does not exist, + * @param lowerbound the name of the first account to return. If the named account does not exist, * the list will start at the account that comes after \c lowerbound * @param limit the maximum number of accounts to return (max: 1000) * @returns a list of accounts mapping account names to account ids */ map list_accounts(const string& lowerbound, uint32_t limit); /** List the balances of an account. - * Each account can have multiple balances, one for each type of asset owned by that + * Each account can have multiple balances, one for each type of asset owned by that * account. The returned list will only contain assets for which the account has a * nonzero balance * @param id the name or id of the account whose balances you want @@ -359,7 +396,7 @@ class wallet_api */ vector list_account_balances(const string& id); /** Lists all assets registered on the blockchain. - * + * * To list all assets, pass the empty string \c "" for the lowerbound to start * at the beginning of the list, and iterate as necessary. * @@ -367,9 +404,9 @@ class wallet_api * @param limit the maximum number of assets to return (max: 100) * @returns the list of asset objects, ordered by symbol */ - vector list_assets(const string& lowerbound, uint32_t limit)const; + vector list_assets(const string& lowerbound, uint32_t limit)const; /** Returns assets count registered on the blockchain. - * + * * @returns assets count */ uint64_t get_asset_count()const; @@ -392,7 +429,8 @@ class wallet_api * @param start the sequence number where to start looping back throw the history * @returns a list of \c operation_history_objects */ - vector get_relative_account_history(string name, uint32_t stop, int limit, uint32_t start)const; + vector get_relative_account_history( string name, uint32_t stop, + int limit, uint32_t start )const; /** * @brief Fetch all objects relevant to the specified account @@ -403,8 +441,19 @@ class wallet_api * of \c name_or_id cannot be tied to an account, that input will be ignored. * */ - full_account get_full_account( const string& name_or_id); - vector get_market_history(string symbol, string symbol2, uint32_t bucket, fc::time_point_sec start, fc::time_point_sec end)const; + full_account get_full_account( const string& name_or_id ); + + /** + * @brief Get OHLCV data of a trading pair in a time range + * @param symbol name or ID of the base asset + * @param symbol2 name or ID of the quote asset + * @param bucket length of each time bucket in seconds. + * @param start the start of a time range, E.G. "2018-01-01T00:00:00" + * @param end the end of the time range + * @return A list of OHLCV data, in "least recent first" order. + */ + vector get_market_history( string symbol, string symbol2, uint32_t bucket, + fc::time_point_sec start, fc::time_point_sec end )const; /** * @brief Fetch all orders relevant to the specified account sorted descendingly by price @@ -422,7 +471,7 @@ class wallet_api * 1. if \c name_or_id cannot be tied to an account, empty result will be returned * 2. \c ostart_id and \c ostart_price can be \c null, if so the api will return the "first page" of orders; * if \c ostart_id is specified and valid, its price will be used to do page query preferentially, - * otherwise the \c ostart_price will be used + * otherwise the \c ostart_price will be used */ vector get_account_limit_orders( const string& name_or_id, const string &base, @@ -431,8 +480,29 @@ class wallet_api optional ostart_id = optional(), optional ostart_price = optional()); + /** + * @brief Get limit orders in a given market + * @param a symbol or ID of asset being sold + * @param b symbol or 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(string a, string b, uint32_t limit)const; + + /** + * @brief Get call orders (aka margin positions) for a given asset + * @param a symbol name or ID of the debt asset + * @param limit Maximum number of orders to retrieve + * @return The call orders, ordered from earliest to be called to latest + */ vector get_call_orders(string a, uint32_t limit)const; + + /** + * @brief Get forced settlement orders in a given asset + * @param a Symbol or 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(string a, uint32_t limit)const; /** Returns the collateral_bid object for the given MPA @@ -443,7 +513,7 @@ class wallet_api * @returns a list of \c collateral_bid_objects */ vector get_collateral_bids(string asset, uint32_t limit = 100, uint32_t start = 0)const; - + /** Returns the block chain's slowly-changing settings. * This object contains all of the properties of the blockchain that are fixed * or that change only once per maintenance interval (daily) such as the @@ -457,12 +527,15 @@ class wallet_api * Get operations relevant to the specified account filtering by operation type, with transaction id * * @param name the name or id of the account, whose history shoulde be queried - * @param operation_types The IDs of the operation we want to get operations in the account( 0 = transfer , 1 = limit order create, ...) + * @param operation_types The IDs of the operation we want to get operations in the account + * ( 0 = transfer , 1 = limit order create, ...) * @param start the sequence number where to start looping back throw the history * @param limit the max number of entries to return (from start number) * @returns account_history_operation_detail */ - account_history_operation_detail get_account_history_by_operations(string name, vector operation_types, uint32_t start, int limit); + account_history_operation_detail get_account_history_by_operations( string name, + vector operation_types, + uint32_t start, int limit); /** Returns the block chain's rapidly-changing properties. * The returned object contains information that changes every block interval @@ -474,7 +547,7 @@ class wallet_api /** Returns information about the given account. * - * @param account_name_or_id the name or id of the account to provide information about + * @param account_name_or_id the name or ID of the account to provide information about * @returns the public account data stored in the blockchain */ account_object get_account(string account_name_or_id) const; @@ -483,7 +556,7 @@ class wallet_api * @param asset_name_or_id the symbol or id of the asset in question * @returns the information about the asset stored in the block chain */ - asset_object get_asset(string asset_name_or_id) const; + extended_asset_object get_asset(string asset_name_or_id) const; /** Returns the BitAsset-specific data for a given asset. * Market-issued assets's behavior are determined both by their "BitAsset Data" and @@ -501,14 +574,14 @@ class wallet_api fc::optional get_htlc(string htlc_id) const; /** Lookup the id of a named account. - * @param account_name_or_id the name of the account to look up + * @param account_name_or_id the name or ID of the account to look up * @returns the id of the named account */ account_id_type get_account_id(string account_name_or_id) const; /** * Lookup the id of a named asset. - * @param asset_name_or_id the symbol of an asset to look up + * @param asset_name_or_id the symbol or ID of an asset to look up * @returns the id of the given asset */ asset_id_type get_asset_id(string asset_name_or_id) const; @@ -517,8 +590,8 @@ class wallet_api * Returns the blockchain object corresponding to the given id. * * This generic function can be used to retrieve any object from the blockchain - * that is assigned an ID. Certain types of objects have specialized convenience - * functions to return their objects -- e.g., assets have \c get_asset(), accounts + * that is assigned an ID. Certain types of objects have specialized convenience + * functions to return their objects -- e.g., assets have \c get_asset(), accounts * have \c get_account(), but this function will work for any object. * * @param id the id of the object to return @@ -526,7 +599,7 @@ class wallet_api */ variant get_object(object_id_type id) const; - /** Returns the current wallet filename. + /** Returns the current wallet filename. * * This is the filename that will be used when automatically saving the wallet. * @@ -538,33 +611,61 @@ class wallet_api /** * Get the WIF private key corresponding to a public key. The * private key must already be in the wallet. + * @param pubkey a public key in Base58 format + * @return the WIF private key */ string get_private_key( public_key_type pubkey )const; /** * @ingroup Transaction Builder API + * + * Create a new transaction builder. + * @return handle of the new transaction builder */ transaction_handle_type begin_builder_transaction(); /** * @ingroup Transaction Builder API + * + * Append a new operation to a transaction builder. + * @param transaction_handle handle of the transaction builder + * @param op the operation in JSON format */ void add_operation_to_builder_transaction(transaction_handle_type transaction_handle, const operation& op); /** * @ingroup Transaction Builder API + * + * Replace an operation in a transaction builder with a new operation. + * @param handle handle of the transaction builder + * @param operation_index the index of the old operation in the builder to be replaced + * @param new_op the new operation in JSON format */ void replace_operation_in_builder_transaction(transaction_handle_type handle, unsigned operation_index, const operation& new_op); /** * @ingroup Transaction Builder API + * + * Calculate and update fees for the operations in a transaction builder. + * @param handle handle of the transaction builder + * @param fee_asset name or ID of an asset that to be used to pay fees + * @return total fees */ asset set_fees_on_builder_transaction(transaction_handle_type handle, string fee_asset = GRAPHENE_SYMBOL); /** * @ingroup Transaction Builder API + * + * Show content of a transaction builder. + * @param handle handle of the transaction builder + * @return a transaction */ transaction preview_builder_transaction(transaction_handle_type handle); /** * @ingroup Transaction Builder API + * + * Sign the transaction in a transaction builder and optionally broadcast to the network. + * @param transaction_handle handle of the transaction builder + * @param broadcast whether to broadcast the signed transaction to the network + * @return a signed transaction */ signed_transaction sign_builder_transaction(transaction_handle_type transaction_handle, bool broadcast = true); @@ -576,6 +677,19 @@ class wallet_api /** * @ingroup Transaction Builder API + * + * Create a proposal containing the operations in a transaction builder (create a new proposal_create + * operation, then replace the transaction builder with the new operation), then sign the transaction + * and optionally broadcast to the network. + * + * Note: this command is buggy because unable to specify proposer. It will be deprecated in a future release. + * Please use \c propose_builder_transaction2() instead. + * + * @param handle handle of the transaction builder + * @param expiration when the proposal will expire + * @param review_period_seconds review period of the proposal in seconds + * @param broadcast whether to broadcast the signed transaction to the network + * @return a signed transaction */ signed_transaction propose_builder_transaction( transaction_handle_type handle, @@ -584,6 +698,20 @@ class wallet_api bool broadcast = true ); + /** + * @ingroup Transaction Builder API + * + * Create a proposal containing the operations in a transaction builder (create a new proposal_create + * operation, then replace the transaction builder with the new operation), then sign the transaction + * and optionally broadcast to the network. + * + * @param handle handle of the transaction builder + * @param account_name_or_id name or ID of the account who would pay fees for creating the proposal + * @param expiration when the proposal will expire + * @param review_period_seconds review period of the proposal in seconds + * @param broadcast whether to broadcast the signed transaction to the network + * @return a signed transaction + */ signed_transaction propose_builder_transaction2( transaction_handle_type handle, string account_name_or_id, @@ -594,6 +722,9 @@ class wallet_api /** * @ingroup Transaction Builder API + * + * Destroy a transaction builder. + * @param handle handle of the transaction builder */ void remove_builder_transaction(transaction_handle_type handle); @@ -604,33 +735,42 @@ class wallet_api * @ingroup Wallet Management */ bool is_new()const; - - /** Checks whether the wallet is locked (is unable to use its private keys). + + /** Checks whether the wallet is locked (is unable to use its private keys). * * This state can be changed by calling \c lock() or \c unlock(). * @return true if the wallet is locked * @ingroup Wallet Management */ bool is_locked()const; - + /** Locks the wallet immediately. * @ingroup Wallet Management */ void lock(); - - /** Unlocks the wallet. + + /** Unlocks the wallet. * * The wallet remain unlocked until the \c lock is called * or the program exits. + * + * When used in command line, if typed "unlock" without a password followed, the user will be prompted + * to input a password without echo. + * * @param password the password previously set with \c set_password() * @ingroup Wallet Management */ void unlock(string password); - + /** Sets a new password on the wallet. * * The wallet must be either 'new' or 'unlocked' to * execute this command. + * + * When used in command line, if typed "set_password" without a password followed, the user will be prompted + * to input a password without echo. + * + * @param password a new password * @ingroup Wallet Management */ void set_password(string password); @@ -639,14 +779,14 @@ class wallet_api * * The keys are printed in WIF format. You can import these keys into another wallet * using \c import_key() - * @returns a map containing the private keys, indexed by their public key + * @returns a map containing the private keys, indexed by their public key */ map dump_private_keys(); /** Returns a list of all commands supported by the wallet API. * * This lists each command, along with its arguments and return types. - * For more detailed help on a single command, use \c get_help() + * For more detailed help on a single command, use \c gethelp() * * @returns a multi-line string suitable for displaying on a terminal */ @@ -673,14 +813,14 @@ class wallet_api */ bool load_wallet_file(string wallet_filename = ""); - /** Quitting from BitShares wallet. - * - * The current wallet will be closed. + /** Quit from the wallet. + * + * The current wallet will be closed and saved. */ void quit(); /** Saves the current wallet to the given filename. - * + * * @warning This does not change the wallet filename that will be used for future * writes, so think of this function as 'Save a Copy As...' instead of * 'Save As...'. Use \c set_wallet_filename() to make the filename @@ -735,8 +875,8 @@ class wallet_api /** Converts a signed_transaction in JSON form to its binary representation. * * @param tx the transaction to serialize - * @returns the binary form of the transaction. It will not be hex encoded, - * this returns a raw string that may have null characters embedded + * @returns the binary form of the transaction. It will not be hex encoded, + * this returns a raw string that may have null characters embedded * in it */ string serialize_transaction(signed_transaction tx) const; @@ -744,7 +884,7 @@ class wallet_api /** Imports the private key for an existing account. * * The private key must match either an owner key or an active key for the - * named account. + * named account. * * @see dump_private_keys() * @@ -754,15 +894,38 @@ class wallet_api */ bool import_key(string account_name_or_id, string wif_key); + /** Imports accounts from a BitShares 0.x wallet file. + * Current wallet file must be unlocked to perform the import. + * + * @param filename the BitShares 0.x wallet file to import + * @param password the password to encrypt the BitShares 0.x wallet file + * @returns a map containing the accounts found and whether imported + */ map import_accounts( string filename, string password ); + /** Imports from a BitShares 0.x wallet file, find keys that were bound to a given account name on the + * BitShares 0.x chain, rebind them to an account name on the 2.0 chain. + * Current wallet file must be unlocked to perform the import. + * + * @param filename the BitShares 0.x wallet file to import + * @param password the password to encrypt the BitShares 0.x wallet file + * @param src_account_name name of the account on BitShares 0.x chain + * @param dest_account_name name of the account on BitShares 2.0 chain, + * can be same or different to \c src_account_name + * @returns whether the import has succeeded + */ bool import_account_keys( string filename, string password, string src_account_name, string dest_account_name ); /** * This call will construct transaction(s) that will claim all balances controled * by wif_keys and deposit them into the given account. + * + * @param account_name_or_id name or ID of an account that to claim balances to + * @param wif_keys private WIF keys of balance objects to claim balances from + * @param broadcast true to broadcast the transaction on the network */ - vector< signed_transaction > import_balance( string account_name_or_id, const vector& wif_keys, bool broadcast ); + vector< signed_transaction > import_balance( string account_name_or_id, const vector& wif_keys, + bool broadcast ); /** Transforms a brain key to reduce the chance of errors when re-entering the key from memory. * @@ -811,8 +974,7 @@ class wallet_api * Upgrades an account to prime status. * This makes the account holder a 'lifetime member'. * - * @todo there is no option for annual membership - * @param name the name or id of the account to upgrade + * @param name the name or id of the account to upgrade * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction upgrading the account */ @@ -847,7 +1009,7 @@ class wallet_api * @param to the name or id of the account receiving the funds * @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5) * @param asset_symbol the symbol or id of the asset to send - * @param memo a memo to attach to the transaction. The memo will be encrypted in the + * @param memo a memo to attach to the transaction. The memo will be encrypted in the * transaction and readable for the receiver. There is no length limit * other than the limit imposed by maximum transaction size, but transaction * increase with transaction size @@ -863,7 +1025,16 @@ class wallet_api /** * This method works just like transfer, except it always broadcasts and - * returns the transaction ID along with the signed transaction. + * returns the transaction ID (hash) along with the signed transaction. + * @param from the name or id of the account sending the funds + * @param to the name or id of the account receiving the funds + * @param amount the amount to send (in nominal units -- to send half of a BTS, specify 0.5) + * @param asset_symbol the symbol or id of the asset to send + * @param memo a memo to attach to the transaction. The memo will be encrypted in the + * transaction and readable for the receiver. There is no length limit + * other than the limit imposed by maximum transaction size, but transaction + * increase with transaction size + * @returns the transaction ID (hash) along with the signed transaction transferring funds */ pair transfer2(string from, string to, @@ -877,95 +1048,174 @@ class wallet_api /** * This method is used to convert a JSON transaction to its transactin ID. + * @param trx a JSON transaction + * @return the ID (hash) of the transaction */ 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. + * @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 + * @return the signed memo data */ memo_data sign_memo(string from, string to, string memo); /** Read a memo. * * @param memo JSON-enconded memo. - * @returns string with decrypted message.. + * @returns string with decrypted message. */ string read_memo(const memo_data& memo); + /** Sign a message using an account's memo key. The signature is generated as in + * in https://github.com/xeroc/python-graphenelib/blob/d9634d74273ebacc92555499eca7c444217ecba0/graphenecommon/message.py#L64 . + * + * @param signer the name or id of signing account + * @param message text to sign + * @return the signed message in an abstract format + */ + signed_message sign_message(string signer, string message); + + /** Verify a message signed with sign_message using the given account's memo key. + * + * @param message the message text + * @param account the account name of the message + * @param block the block number of the message + * @param time the timestamp of the message + * @param sig the message signature + * @return true if signature matches + */ + bool verify_message( string message, string account, int block, const string& time, compact_signature sig ); + + /** Verify a message signed with sign_message + * + * @param message the signed_message structure containing message, meta data and signature + * @return true if signature matches + */ + bool verify_signed_message( signed_message message ); + + /** Verify a message signed with sign_message, in its encapsulated form. + * + * @param message the complete encapsulated message string including separators and line feeds + * @return true if signature matches + */ + bool verify_encapsulated_message( string message ); + /** These methods are used for stealth transfers */ ///@{ /** - * This method can be used to set the label for a public key + * This method can be used to set a label for a public key * - * @note No two keys can have the same label. - * - * @return true if the label was set, otherwise false + * @note No two keys can have the same label. + * @param key a public key + * @param label a user-defined string as label + * @return true if the label was set, otherwise false + */ + bool set_key_label( public_key_type key, string label ); + + /** + * Get label of a public key. + * @param key a public key + * @return the label if already set by \c set_key_label(), or an empty string if not set */ - bool set_key_label( public_key_type, string label ); - string get_key_label( public_key_type )const; + string get_key_label( public_key_type key )const; /** - * Generates a new blind account for the given brain key and assigns it the given label. + * Generates a new blind account for the given brain key and assigns it the given label. + * @param label a label + * @param brain_key the brain key to be used to generate a new blind account + * @return the public key of the new account */ public_key_type create_blind_account( string label, string brain_key ); /** - * @return the total balance of all blinded commitments that can be claimed by the + * Return the total balances of all blinded commitments that can be claimed by the + * given account key or label. + * @param key_or_label a public key in Base58 format or a label + * @return the total balances of all blinded commitments that can be claimed by the * given account key or label */ vector get_blind_balances( string key_or_label ); - /** @return all blind accounts */ + /** + * Get all blind accounts. + * @return all blind accounts + */ map get_blind_accounts()const; - /** @return all blind accounts for which this wallet has the private key */ + /** + * Get all blind accounts for which this wallet has the private key. + * @return all blind accounts for which this wallet has the private key + */ map get_my_blind_accounts()const; - /** @return the public key associated with the given label */ + /** + * Get the public key associated with a given label. + * @param label a label + * @return the public key associated with the given label + */ public_key_type get_public_key( string label )const; ///@} /** - * @return all blind receipts to/form a particular account + * Get all blind receipts to/form a particular account + * @param key_or_account a public key in Base58 format or an account + * @return all blind receipts to/form the account */ vector blind_history( string key_or_account ); /** - * Given a confirmation receipt, this method will parse it for a blinded balance and confirm - * that it exists in the blockchain. If it exists then it will report the amount received and - * who sent it. + * Given a confirmation receipt, this method will parse it for a blinded balance and confirm + * that it exists in the blockchain. If it exists then it will report the amount received and + * who sent it. * - * @param opt_from if not empty and the sender is a unknown public key, - * then the unknown public key will be given the label \c opt_from - * @param confirmation_receipt a base58 encoded stealth confirmation - * @param opt_memo a self-defined label for this transfer to be saved in local wallet file + * @param confirmation_receipt a base58 encoded stealth confirmation + * @param opt_from if not empty and the sender is a unknown public key, + * then the unknown public key will be given the label \c opt_from + * @param opt_memo a self-defined label for this transfer to be saved in local wallet file + * @return a blind receipt */ blind_receipt receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ); /** - * Transfers a public balance from \c from_account_id_or_name to one or more blinded balances using a - * stealth transfer. + * Transfers a public balance from \c from_account_id_or_name to one or more blinded balances using a + * stealth transfer. + * @param from_account_id_or_name ID or name of an account to transfer from + * @param asset_symbol symbol or ID of the asset to be transferred + * @param to_amounts map from key or label to amount + * @param broadcast true to broadcast the transaction on the network + * @return a blind confirmation */ - blind_confirmation transfer_to_blind( string from_account_id_or_name, + blind_confirmation transfer_to_blind( string from_account_id_or_name, string asset_symbol, - /** map from key or label to amount */ - vector> to_amounts, + vector> to_amounts, bool broadcast = false ); /** * Transfers funds from a set of blinded balances to a public account balance. + * @param from_blind_account_key_or_label a public key in Base58 format or a label to transfer from + * @param to_account_id_or_name ID or name of an account to transfer to + * @param amount the amount to be transferred + * @param asset_symbol symbol or ID of the asset to be transferred + * @param broadcast true to broadcast the transaction on the network + * @return a blind confirmation */ - blind_confirmation transfer_from_blind( + blind_confirmation transfer_from_blind( string from_blind_account_key_or_label, - string to_account_id_or_name, + string to_account_id_or_name, string amount, string asset_symbol, bool broadcast = false ); /** - * Used to transfer from one set of blinded balances to another + * Transfer from one set of blinded balances to another. + * @param from_key_or_label a public key in Base58 format or a label to transfer from + * @param to_key_or_label a public key in Base58 format or a label to transfer to + * @param amount the amount to be transferred + * @param symbol symbol or ID of the asset to be transferred + * @param broadcast true to broadcast the transaction on the network + * @return a blind confirmation */ blind_confirmation blind_transfer( string from_key_or_label, string to_key_or_label, @@ -975,14 +1225,14 @@ class wallet_api /** Place a limit order attempting to sell one asset for another. * - * Buying and selling are the same operation on Graphene; if you want to buy BTS + * Buying and selling are the same operation on Graphene; if you want to buy BTS * with USD, you should sell USD for BTS. * * The blockchain will attempt to sell the \c symbol_to_sell for as - * much \c symbol_to_receive as possible, as long as the price is at - * least \c min_to_receive / \c amount_to_sell. + * much \c symbol_to_receive as possible, as long as the price is at + * least \c min_to_receive / \c amount_to_sell. * - * In addition to the transaction fees, market fees will apply as specified + * In addition to the transaction fees, market fees will apply as specified * by the issuer of both the selling asset and the receiving asset as * a percentage of the amount exchanged. * @@ -993,18 +1243,18 @@ class wallet_api * Market orders are matched in the order they are included * in the block chain. * - * @todo Allow order expiration to be set here. Document default/max expiration time + * @todo Document default/max expiration time * - * @param seller_account the account providing the asset being sold, and which will + * @param seller_account the account providing the asset being sold, and which will * receive the proceeds of the sale. * @param amount_to_sell the amount of the asset being sold to sell (in nominal units) * @param symbol_to_sell the name or id of the asset to sell * @param min_to_receive the minimum amount you are willing to receive in return for * selling the entire amount_to_sell * @param symbol_to_receive the name or id of the asset you wish to receive - * @param timeout_sec if the order does not fill immediately, this is the length of - * time the order will remain on the order books before it is - * cancelled and the un-spent funds are returned to the seller's + * @param timeout_sec if the order does not fill immediately, this is the length of + * time the order will remain on the order books before it is + * cancelled and the un-spent funds are returned to the seller's * account * @param fill_or_kill if true, the order will only be included in the blockchain * if it is filled immediately; if false, an open order will be @@ -1074,14 +1324,14 @@ class wallet_api * Right now this function is difficult to use because you must provide raw JSON data * structures for the options objects, and those include prices and asset ids. * - * @param issuer the name or id of the account who will pay the fee and become the + * @param issuer the name or id of the account who will pay the fee and become the * issuer of the new asset. This can be updated later * @param symbol the ticker symbol of the new asset * @param precision the number of digits of precision to the right of the decimal point, * must be less than or equal to 12 * @param common asset options required for all new assets. - * Note that core_exchange_rate technically needs to store the asset ID of - * this new asset. Since this ID is not known at the time this operation is + * Note that core_exchange_rate technically needs to store the asset ID of + * this new asset. Since this ID is not known at the time this operation is * created, create this price as though the new asset has instance ID 1, and * the chain will overwrite it with the new asset's ID. * @param bitasset_opts options specific to BitAssets. This may be null unless the @@ -1111,8 +1361,8 @@ class wallet_api bool broadcast = false); /** Update the core options on an asset. - * There are a number of options which all assets in the network use. These options are - * enumerated in the asset_object::asset_options struct. This command is used to update + * There are a number of options which all assets in the network use. These options are + * enumerated in the asset_object::asset_options struct. This command is used to update * these options for an existing asset. * * @note This operation cannot be used to update BitAsset-specific options. For these options, @@ -1176,7 +1426,7 @@ class wallet_api signed_transaction update_asset_feed_producers(string symbol, flat_set new_feed_producers, bool broadcast = false); - + /** Publishes a price feed for the named asset. * * Price feed providers use this command to publish their price feeds for market-issued assets. A price feed is @@ -1204,7 +1454,7 @@ class wallet_api /** Pay into the fee pool for the given asset. * - * User-issued assets can optionally have a pool of the core asset which is + * User-issued assets can optionally have a pool of the core asset which is * automatically used to pay transaction fees for any transaction using that * asset (using the asset's core exchange rate). * @@ -1223,7 +1473,7 @@ class wallet_api /** Claim funds from the fee pool for the given asset. * - * User-issued assets can optionally have a pool of the core asset which is + * User-issued assets can optionally have a pool of the core asset which is * automatically used to pay transaction fees for any transaction using that * asset (using the asset's core exchange rate). * @@ -1238,9 +1488,9 @@ class wallet_api string amount, bool broadcast = false); - /** Burns the given user-issued asset. + /** Burns an amount of given asset. * - * This command burns the user-issued asset to reduce the amount in circulation. + * This command burns an amount of given asset to reduce the amount in circulation. * @note you cannot burn market-issued assets. * @param from the account containing the asset you wish to burn * @param amount the amount to burn, in nominal units @@ -1257,15 +1507,15 @@ class wallet_api * * In order to use this operation, asset_to_settle must have the global_settle flag set * - * When this operation is executed all balances are converted into the backing asset at the - * settle_price and all open margin positions are called at the settle price. If this asset is - * used as backing for other bitassets, those bitassets will be force settled at their current - * feed price. + * When this operation is executed all open margin positions are called at the settle price. + * A pool will be formed containing the collateral got from the margin positions. + * Users owning an amount of the asset may use \c settle_asset() to claim collateral instantly + * at the settle price from the pool. + * If this asset is used as backing for other bitassets, those bitassets will not be affected. * - * @note this operation is used only by the asset issuer, \c settle_asset() may be used by - * any user owning the asset + * @note this operation is used only by the asset issuer. * - * @param symbol the name or id of the asset to force settlement on + * @param symbol the name or id of the asset to globally settle * @param settle_price the price at which to settle * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction settling the named asset @@ -1276,16 +1526,19 @@ class wallet_api /** Schedules a market-issued asset for automatic settlement. * - * Holders of market-issued assests may request a forced settlement for some amount of their asset. This means that - * the specified sum will be locked by the chain and held for the settlement period, after which time the chain will - * choose a margin posision holder and buy the settled asset using the margin's collateral. The price of this sale - * will be based on the feed price for the market-issued asset being settled. The exact settlement price will be the + * Holders of market-issued assests may request a forced settlement for some amount of their asset. + * This means that the specified sum will be locked by the chain and held for the settlement period, + * after which time the chain will + * choose a margin posision holder and buy the settled asset using the margin's collateral. + * The price of this sale + * will be based on the feed price for the market-issued asset being settled. + * The exact settlement price will be the * feed price at the time of settlement with an offset in favor of the margin position, where the offset is a * blockchain parameter set in the global_property_object. * * @param account_to_settle the name or id of the account owning the asset * @param amount_to_settle the amount of the named asset to schedule for settlement - * @param symbol the name or id of the asset to settlement on + * @param symbol the name or id of the asset to settle * @param broadcast true to broadcast the transaction on the network * @returns the signed transaction settling the named asset */ @@ -1316,16 +1569,16 @@ class wallet_api /** Whitelist and blacklist accounts, primarily for transacting in whitelisted assets. * * Accounts can freely specify opinions about other accounts, in the form of either whitelisting or blacklisting - * them. This information is used in chain validation only to determine whether an account is authorized to transact - * in an asset type which enforces a whitelist, but third parties can use this information for other uses as well, - * as long as it does not conflict with the use of whitelisted assets. + * them. This information is used in chain validation only to determine whether an account is authorized to + * transact in an asset type which enforces a whitelist, but third parties can use this information for other + * uses as well, as long as it does not conflict with the use of whitelisted assets. * * An asset which enforces a whitelist specifies a list of accounts to maintain its whitelist, and a list of - * accounts to maintain its blacklist. In order for a given account A to hold and transact in a whitelisted asset S, - * A must be whitelisted by at least one of S's whitelist_authorities and blacklisted by none of S's - * blacklist_authorities. If A receives a balance of S, and is later removed from the whitelist(s) which allowed it - * to hold S, or added to any blacklist S specifies as authoritative, A's balance of S will be frozen until A's - * authorization is reinstated. + * accounts to maintain its blacklist. In order for a given account A to hold and transact in a whitelisted + * asset S, A must be whitelisted by at least one of S's whitelist_authorities and blacklisted by none of S's + * blacklist_authorities. If A receives a balance of S, and is later removed from the whitelist(s) which allowed + * it to hold S, or added to any blacklist S specifies as authoritative, A's balance of S will be frozen until + * A's authorization is reinstated. * * @param authorizing_account the account who is doing the whitelisting * @param account_to_list the account being whitelisted @@ -1349,7 +1602,7 @@ class wallet_api * @returns the signed transaction registering a committee_member */ signed_transaction create_committee_member(string owner_account, - string url, + string url, bool broadcast = false); /** Lists all witnesses registered in the blockchain. @@ -1360,7 +1613,7 @@ class wallet_api * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass * the last witness name returned as the \c lowerbound for the next \c list_witnesss() call. * - * @param lowerbound the name of the first witness to return. If the named witness does not exist, + * @param lowerbound the name of the first witness to return. If the named witness does not exist, * the list will start at the witness that comes after \c lowerbound * @param limit the maximum number of witnesss to return (max: 1000) * @returns a list of witnesss mapping witness names to witness ids @@ -1375,8 +1628,8 @@ class wallet_api * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass * the last committee_member name returned as the \c lowerbound for the next \c list_committee_members() call. * - * @param lowerbound the name of the first committee_member to return. If the named committee_member does not exist, - * the list will start at the committee_member that comes after \c lowerbound + * @param lowerbound the name of the first committee_member to return. If the named committee_member does not + * exist, the list will start at the committee_member that comes after \c lowerbound * @param limit the maximum number of committee_members to return (max: 1000) * @returns a list of committee_members mapping committee_member names to committee_member ids */ @@ -1411,10 +1664,12 @@ class wallet_api /** * Update a witness object owned by the given account. * - * @param witness_name The name of the witness's owner account. Also accepts the ID of the owner account or the ID of the witness. + * @param witness_name The name of the witness's owner account. + * Also accepts the ID of the owner account or the ID of the witness. * @param url Same as for create_witness. The empty string makes it remain the same. * @param block_signing_key The new block signing public key. The empty string makes it remain the same. * @param broadcast true if you wish to broadcast the transaction. + * @return the signed transaction */ signed_transaction update_witness(string witness_name, string url, @@ -1433,6 +1688,7 @@ class wallet_api * @param url Any text * @param worker_settings {"type" : "burn"|"refund"|"vesting", "pay_vesting_period_days" : x} * @param broadcast true if you wish to broadcast the transaction. + * @return the signed transaction */ signed_transaction create_worker( string owner_account, @@ -1446,11 +1702,12 @@ class wallet_api ); /** - * Update your votes for a worker + * Update your votes for workers * * @param account The account which will pay the fee and update votes. * @param delta {"vote_for" : [...], "vote_against" : [...], "vote_abstain" : [...]} * @param broadcast true if you wish to broadcast the transaction. + * @return the signed transaction */ signed_transaction update_worker_votes( string account, @@ -1460,7 +1717,7 @@ class wallet_api /** * Create a hashed time lock contract - * + * * @param source The account that will reserve the funds (and pay the fee) * @param destination The account that will receive the funds if the preimage is presented * @param amount the amount of the asset that is to be traded @@ -1470,36 +1727,40 @@ class wallet_api * @param preimage_size the size of the preimage in bytes * @param claim_period_seconds how long after creation until the lock expires * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction */ signed_transaction htlc_create( string source, string destination, string amount, string asset_symbol, - string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, + string hash_algorithm, const std::string& preimage_hash, uint32_t preimage_size, const uint32_t claim_period_seconds, bool broadcast = false ); /**** * Update a hashed time lock contract - * + * * @param htlc_id The object identifier of the HTLC on the blockchain * @param issuer Who is performing this operation (and paying the fee) * @param preimage the preimage that should evaluate to the preimage_hash + * @return the signed transaction */ - signed_transaction htlc_redeem( string htlc_id, string issuer, const std::string& preimage, + signed_transaction htlc_redeem( string htlc_id, string issuer, const std::string& preimage, bool broadcast = false ); /***** * Increase the timelock on an existing HTLC - * + * * @param htlc_id The object identifier of the HTLC on the blockchain * @param issuer Who is performing this operation (and paying the fee) * @param seconds_to_add how many seconds to add to the existing timelock * @param broadcast true to broadcast to the network + * @return the signed transaction */ signed_transaction htlc_extend(string htlc_id, string issuer, const uint32_t seconds_to_add, bool broadcast = false); /** - * Get information about a vesting balance object. + * Get information about a vesting balance object or vesting balance objects owned by an account. * * @param account_name An account name, account ID, or vesting balance object ID. + * @return a list of vesting balance objects with additional info */ vector< vesting_balance_object_with_info > get_vesting_balances( string account_name ); @@ -1510,6 +1771,7 @@ class wallet_api * @param amount The amount to withdraw. * @param asset_symbol The symbol of the asset to withdraw. * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction */ signed_transaction withdraw_vesting( string witness_name, @@ -1519,8 +1781,8 @@ class wallet_api /** Vote for a given committee_member. * - * An account can publish a list of all committee_memberes they approve of. This - * command allows you to add or remove committee_memberes from this list. + * An account can publish a list of all committee_members they approve of. This + * command allows you to add or remove committee_members from this list. * Each account's vote is weighted according to the number of shares of the * core asset owned by that account at the time the votes are tallied. * @@ -1529,7 +1791,7 @@ class wallet_api * * @param voting_account the name or id of the account who is voting with their shares * @param committee_member the name or id of the committee_member' owner account - * @param approve true if you wish to vote in favor of that committee_member, false to + * @param approve true if you wish to vote in favor of that committee_member, false to * remove your vote in favor of that committee_member * @param broadcast true if you wish to broadcast the transaction * @return the signed transaction changing your vote for the given committee_member @@ -1541,7 +1803,7 @@ class wallet_api /** Vote for a given witness. * - * An account can publish a list of all witnesses they approve of. This + * An account can publish a list of all witnesses they approve of. This * command allows you to add or remove witnesses from this list. * Each account's vote is weighted according to the number of shares of the * core asset owned by that account at the time the votes are tallied. @@ -1551,7 +1813,7 @@ class wallet_api * * @param voting_account the name or id of the account who is voting with their shares * @param witness the name or id of the witness' owner account - * @param approve true if you wish to vote in favor of that witness, false to + * @param approve true if you wish to vote in favor of that witness, false to * remove your vote in favor of that witness * @param broadcast true if you wish to broadcast the transaction * @return the signed transaction changing your vote for the given witness @@ -1582,16 +1844,16 @@ class wallet_api signed_transaction set_voting_proxy(string account_to_modify, optional voting_account, bool broadcast = false); - + /** Set your vote for the number of witnesses and committee_members in the system. * - * Each account can voice their opinion on how many committee_members and how many + * Each account can voice their opinion on how many committee_members and how many * witnesses there should be in the active committee_member/active witness list. These * are independent of each other. You must vote your approval of at least as many * committee_members or witnesses as you claim there should be (you can't say that there should - * be 20 committee_members but only vote for 10). + * be 20 committee_members but only vote for 10). * - * There are maximum values for each set in the blockchain parameters (currently + * There are maximum values for each set in the blockchain parameters (currently * defaulting to 1001). * * This setting can be changed at any time. If your account has a voting proxy @@ -1638,17 +1900,17 @@ class wallet_api /** Returns an uninitialized object representing a given blockchain operation. * - * This returns a default-initialized object of the given type; it can be used + * This returns a default-initialized object of the given type; it can be used * during early development of the wallet when we don't yet have custom commands for - * creating all of the operations the blockchain supports. + * creating all of the operations the blockchain supports. * * Any operation the blockchain supports can be created using the transaction builder's - * \c add_operation_to_builder_transaction() , but to do that from the CLI you need to + * \c add_operation_to_builder_transaction() , but to do that from the CLI you need to * know what the JSON form of the operation looks like. This will give you a template * you can fill in. It's better than nothing. - * - * @param operation_type the type of operation to return, must be one of the - * operations defined in `graphene/chain/operations.hpp` + * + * @param operation_type the type of operation to return, must be one of the + * operations defined in `graphene/protocol/operations.hpp` * (e.g., "global_parameters_update_operation") * @return a default-constructed operation of the given type */ @@ -1672,7 +1934,7 @@ class wallet_api bool broadcast = false); /** Propose a fee change. - * + * * @param proposing_account The account paying the fee to propose the tx * @param expiration_time Timestamp specifying when the proposal will either take effect or expire. * @param changed_values Map of operation type to new fee. Operations may be specified by name or ID. @@ -1700,7 +1962,14 @@ class wallet_api const approval_delta& delta, bool broadcast /* = false */ ); - + + /** + * Returns the order book for the market base:quote. + * @param base symbol name or ID of the base asset + * @param quote symbol name or ID of the quote asset + * @param limit depth of the order book to retrieve, for bids and asks each, capped at 50 + * @return Order book of the market + */ order_book get_order_book( const string& base, const string& quote, unsigned limit = 50); /** Signs a transaction. @@ -1753,7 +2022,8 @@ extern template class fc::api; FC_REFLECT( graphene::wallet::key_label, (label)(key) ) FC_REFLECT( graphene::wallet::blind_balance, (amount)(from)(to)(one_time_key)(blinding_factor)(commitment)(used) ) -FC_REFLECT( graphene::wallet::blind_confirmation::output, (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) ) +FC_REFLECT( graphene::wallet::blind_confirmation::output, + (label)(pub_key)(decrypted_memo)(confirmation)(auth)(confirmation_receipt) ) FC_REFLECT( graphene::wallet::blind_confirmation, (trx)(outputs) ) FC_REFLECT( graphene::wallet::plain_keys, (keys)(checksum) ) @@ -1805,7 +2075,7 @@ FC_REFLECT_DERIVED( graphene::wallet::signed_block_with_info, (graphene::chain:: FC_REFLECT_DERIVED( graphene::wallet::vesting_balance_object_with_info, (graphene::chain::vesting_balance_object), (allowed_withdraw)(allowed_withdraw_time) ) -FC_REFLECT( graphene::wallet::operation_detail, +FC_REFLECT( graphene::wallet::operation_detail, (memo)(description)(op) ) FC_REFLECT(graphene::wallet::operation_detail_ex, @@ -1814,6 +2084,9 @@ FC_REFLECT(graphene::wallet::operation_detail_ex, FC_REFLECT( graphene::wallet::account_history_operation_detail, (total_count)(result_count)(details)) +FC_REFLECT( graphene::wallet::signed_message_meta, (account)(memo_key)(block)(time) ) +FC_REFLECT( graphene::wallet::signed_message, (message)(meta)(signature) ) + FC_API( graphene::wallet::wallet_api, (help) (gethelp) @@ -1931,6 +2204,10 @@ FC_API( graphene::wallet::wallet_api, (network_get_connected_peers) (sign_memo) (read_memo) + (sign_message) + (verify_message) + (verify_signed_message) + (verify_encapsulated_message) (set_key_label) (get_key_label) (get_public_key) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index a2783a8584..7c19bb94fe 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -107,6 +107,11 @@ using std::endl; namespace detail { +static const string ENC_HEADER( "-----BEGIN BITSHARES SIGNED MESSAGE-----\n" ); +static const string ENC_META( "-----BEGIN META-----\n" ); +static const string ENC_SIG( "-----BEGIN SIGNATURE-----\n" ); +static const string ENC_FOOTER( "-----END BITSHARES SIGNED MESSAGE-----" ); + struct operation_result_printer { public: @@ -297,6 +302,26 @@ class htlc_hash_to_string_visitor } }; +/* meta contains lines of the form "key=value". + * Returns the value for the corresponding key, throws if key is not present. */ +static string meta_extract( const string& meta, const string& key ) +{ + FC_ASSERT( meta.size() > key.size(), "Key '${k}' not found!", ("k",key) ); + size_t start; + if( meta.substr( 0, key.size() ) == key && meta[key.size()] == '=' ) + start = 0; + else + { + start = meta.find( "\n" + key + "=" ); + FC_ASSERT( start != string::npos, "Key '${k}' not found!", ("k",key) ); + ++start; + } + start += key.size() + 1; + size_t lf = meta.find( "\n", start ); + if( lf == string::npos ) lf = meta.size(); + return meta.substr( start, lf - start ); +} + class wallet_api_impl { public: @@ -684,12 +709,12 @@ class wallet_api_impl "." + fc::to_string(id.instance.value); return asset_id; } - optional find_asset(asset_id_type id)const + optional find_asset(asset_id_type id)const { auto rec = _remote_db->get_assets({asset_id_to_string(id)}).front(); return rec; } - optional find_asset(string asset_symbol_or_id)const + optional find_asset(string asset_symbol_or_id)const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); @@ -708,13 +733,13 @@ class wallet_api_impl return rec; } } - asset_object get_asset(asset_id_type id)const + extended_asset_object get_asset(asset_id_type id)const { auto opt = find_asset(id); FC_ASSERT(opt); return *opt; } - asset_object get_asset(string asset_symbol_or_id)const + extended_asset_object get_asset(string asset_symbol_or_id)const { auto opt = find_asset(asset_symbol_or_id); FC_ASSERT(opt); @@ -736,7 +761,7 @@ class wallet_api_impl asset_id_type get_asset_id(string asset_symbol_or_id) const { FC_ASSERT( asset_symbol_or_id.size() > 0 ); - vector> opt_asset; + vector> opt_asset; if( std::isdigit( asset_symbol_or_id.front() ) ) return fc::variant(asset_symbol_or_id, 1).as( 1 ); opt_asset = _remote_db->lookup_asset_symbols( {asset_symbol_or_id} ); @@ -1159,35 +1184,11 @@ class wallet_api_impl account_create_op.options.memo_key = active; signed_transaction tx; - tx.operations.push_back( account_create_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees() ); - - vector paying_keys = registrar_account_object.active.get_keys(); - - auto dyn_props = get_dynamic_global_properties(); - tx.set_reference_block( dyn_props.head_block_id ); - tx.set_expiration( dyn_props.time + fc::seconds(30) ); tx.validate(); - for( public_key_type& key : paying_keys ) - { - auto it = _keys.find(key); - if( it != _keys.end() ) - { - fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); - if( !privkey.valid() ) - { - FC_ASSERT( false, "Malformed private key in _keys" ); - } - tx.sign( *privkey, _chain_id ); - } - } - - if( broadcast ) - _remote_net_broadcast->broadcast_transaction( tx ); - return tx; + return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (name)(owner)(active)(registrar_account) (referrer_account)(referrer_percent)(broadcast) ) } @@ -1284,38 +1285,17 @@ class wallet_api_impl // account_create_op.fee = account_create_op.calculate_fee(db.current_fee_schedule()); signed_transaction tx; - tx.operations.push_back( account_create_op ); - set_operation_fees( tx, _remote_db->get_global_properties().parameters.get_current_fees()); - - vector paying_keys = registrar_account_object.active.get_keys(); - - auto dyn_props = get_dynamic_global_properties(); - tx.set_reference_block( dyn_props.head_block_id ); - tx.set_expiration( dyn_props.time + fc::seconds(30) ); tx.validate(); - for( public_key_type& key : paying_keys ) - { - auto it = _keys.find(key); - if( it != _keys.end() ) - { - fc::optional< fc::ecc::private_key > privkey = wif_to_key( it->second ); - FC_ASSERT( privkey.valid(), "Malformed private key in _keys" ); - tx.sign( *privkey, _chain_id ); - } - } - // we do not insert owner_privkey here because // it is intended to only be used for key recovery _wallet.pending_account_registrations[account_name].push_back(key_to_wif( active_privkey )); _wallet.pending_account_registrations[account_name].push_back(key_to_wif( memo_privkey )); if( save_wallet ) save_wallet_file(); - if( broadcast ) - _remote_net_broadcast->broadcast_transaction( tx ); - return tx; + return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (account_name)(registrar_account)(referrer_account)(broadcast) ) } signed_transaction create_account_with_brain_key(string brain_key, @@ -2289,6 +2269,81 @@ class wallet_api_impl return clear_text; } + signed_message sign_message(string signer, string message) + { + FC_ASSERT( !self.is_locked() ); + + const account_object from_account = get_account(signer); + auto dynamic_props = get_dynamic_global_properties(); + + signed_message msg; + msg.message = message; + msg.meta.account = from_account.name; + msg.meta.memo_key = from_account.options.memo_key; + msg.meta.block = dynamic_props.head_block_number; + msg.meta.time = dynamic_props.time.to_iso_string() + "Z"; + msg.signature = get_private_key( from_account.options.memo_key ).sign_compact( msg.digest() ); + return msg; + } + + bool verify_message( const string& message, const string& account, int block, const string& time, + const compact_signature& sig ) + { + const account_object from_account = get_account( account ); + + signed_message msg; + msg.message = message; + msg.meta.account = from_account.name; + msg.meta.memo_key = from_account.options.memo_key; + msg.meta.block = block; + msg.meta.time = time; + msg.signature = sig; + + return verify_signed_message( msg ); + } + + bool verify_signed_message( const signed_message& message ) + { + if( !message.signature.valid() ) return false; + + const account_object from_account = get_account( message.meta.account ); + + const public_key signer( *message.signature, message.digest() ); + if( !( message.meta.memo_key == signer ) ) return false; + FC_ASSERT( from_account.options.memo_key == signer, + "Message was signed by contained key, but it doesn't belong to the contained account!" ); + + return true; + } + + bool verify_encapsulated_message( const string& message ) + { + signed_message msg; + size_t begin_p = message.find( ENC_HEADER ); + FC_ASSERT( begin_p != string::npos, "BEGIN MESSAGE line not found!" ); + size_t meta_p = message.find( ENC_META, begin_p ); + FC_ASSERT( meta_p != string::npos, "BEGIN META line not found!" ); + FC_ASSERT( meta_p >= begin_p + ENC_HEADER.size() + 1, "Missing message!?" ); + size_t sig_p = message.find( ENC_SIG, meta_p ); + FC_ASSERT( sig_p != string::npos, "BEGIN SIGNATURE line not found!" ); + FC_ASSERT( sig_p >= meta_p + ENC_META.size(), "Missing metadata?!" ); + size_t end_p = message.find( ENC_FOOTER, meta_p ); + FC_ASSERT( end_p != string::npos, "END MESSAGE line not found!" ); + FC_ASSERT( end_p >= sig_p + ENC_SIG.size() + 1, "Missing signature?!" ); + + msg.message = message.substr( begin_p + ENC_HEADER.size(), meta_p - begin_p - ENC_HEADER.size() - 1 ); + const string meta = message.substr( meta_p + ENC_META.size(), sig_p - meta_p - ENC_META.size() ); + const string sig = message.substr( sig_p + ENC_SIG.size(), end_p - sig_p - ENC_SIG.size() - 1 ); + + msg.meta.account = meta_extract( meta, "account" ); + msg.meta.memo_key = public_key_type( meta_extract( meta, "memokey" ) ); + msg.meta.block = boost::lexical_cast( meta_extract( meta, "block" ) ); + msg.meta.time = meta_extract( meta, "timestamp" ); + msg.signature = variant(sig).as< fc::ecc::compact_signature >( 5 ); + + return verify_signed_message( msg ); + } + signed_transaction sell_asset(string seller_account, string amount_to_sell, string symbol_to_sell, @@ -2682,6 +2737,25 @@ class wallet_api_impl return ss.str(); }; + m["sign_message"] = [this](variant result, const fc::variants& a) + { + auto r = result.as( GRAPHENE_MAX_NESTED_OBJECTS ); + + fc::stringstream encapsulated; + encapsulated << ENC_HEADER; + encapsulated << r.message << '\n'; + encapsulated << ENC_META; + encapsulated << "account=" << r.meta.account << '\n'; + encapsulated << "memokey=" << std::string( r.meta.memo_key ) << '\n'; + encapsulated << "block=" << r.meta.block << '\n'; + encapsulated << "timestamp=" << r.meta.time << '\n'; + encapsulated << ENC_SIG; + encapsulated << fc::to_hex( (const char*)r.signature->data, r.signature->size() ) << '\n'; + encapsulated << ENC_FOOTER; + + return encapsulated.str(); + }; + return m; } @@ -3181,6 +3255,17 @@ std::string operation_result_printer::operator()(const asset& a) }}} namespace graphene { namespace wallet { + fc::sha256 signed_message::digest()const + { + fc::stringstream to_sign; + to_sign << message << '\n'; + to_sign << "account=" << meta.account << '\n'; + to_sign << "memokey=" << std::string( meta.memo_key ) << '\n'; + to_sign << "block=" << meta.block << '\n'; + to_sign << "timestamp=" << meta.time; + + return fc::sha256::hash( to_sign.str() ); + } vector utility::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys) { // Safety-check @@ -3275,7 +3360,7 @@ vector wallet_api::list_account_balances(const string& id) return my->_remote_db->get_account_balances(id, flat_set()); } -vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const +vector wallet_api::list_assets(const string& lowerbound, uint32_t limit)const { return my->_remote_db->list_assets( lowerbound, limit ); } @@ -3645,7 +3730,7 @@ account_object wallet_api::get_account(string account_name_or_id) const return my->get_account(account_name_or_id); } -asset_object wallet_api::get_asset(string asset_name_or_id) const +extended_asset_object wallet_api::get_asset(string asset_name_or_id) const { auto a = my->find_asset(asset_name_or_id); FC_ASSERT(a); @@ -4217,6 +4302,10 @@ string wallet_api::gethelp(const string& method)const std::stringstream ss; ss << "\n"; + std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); + if (!doxygenHelpString.empty()) + ss << doxygenHelpString << "\n"; + if( method == "import_key" ) { ss << "usage: import_key ACCOUNT_NAME_OR_ID WIF_PRIVATE_KEY\n\n"; @@ -4264,14 +4353,8 @@ string wallet_api::gethelp(const string& method)const ss << fc::json::to_pretty_string( graphene::chain::bitasset_options() ); ss << "\nBITASSET_OPTIONS may be null\n"; } - else - { - std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); - if (!doxygenHelpString.empty()) - ss << doxygenHelpString; - else - ss << "No help defined for method " << method << "\n"; - } + else if (doxygenHelpString.empty()) + ss << "No help defined for method " << method << "\n"; return ss.str(); } @@ -4527,6 +4610,38 @@ string wallet_api::read_memo(const memo_data& memo) return my->read_memo(memo); } +signed_message wallet_api::sign_message(string signer, string message) +{ + FC_ASSERT(!is_locked()); + return my->sign_message(signer, message); +} + +bool wallet_api::verify_message( string message, string account, int block, const string& time, compact_signature sig ) +{ + return my->verify_message( message, account, block, time, sig ); +} + +/** Verify a message signed with sign_message + * + * @param message the signed_message structure containing message, meta data and signature + * @return true if signature matches + */ +bool wallet_api::verify_signed_message( signed_message message ) +{ + return my->verify_signed_message( message ); +} + +/** Verify a message signed with sign_message, in its encapsulated form. + * + * @param message the complete encapsulated message string including separators and line feeds + * @return true if signature matches + */ +bool wallet_api::verify_encapsulated_message( string message ) +{ + return my->verify_encapsulated_message( message ); +} + + string wallet_api::get_key_label( public_key_type key )const { auto key_itr = my->_wallet.labeled_keys.get().find(key); @@ -4879,13 +4994,13 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, -/** +/* * Transfers a public balance from @from to one or more blinded balances using a * stealth transfer. */ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name, string asset_symbol, - /** map from key or label to amount */ + /* map from key or label to amount */ vector> to_amounts, bool broadcast ) { try { diff --git a/programs/build_helpers/build_and_test b/programs/build_helpers/build_and_test new file mode 100755 index 0000000000..5c0d268cb9 --- /dev/null +++ b/programs/build_helpers/build_and_test @@ -0,0 +1,15 @@ +#!/bin/bash + +programs/build_helpers/buildstep -s 3500 +ccache -s +programs/build_helpers/buildstep Prepare 1 "sed -i '/tests/d' libraries/fc/CMakeLists.txt" +programs/build_helpers/buildstep cmake 5 "cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON ." +programs/build_helpers/buildstep make.everything 2400 "programs/build_helpers/make_with_sonar bw-output -j 2 witness_node chain_test cli_test" +set -o pipefail +programs/build_helpers/buildstep run.chain_test 240 "libraries/fc/tests/run-parallel-tests.sh tests/chain_test" +programs/build_helpers/buildstep run.cli_test 60 "libraries/fc/tests/run-parallel-tests.sh tests/cli_test" +'programs/build_helpers/buildstep prepare.sonar 20 "find libraries/[acdenptuw]*/CMakeFiles/*.dir programs/[cdgjsw]*/CMakeFiles/*.dir -type d -print | while read d; do gcov -o \"\$d\" \"\${d/CMakeFiles*.dir//}\"/*.cpp; done >/dev/null; programs/build_helpers/set_sonar_branch sonar-project.properties" || true' +'programs/build_helpers/buildstep run.sonar 1200 "which sonar-scanner && sonar-scanner" || true' +programs/build_helpers/buildstep end 0 +ccache -s + diff --git a/programs/build_helpers/build_protocol b/programs/build_helpers/build_protocol new file mode 100755 index 0000000000..6307400586 --- /dev/null +++ b/programs/build_helpers/build_protocol @@ -0,0 +1,10 @@ +#!/bin/bash + +programs/build_helpers/buildstep -s 3500 +ccache -s +programs/build_helpers/buildstep Prepare 1 "sed -i '/tests/d' libraries/fc/CMakeLists.txt" +programs/build_helpers/buildstep cmake 5 "cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=--coverage -DCMAKE_CXX_FLAGS=--coverage -DBoost_USE_STATIC_LIBS=OFF -DCMAKE_CXX_OUTPUT_EXTENSION_REPLACE=ON ." +programs/build_helpers/buildstep make.fc 200 "make -j 2 fc" +programs/build_helpers/buildstep make.custom_auths 1000 "make -j 2 graphene_protocol" +programs/build_helpers/buildstep end 0 +ccache -s diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 630256615a..e33c1c28fe 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -309,9 +309,11 @@ int main( int argc, char** argv ) for( auto& name_formatter : wapiptr->get_result_formatters() ) wallet_cli->format_result( name_formatter.first, name_formatter.second ); + std::cout << "\nType \"help\" for a list of available commands.\n"; + std::cout << "Type \"gethelp \" for info about individual commands.\n\n"; if( wapiptr->is_new() ) { - std::cout << "Please use the set_password method to initialize a new wallet before continuing\n"; + std::cout << "Please use the \"set_password\" method to initialize a new wallet before continuing\n"; wallet_cli->set_prompt( "new >>> " ); } else diff --git a/programs/genesis_util/CMakeLists.txt b/programs/genesis_util/CMakeLists.txt index 9c3b278d23..31df5e5968 100644 --- a/programs/genesis_util/CMakeLists.txt +++ b/programs/genesis_util/CMakeLists.txt @@ -18,7 +18,7 @@ install( TARGETS add_executable( get_dev_key get_dev_key.cpp ) target_link_libraries( get_dev_key - PRIVATE graphene_app graphene_chain graphene_egenesis_none graphene_utilities fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) + PRIVATE graphene_utilities graphene_protocol fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) install( TARGETS get_dev_key diff --git a/programs/genesis_util/get_dev_key.cpp b/programs/genesis_util/get_dev_key.cpp index 4fc6a42f2c..52fa986e15 100644 --- a/programs/genesis_util/get_dev_key.cpp +++ b/programs/genesis_util/get_dev_key.cpp @@ -44,7 +44,7 @@ int main( int argc, char** argv ) { std::string dev_key_prefix; bool need_help = false; - if( argc < 2 ) + if( argc < 3 ) // requires at least a prefix and a suffix need_help = true; else { @@ -57,13 +57,13 @@ int main( int argc, char** argv ) if( need_help ) { - std::cerr << argc << " " << argv[1] << "\n"; - std::cerr << "get-dev-key ...\n" - "\n" - "example:\n" - "\n" - "get-dev-key wxyz- owner-5 active-7 balance-9 wit-block-signing-3 wit-owner-5 wit-active-33\n" - "get-dev-key wxyz- wit-block-signing-0:101\n" + std::cerr << "\nThis program generates keys with specified prefix and suffix(es) as seed(s).\n\n" + "Syntax:\n\n" + " get_dev_key ...\n\n" + "Examples:\n\n" + " get_dev_key nath an\n" + " get_dev_key wxyz- owner-5 active-7 balance-9 wit-block-signing-3 wit-owner-5 wit-active-33\n" + " get_dev_key wxyz- wit-block-signing-0:101\n" "\n"; return 1; } @@ -121,7 +121,7 @@ int main( int argc, char** argv ) } catch ( const fc::exception& e ) { - std::cout << e.to_detail_string() << "\n"; + std::cerr << e.to_detail_string() << "\n"; return 1; } return 0; diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index 4815879a40..d671a93c17 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -12,7 +12,9 @@ endif() # We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246 target_link_libraries( witness_node -PRIVATE graphene_app graphene_delayed_node graphene_account_history graphene_elasticsearch graphene_market_history graphene_grouped_orders graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full graphene_snapshot graphene_es_objects fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) +PRIVATE graphene_app graphene_delayed_node graphene_account_history graphene_elasticsearch graphene_market_history graphene_grouped_orders graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full graphene_snapshot graphene_es_objects + graphene_api_helper_indexes + fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) install( TARGETS witness_node diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index c4748697c6..acbda6386c 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -68,7 +69,8 @@ int main(int argc, char** argv) { ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.") ("version,v", "Display version information") - ("plugins", bpo::value()->default_value("witness account_history market_history grouped_orders"), + ("plugins", bpo::value() + ->default_value("witness account_history market_history grouped_orders api_helper_indexes"), "Space-separated list of plugins to activate"); bpo::variables_map options; @@ -78,7 +80,8 @@ int main(int argc, char** argv) { cfg_options.add(cfg); cfg_options.add_options() - ("plugins", bpo::value()->default_value("witness account_history market_history grouped_orders"), + ("plugins", bpo::value() + ->default_value("witness account_history market_history grouped_orders api_helper_indexes"), "Space-separated list of plugins to activate"); auto witness_plug = node->register_plugin(); @@ -90,6 +93,7 @@ int main(int argc, char** argv) { auto snapshot_plug = node->register_plugin(); auto es_objects_plug = node->register_plugin(); auto grouped_orders_plug = node->register_plugin(); + auto api_helper_indexes_plug = node->register_plugin(); // add plugin options to config try diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b863c6347e..d05bf13183 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,7 +8,10 @@ endif() file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${COMMON_SOURCES} ${UNIT_TESTS} ) -target_link_libraries( chain_test graphene_chain graphene_app graphene_witness graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_test + graphene_chain graphene_app graphene_witness graphene_account_history graphene_elasticsearch + graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes + fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) set_source_files_properties( tests/common/database_fixture.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) @@ -16,11 +19,17 @@ endif(MSVC) file(GLOB PERFORMANCE_TESTS "performance/*.cpp") add_executable( performance_test ${COMMON_SOURCES} ${PERFORMANCE_TESTS} ) -target_link_libraries( performance_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( performance_test + graphene_chain graphene_app graphene_account_history graphene_elasticsearch + graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes + fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB BENCH_MARKS "benchmarks/*.cpp") add_executable( chain_bench ${COMMON_SOURCES} ${BENCH_MARKS} ) -target_link_libraries( chain_bench graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chain_bench + graphene_chain graphene_app graphene_account_history graphene_elasticsearch + graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes + fc ${PLATFORM_SPECIFIC_LIBS} ) file(GLOB APP_SOURCES "app/*.cpp") add_executable( app_test ${APP_SOURCES} ) @@ -38,6 +47,9 @@ endif(MSVC) file(GLOB ES_SOURCES "elasticsearch/*.cpp") add_executable( es_test ${COMMON_SOURCES} ${ES_SOURCES} ) -target_link_libraries( es_test graphene_chain graphene_app graphene_account_history graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( es_test + graphene_chain graphene_app graphene_account_history graphene_elasticsearch + graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes + fc ${PLATFORM_SPECIFIC_LIBS} ) add_subdirectory( generate_empty_blocks ) diff --git a/tests/cli/main.cpp b/tests/cli/main.cpp index ccff75736a..13510a4c8d 100644 --- a/tests/cli/main.cpp +++ b/tests/cli/main.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include @@ -1084,3 +1086,99 @@ BOOST_AUTO_TEST_CASE( cli_create_htlc ) } app1->shutdown(); } + +static string encapsulate( const graphene::wallet::signed_message& msg ) +{ + fc::stringstream encapsulated; + encapsulated << "-----BEGIN BITSHARES SIGNED MESSAGE-----\n" + << msg.message << '\n' + << "-----BEGIN META-----\n" + << "account=" << msg.meta.account << '\n' + << "memokey=" << std::string( msg.meta.memo_key ) << '\n' + << "block=" << msg.meta.block << '\n' + << "timestamp=" << msg.meta.time << '\n' + << "-----BEGIN SIGNATURE-----\n" + << fc::to_hex( (const char*)msg.signature->data, msg.signature->size() ) << '\n' + << "-----END BITSHARES SIGNED MESSAGE-----"; + return encapsulated.str(); +} + +/****** + * Check signing/verifying a message with a memo key + */ +BOOST_FIXTURE_TEST_CASE( cli_sign_message, cli_fixture ) +{ try { + const auto nathan_priv = *wif_to_key( nathan_keys[0] ); + const public_key_type nathan_pub( nathan_priv.get_public_key() ); + + // account does not exist + BOOST_REQUIRE_THROW( con.wallet_api_ptr->sign_message( "dan", "123" ), fc::assert_exception ); + + // success + graphene::wallet::signed_message msg = con.wallet_api_ptr->sign_message( "nathan", "123" ); + BOOST_CHECK_EQUAL( "123", msg.message ); + BOOST_CHECK_EQUAL( "nathan", msg.meta.account ); + BOOST_CHECK_EQUAL( std::string( nathan_pub ), std::string( msg.meta.memo_key ) ); + BOOST_CHECK( msg.signature.valid() ); + + // change message, verify failure + msg.message = "124"; + BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time, + *msg.signature ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) ); + msg.message = "123"; + + // change account, verify failure + msg.meta.account = "dan"; + BOOST_REQUIRE_THROW( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, + msg.meta.time, *msg.signature ), fc::assert_exception ); + BOOST_REQUIRE_THROW( !con.wallet_api_ptr->verify_signed_message( msg ), fc::assert_exception ); + BOOST_REQUIRE_THROW( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ), fc::assert_exception); + msg.meta.account = "nathan"; + + // change key, verify failure + ++msg.meta.memo_key.key_data.data[1]; + //BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time, + // *msg.signature ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) ); + --msg.meta.memo_key.key_data.data[1]; + + // change block, verify failure + ++msg.meta.block; + BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time, + *msg.signature ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) ); + --msg.meta.block; + + // change time, verify failure + ++msg.meta.time[0]; + BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time, + *msg.signature ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) ); + BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) ); + --msg.meta.time[0]; + + // change signature, verify failure + ++msg.signature->data[1]; + try { + BOOST_CHECK( !con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time, + *msg.signature ) ); + } catch( const fc::assert_exception& ) {} // failure to reconstruct key from signature is ok as well + try { + BOOST_CHECK( !con.wallet_api_ptr->verify_signed_message( msg ) ); + } catch( const fc::assert_exception& ) {} // failure to reconstruct key from signature is ok as well + try { + BOOST_CHECK( !con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) ); + } catch( const fc::assert_exception& ) {} // failure to reconstruct key from signature is ok as well + --msg.signature->data[1]; + + // verify success + BOOST_CHECK( con.wallet_api_ptr->verify_message( msg.message, msg.meta.account, msg.meta.block, msg.meta.time, + *msg.signature ) ); + BOOST_CHECK( con.wallet_api_ptr->verify_signed_message( msg ) ); + BOOST_CHECK( con.wallet_api_ptr->verify_encapsulated_message( encapsulate( msg ) ) ); + +} FC_LOG_AND_RETHROW() } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 0760f68bc2..e31eb712c2 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,9 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) else genesis_state.initial_active_witnesses = 10; + genesis_state.immutable_parameters.min_committee_member_count = INITIAL_COMMITTEE_MEMBER_COUNT; + genesis_state.immutable_parameters.min_witness_count = INITIAL_WITNESS_COUNT; + for( unsigned int i = 0; i < genesis_state.initial_active_witnesses; ++i ) { auto name = "init"+fc::to_string(i); @@ -129,71 +133,125 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) { options.insert(std::make_pair("max-ops-per-account", boost::program_options::variable_value((uint64_t)125, false))); options.insert(std::make_pair("api-limit-get-account-history-operations", boost::program_options::variable_value((uint64_t)300, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("account_history"), false))); } if(current_test_name =="api_limit_get_account_history") { options.insert(std::make_pair("max-ops-per-account", boost::program_options::variable_value((uint64_t)125, false))); options.insert(std::make_pair("api-limit-get-account-history", boost::program_options::variable_value((uint64_t)250, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("account_history"), false))); } if(current_test_name =="api_limit_get_grouped_limit_orders") { options.insert(std::make_pair("api-limit-get-grouped-limit-orders", boost::program_options::variable_value((uint64_t)250, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("grouped_orders"), false))); } if(current_test_name =="api_limit_get_relative_account_history") { options.insert(std::make_pair("max-ops-per-account", boost::program_options::variable_value((uint64_t)125, false))); options.insert(std::make_pair("api-limit-get-relative-account-history", boost::program_options::variable_value((uint64_t)250, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("account_history"), false))); } if(current_test_name =="api_limit_get_account_history_by_operations") { options.insert(std::make_pair("api-limit-get-account-history-by-operations", boost::program_options::variable_value((uint64_t)250, false))); options.insert(std::make_pair("api-limit-get-relative-account-history", boost::program_options::variable_value((uint64_t)250, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("account_history"), false))); } if(current_test_name =="api_limit_get_asset_holders") { options.insert(std::make_pair("api-limit-get-asset-holders", boost::program_options::variable_value((uint64_t)250, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("account_history"), false))); } if(current_test_name =="api_limit_get_key_references") { options.insert(std::make_pair("api-limit-get-key-references", boost::program_options::variable_value((uint64_t)200, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value(string("account_history"), false))); } if(current_test_name =="api_limit_get_limit_orders") { options.insert(std::make_pair("api-limit-get-limit-orders", boost::program_options::variable_value( (uint64_t)350, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value( - string("account_history"), false))); } if(current_test_name =="api_limit_get_call_orders") { options.insert(std::make_pair("api-limit-get-call-orders", boost::program_options::variable_value( (uint64_t)350, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value( - string("account_history"), false))); } if(current_test_name =="api_limit_get_settle_orders") { options.insert(std::make_pair("api-limit-get-settle-orders", boost::program_options::variable_value( (uint64_t)350, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value( - string("account_history"), false))); } if(current_test_name =="api_limit_get_order_book") { options.insert(std::make_pair("api-limit-get-order-book", boost::program_options::variable_value( (uint64_t)80, false))); - options.insert(std::make_pair("plugins", boost::program_options::variable_value( - string("account_history"), false))); } - - // add account tracking for ahplugin for special test case with track-account enabled + if( current_test_name == "asset_in_collateral" ) + { + options.insert( std::make_pair( "plugins", + boost::program_options::variable_value( string("api_helper_indexes"), false ) ) ); + } + if(current_test_name =="api_limit_lookup_accounts") + { + options.insert(std::make_pair("api-limit-lookup-accounts", boost::program_options::variable_value + ((uint64_t)200, false))); + } + if(current_test_name =="api_limit_lookup_witness_accounts") + { + options.insert(std::make_pair("api-limit-lookup-witness-accounts", boost::program_options::variable_value + ((uint64_t)200, false))); + } + if(current_test_name =="api_limit_lookup_committee_member_accounts") + { + options.insert(std::make_pair("api-limit-lookup-committee-member-accounts", boost::program_options::variable_value + ((uint64_t)200, false))); + } + if(current_test_name =="api_limit_lookup_committee_member_accounts") + { + options.insert(std::make_pair("api-limit-lookup-committee-member-accounts", boost::program_options::variable_value + ((uint64_t)200, false))); + } + if(current_test_name =="api_limit_lookup_vote_ids") + { + options.insert(std::make_pair("api-limit-lookup-vote-ids", boost::program_options::variable_value + ((uint64_t)3, false))); + } + if(current_test_name =="api_limit_get_account_limit_orders") + { + options.insert(std::make_pair("api-limit-get-account-limit-orders", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_collateral_bids") + { + options.insert(std::make_pair("api-limit-get-collateral-bids", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_top_markets") + { + options.insert(std::make_pair("api-limit-get-top-markets", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_trade_history") + { + options.insert(std::make_pair("api-limit-get-trade-history", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_trade_history_by_sequence") + { + options.insert(std::make_pair("api-limit-get-trade-history-by-sequence", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_withdraw_permissions_by_giver") + { + options.insert(std::make_pair("api-limit-get-withdraw-permissions-by-giver", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_withdraw_permissions_by_recipient") + { + options.insert(std::make_pair("api-limit-get-withdraw-permissions-by-recipient", boost::program_options::variable_value + ((uint64_t)250, false))); + } + if(current_test_name =="api_limit_get_full_accounts2") + { + options.insert(std::make_pair("api-limit-get-full-accounts", boost::program_options::variable_value + ((uint64_t)200, false))); + } + // add account tracking for ahplugin for special test case with track-account enabled if( !options.count("track-account") && current_test_name == "track_account") { std::vector track_account; std::string track = "\"1.2.17\""; @@ -234,12 +292,8 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) ahplugin->plugin_set_app(&app); ahplugin->plugin_initialize(options); ahplugin->plugin_startup(); - if (current_test_name == "api_limit_get_account_history_operations" || current_test_name == "api_limit_get_account_history" - || current_test_name == "api_limit_get_grouped_limit_orders" || current_test_name == "api_limit_get_relative_account_history" - || current_test_name == "api_limit_get_account_history_by_operations" || current_test_name =="api_limit_get_asset_holders" - || current_test_name =="api_limit_get_key_references" || current_test_name =="api_limit_get_limit_orders" - || current_test_name =="api_limit_get_call_orders" || current_test_name =="api_limit_get_settle_orders" - || current_test_name =="api_limit_get_order_book") + + if(validation_current_test_name_for_setting_api_limit(current_test_name)) { app.initialize(graphene::utilities::temp_directory_path(), options); app.set_api_limit(); @@ -263,6 +317,13 @@ database_fixture::database_fixture(const fc::time_point_sec &initial_timestamp) esobjects_plugin->plugin_initialize(options); esobjects_plugin->plugin_startup(); } + else if( current_test_name == "asset_in_collateral" ) + { + auto ahiplugin = app.register_plugin(); + ahiplugin->plugin_set_app(&app); + ahiplugin->plugin_initialize(options); + ahiplugin->plugin_startup(); + } options.insert(std::make_pair("bucket-size", boost::program_options::variable_value(string("[15]"),false))); mhplugin->plugin_set_app(&app); @@ -310,6 +371,46 @@ database_fixture::~database_fixture() } } +void database_fixture::vote_for_committee_and_witnesses(uint16_t num_committee, uint16_t num_witness) +{ try { + + auto &init0 = get_account("init0"); + fund(init0, asset(10)); + + flat_set votes; + + const auto& wits = db.get_index_type().indices().get(); + num_witness = std::min(num_witness, (uint16_t) wits.size()); + auto wit_end = wits.begin(); + std::advance(wit_end, num_witness); + std::transform(wits.begin(), wit_end, + std::inserter(votes, votes.end()), + [](const witness_object& w) { return w.vote_id; }); + + const auto& comms = db.get_index_type().indices().get(); + num_committee = std::min(num_committee, (uint16_t) comms.size()); + auto comm_end = comms.begin(); + std::advance(comm_end, num_committee); + std::transform(comms.begin(), comm_end, + std::inserter(votes, votes.end()), + [](const committee_member_object& cm) { return cm.vote_id; }); + + account_update_operation op; + op.account = init0.get_id(); + op.new_options = init0.options; + op.new_options->votes = votes; + op.new_options->num_witness = num_witness; + op.new_options->num_committee = num_committee; + + op.fee = db.current_fee_schedule().calculate_fee( op ); + + trx.operations.push_back(op); + trx.validate(); + PUSH_TX(db, trx, ~0); + trx.operations.clear(); + +} FC_CAPTURE_AND_RETHROW() } + fc::ecc::private_key database_fixture::generate_private_key(string seed) { static const fc::ecc::private_key committee = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); @@ -324,7 +425,30 @@ string database_fixture::generate_anon_acct_name() // to workaround issue #46 return "anon-acct-x" + std::to_string( anon_acct_count++ ); } +bool database_fixture::validation_current_test_name_for_setting_api_limit(string & current_test_name) const +{ + vector valid_testcase {"api_limit_get_account_history_operations","api_limit_get_account_history" + ,"api_limit_get_grouped_limit_orders","api_limit_get_relative_account_history" + ,"api_limit_get_account_history_by_operations","api_limit_get_asset_holders" + ,"api_limit_get_key_references","api_limit_get_limit_orders" + ,"api_limit_get_call_orders","api_limit_get_settle_orders" + ,"api_limit_get_order_book","api_limit_lookup_accounts" + ,"api_limit_lookup_witness_accounts","api_limit_lookup_committee_member_accounts" + ,"api_limit_lookup_vote_ids","api_limit_get_account_limit_orders" + ,"api_limit_get_collateral_bids","api_limit_get_top_markets" + ,"api_limit_get_trade_history", "api_limit_get_trade_history_by_sequence" + ,"api_limit_get_withdraw_permissions_by_giver","api_limit_get_withdraw_permissions_by_recipient" + ,"api_limit_get_full_accounts2"}; + for(string i_valid_testcase: valid_testcase) + { + if(i_valid_testcase.compare(current_test_name)==0) + { + return true; + } + } + return false; +} void database_fixture::verify_asset_supplies( const database& db ) { //wlog("*** Begin asset supply verification ***"); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index bf764f45f6..7747c1c11a 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -171,6 +171,9 @@ extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP; #define ACTORS_IMPL(r, data, elem) ACTOR(elem) #define ACTORS(names) BOOST_PP_SEQ_FOR_EACH(ACTORS_IMPL, ~, names) +#define INITIAL_WITNESS_COUNT (9u) +#define INITIAL_COMMITTEE_MEMBER_COUNT INITIAL_WITNESS_COUNT + namespace graphene { namespace chain { class clearable_block : public signed_block { @@ -205,6 +208,7 @@ struct database_fixture { string generate_anon_acct_name(); static void verify_asset_supplies( const database& db ); void open_database(); + void vote_for_committee_and_witnesses(uint16_t num_committee, uint16_t num_witness); signed_block generate_block(uint32_t skip = ~0, const fc::ecc::private_key& key = generate_private_key("null_key"), int miss_blocks = 0); @@ -367,6 +371,7 @@ struct database_fixture { vector< operation_history_object > get_operation_history( account_id_type account_id )const; vector< graphene::market_history::order_history_object > get_market_order_history( asset_id_type a, asset_id_type b )const; + bool validation_current_test_name_for_setting_api_limit (string & current_test_name) const; }; namespace test { diff --git a/tests/tests/bitasset_tests.cpp b/tests/tests/bitasset_tests.cpp index 03062fbe6e..62bc8b943d 100644 --- a/tests/tests/bitasset_tests.cpp +++ b/tests/tests/bitasset_tests.cpp @@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE( reset_backing_asset_on_witness_asset ) std::vector active_witnesses; for(const witness_id_type& wit_id : global_props.active_witnesses) active_witnesses.push_back(wit_id(db).witness_account); - BOOST_REQUIRE_EQUAL(active_witnesses.size(), 10lu); + BOOST_REQUIRE_EQUAL(active_witnesses.size(), INITIAL_WITNESS_COUNT); { BOOST_TEST_MESSAGE("Adding price feed 1"); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 9b336eb2d5..d93d9820a2 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -52,6 +52,9 @@ genesis_state_type make_genesis() { auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); genesis_state.initial_active_witnesses = 10; + genesis_state.immutable_parameters.min_committee_member_count = INITIAL_COMMITTEE_MEMBER_COUNT; + genesis_state.immutable_parameters.min_witness_count = INITIAL_WITNESS_COUNT; + for( unsigned int i = 0; i < genesis_state.initial_active_witnesses; ++i ) { auto name = "init"+fc::to_string(i); @@ -1418,25 +1421,33 @@ BOOST_AUTO_TEST_CASE( genesis_reserve_ids ) BOOST_FIXTURE_TEST_CASE( miss_some_blocks, database_fixture ) { try { + // Witnesses scheduled incorrectly in genesis block - reschedule + generate_blocks( witness_schedule_id_type()(db).current_shuffled_witnesses.size() ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + std::vector witnesses = witness_schedule_id_type()(db).current_shuffled_witnesses; - BOOST_CHECK_EQUAL( 10u, witnesses.size() ); + BOOST_CHECK_EQUAL( INITIAL_WITNESS_COUNT, witnesses.size() ); // database_fixture constructor calls generate_block once, signed by witnesses[0] generate_block(); // witnesses[1] generate_block(); // witnesses[2] for( const auto& id : witnesses ) BOOST_CHECK_EQUAL( 0, id(db).total_missed ); // generate_blocks generates another block *now* (witnesses[3]) - // and one at now+10 blocks (witnesses[12%10]) - generate_blocks( db.head_block_time() + db.get_global_properties().parameters.block_interval * 10, true ); - // i. e. 8 blocks are missed in between by witness[4..11%10] + // and one at now+9 blocks (witnesses[12%9]) + generate_blocks( db.head_block_time() + db.get_global_properties().parameters.block_interval * 9, true ); + // i. e. 7 blocks are missed in between by witness[4..11%9] for( uint32_t i = 0; i < witnesses.size(); i++ ) - BOOST_CHECK_EQUAL( (i+7) % 10 < 2 ? 0 : 1, witnesses[i](db).total_missed ); + BOOST_CHECK_EQUAL( (i+6) % 9 < 2 ? 0 : 1, witnesses[i](db).total_missed ); } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( miss_many_blocks, database_fixture ) { try { + // Witnesses scheduled incorrectly in genesis block - reschedule + generate_blocks( witness_schedule_id_type()(db).current_shuffled_witnesses.size() ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + auto get_misses = []( database& db ) { std::map< witness_id_type, uint32_t > misses; for( const auto& witness_id : witness_schedule_id_type()(db).current_shuffled_witnesses ) @@ -1447,8 +1458,9 @@ BOOST_FIXTURE_TEST_CASE( miss_many_blocks, database_fixture ) generate_block(); generate_block(); auto missed_before = get_misses( db ); - // miss 10 maintenance intervals - generate_blocks( db.get_dynamic_global_properties().next_maintenance_time + db.get_global_properties().parameters.maintenance_interval * 10, true ); + // miss 9 maintenance intervals + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time + + db.get_global_properties().parameters.maintenance_interval * 9, true ); generate_block(); generate_block(); generate_block(); diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index a8ba90c63e..2408a5d53b 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -1542,4 +1542,445 @@ BOOST_AUTO_TEST_CASE( api_limit_get_order_book ){ throw; } } + +BOOST_AUTO_TEST_CASE( asset_in_collateral ) +{ try { + ACTORS( (dan)(nathan) ); + fund( nathan ); + fund( dan ); + + graphene::app::database_api db_api( db, &( app.get_options() ) ); + + auto oassets = db_api.get_assets( { GRAPHENE_SYMBOL } ); + BOOST_REQUIRE( !oassets.empty() ); + BOOST_REQUIRE( oassets[0].valid() ); + BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() ); + BOOST_CHECK_EQUAL( 0, oassets[0]->total_in_collateral->value ); + BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() ); + + asset_id_type bitusd_id = create_bitasset( "USDBIT", nathan_id, 100, charge_market_fee ).id; + update_feed_producers( bitusd_id, { nathan_id } ); + asset_id_type bitdan_id = create_bitasset( "DANBIT", dan_id, 100, charge_market_fee ).id; + update_feed_producers( bitdan_id, { nathan_id } ); + asset_id_type btc_id = create_bitasset( "BTC", nathan_id, 100, charge_market_fee, 8, bitusd_id ).id; + update_feed_producers( btc_id, { nathan_id } ); + + oassets = db_api.get_assets( { GRAPHENE_SYMBOL, "USDBIT", "DANBIT", "BTC" } ); + BOOST_REQUIRE_EQUAL( 4, oassets.size() ); + BOOST_REQUIRE( oassets[0].valid() ); + BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() ); + BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[1].valid() ); + BOOST_REQUIRE( oassets[1]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[1]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[2].valid() ); + BOOST_REQUIRE( oassets[2]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[2]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[3].valid() ); + BOOST_REQUIRE( oassets[3]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[3]->total_backing_collateral.valid() ); + BOOST_CHECK_EQUAL( 0, oassets[0]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[1]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[1]->total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[2]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[2]->total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[3]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[3]->total_backing_collateral->value ); + + generate_block(); + fc::usleep(fc::milliseconds(100)); + + const auto& bitusd = bitusd_id( db ); + const auto& bitdan = bitdan_id( db ); + const auto& btc = btc_id( db ); + + { + const auto& core = asset_id_type()( db ); + price_feed current_feed; + current_feed.maintenance_collateral_ratio = 1750; + current_feed.maximum_short_squeeze_ratio = 1100; + current_feed.settlement_price = bitusd.amount(1) / core.amount(5); + publish_feed( bitusd_id, nathan_id, current_feed ); + current_feed.settlement_price = bitdan.amount(1) / core.amount(5); + publish_feed( bitdan_id, nathan_id, current_feed ); + current_feed.settlement_price = btc.amount(1) / bitusd.amount(100); + publish_feed( btc_id, nathan_id, current_feed ); + } + + borrow( nathan_id, bitusd.amount(1000), asset(15000) ); + borrow( dan_id, bitusd.amount(100), asset(2000) ); + + oassets = db_api.get_assets( { GRAPHENE_SYMBOL, "USDBIT", "DANBIT", "BTC" } ); + BOOST_REQUIRE_EQUAL( 4, oassets.size() ); + BOOST_REQUIRE( oassets[0].valid() ); + BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() ); + BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[1].valid() ); + BOOST_REQUIRE( oassets[1]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[1]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[2].valid() ); + BOOST_REQUIRE( oassets[2]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[2]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[3].valid() ); + BOOST_REQUIRE( oassets[3]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[3]->total_backing_collateral.valid() ); + BOOST_CHECK_EQUAL( 17000, oassets[0]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[1]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 17000, oassets[1]->total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[2]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[2]->total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[3]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[3]->total_backing_collateral->value ); + + borrow( nathan_id, bitdan.amount(1000), asset(15000) ); + borrow( nathan_id, btc.amount(5), bitusd.amount(1000) ); + + oassets = db_api.lookup_asset_symbols( { GRAPHENE_SYMBOL, "USDBIT", "DANBIT", "BTC" } ); + BOOST_REQUIRE_EQUAL( 4, oassets.size() ); + BOOST_REQUIRE( oassets[0].valid() ); + BOOST_REQUIRE( oassets[0]->total_in_collateral.valid() ); + BOOST_CHECK( !oassets[0]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[1].valid() ); + BOOST_REQUIRE( oassets[1]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[1]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[2].valid() ); + BOOST_REQUIRE( oassets[2]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[2]->total_backing_collateral.valid() ); + BOOST_REQUIRE( oassets[3].valid() ); + BOOST_REQUIRE( oassets[3]->total_in_collateral.valid() ); + BOOST_REQUIRE( oassets[3]->total_backing_collateral.valid() ); + BOOST_CHECK_EQUAL( 32000, oassets[0]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 1000, oassets[1]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 17000, oassets[1]->total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[2]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 15000, oassets[2]->total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, oassets[3]->total_in_collateral->value ); + BOOST_CHECK_EQUAL( 1000, oassets[3]->total_backing_collateral->value ); + + force_settle( dan_id(db), bitusd.amount(100) ); // settles against nathan, receives 500 CORE collateral + generate_blocks( db.head_block_time() + fc::days(2) ); + fc::usleep(fc::milliseconds(100)); + + auto assets = db_api.list_assets( GRAPHENE_SYMBOL, 1 ); + BOOST_REQUIRE( !assets.empty() ); + BOOST_REQUIRE( assets[0].total_in_collateral.valid() ); + BOOST_CHECK( !assets[0].total_backing_collateral.valid() ); + BOOST_CHECK_EQUAL( 31500, assets[0].total_in_collateral->value ); + + assets = db_api.get_assets_by_issuer( "nathan", asset_id_type(1), 2 ); + BOOST_REQUIRE_EQUAL( 2, assets.size() ); + BOOST_REQUIRE( assets[0].total_in_collateral.valid() ); + BOOST_REQUIRE( assets[0].total_backing_collateral.valid() ); + BOOST_REQUIRE( assets[1].total_in_collateral.valid() ); + BOOST_REQUIRE( assets[1].total_backing_collateral.valid() ); + BOOST_CHECK_EQUAL( 1000, assets[0].total_in_collateral->value ); + BOOST_CHECK_EQUAL( 16500, assets[0].total_backing_collateral->value ); + BOOST_CHECK_EQUAL( 0, assets[1].total_in_collateral->value ); + BOOST_CHECK_EQUAL( 1000, assets[1].total_backing_collateral->value ); + +} FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( api_limit_lookup_accounts ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + auto bob_private_key = generate_private_key("bob"); + account_id_type bob_id = create_account("bob", bob_private_key.get_public_key()).id; + transfer(account_id_type(), bob_id, asset(100)); + generate_block(); + fc::usleep(fc::milliseconds(100)); + GRAPHENE_CHECK_THROW(db_api.lookup_accounts("bob",220), fc::exception); + map result =db_api.lookup_accounts("bob",190); + BOOST_REQUIRE_EQUAL( result.size(), 17u); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( api_limit_lookup_witness_accounts ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + auto bob_private_key = generate_private_key("bob"); + account_id_type bob_id = create_account("bob", bob_private_key.get_public_key()).id; + transfer(account_id_type(), bob_id, asset(100)); + generate_block(); + fc::usleep(fc::milliseconds(100)); + GRAPHENE_CHECK_THROW(db_api.lookup_witness_accounts("bob",220), fc::exception); + map result =db_api.lookup_witness_accounts("bob",190); + BOOST_REQUIRE_EQUAL( result.size(), 10u); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_recipient){ + try{ + graphene::app::database_api db_api( db, &app.get_options()); + ACTORS((bob)) ; + withdraw_permission_id_type withdraw_permission; + transfer(account_id_type(), bob_id, asset(100)); + GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_recipient( + "bob",withdraw_permission, 251), fc::exception); + vector result =db_api.get_withdraw_permissions_by_recipient( + "bob",withdraw_permission,250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_withdraw_permissions_by_giver){ + try{ + graphene::app::database_api db_api( db, &app.get_options()); + ACTORS((bob)) ; + withdraw_permission_id_type withdraw_permission; + transfer(account_id_type(), bob_id, asset(100)); + GRAPHENE_CHECK_THROW(db_api.get_withdraw_permissions_by_giver( + "bob",withdraw_permission, 251), fc::exception); + vector result =db_api.get_withdraw_permissions_by_giver( + "bob",withdraw_permission,250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_trade_history_by_sequence){ + try{ + app.enable_plugin("market_history"); + graphene::app::application_options opt=app.get_options(); + opt.has_market_history_plugin = true; + graphene::app::database_api db_api( db, &opt); + ACTORS((bob) (alice) (fred)) ; + const auto& bitusd = create_bitasset("USDBIT", bob_id); + asset_id_type asset_1, asset_2; + asset_1 = bitusd.id; + asset_2 = asset_id_type(); + GRAPHENE_CHECK_THROW(db_api.get_trade_history_by_sequence( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + 0,fc::time_point_sec(1532008940), 251), fc::exception); + vector result =db_api.get_trade_history_by_sequence( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + 0,fc::time_point_sec(1532008940),250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE(api_limit_get_trade_history){ + try{ + app.enable_plugin("market_history"); + graphene::app::application_options opt=app.get_options(); + opt.has_market_history_plugin = true; + graphene::app::database_api db_api( db, &opt); + ACTORS((bob) (alice) (fred)) ; + const auto& bitusd = create_bitasset("USDBIT", bob_id); + asset_id_type asset_1, asset_2; + asset_1 = bitusd.id; + asset_2 = asset_id_type(); + GRAPHENE_CHECK_THROW(db_api.get_trade_history( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + fc::time_point_sec(1532008920),fc::time_point_sec(1532008940), + 251), fc::exception); + vector result =db_api.get_trade_history( + std::string( static_cast(asset_1)), + std::string( static_cast(asset_2)), + fc::time_point_sec(1532008920),fc::time_point_sec(1532008940),250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_get_full_accounts2 ) { + try { + graphene::app::database_api db_api(db, &(this->app.get_options())); + vector accounts; + for (int i=0; i<201; i++) { + std::string acct_name = "mytempacct" + std::to_string(i); + const account_object& account_name=create_account(acct_name); + accounts.push_back(account_name.name); + } + GRAPHENE_CHECK_THROW(db_api.get_full_accounts(accounts, false), fc::exception); + accounts.erase(accounts.begin()); + auto full_accounts = db_api.get_full_accounts(accounts, false); + BOOST_REQUIRE_EQUAL(full_accounts.size(), 200u); + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_top_markets){ + try{ + app.enable_plugin("market_history"); + graphene::app::application_options opt=app.get_options(); + opt.has_market_history_plugin = true; + graphene::app::database_api db_api( db, &opt); + ACTORS((bob) (alice) (fred)) ; + const auto& bitusd = create_bitasset("USDBIT", bob_id); + asset_id_type asset_1, asset_2; + asset_1 = bitusd.id; + asset_2 = asset_id_type(); + GRAPHENE_CHECK_THROW(db_api.get_top_markets(251), fc::exception); + vector result =db_api.get_top_markets(250); + BOOST_REQUIRE_EQUAL( result.size(), 0u); + }catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_collateral_bids) { + try { + graphene::app::database_api db_api( db, &( app.get_options() )); + + int64_t init_balance = 10000; + ///account_id_type borrower, borrower2, feedproducer; + asset_id_type swan, back; + ACTORS((borrower) (borrower2) (feedproducer)) ; + const auto& bitusd = create_bitasset("USDBIT", feedproducer_id); + swan = bitusd.id; + back = asset_id_type(); + update_feed_producers(swan(db), {feedproducer_id}); + transfer(committee_account, borrower_id, asset(init_balance)); + transfer(committee_account, borrower2_id, asset(init_balance)); + + generate_blocks( HARDFORK_CORE_216_TIME ); + generate_block(); + + price_feed feed; + feed.maintenance_collateral_ratio = 1750; // need to set this explicitly, testnet has a different default + feed.settlement_price = swan(db).amount(1) / back(db).amount(1); + publish_feed(swan(db), feedproducer_id(db), feed); + // start out with 2:1 collateral + borrow(borrower_id(db), swan(db).amount(10), back(db).amount(2*10)); + borrow(borrower2_id(db), swan(db).amount(10), back(db).amount(4*10)); + //feed 1: 2 + feed.settlement_price = swan(db).amount(1) / back(db).amount(2); + publish_feed(swan(db), feedproducer_id(db), feed); + + // this sell order is designed to trigger a black swan + + create_sell_order( borrower2_id(db), swan(db).amount(1), back(db).amount(3) )->id; + BOOST_CHECK( swan(db).bitasset_data(db).has_settlement() ); + //making 3 collateral bids + for (int i=0; i<3; i++) { + + std::string acct_name = "mytempacct" + std::to_string(i); + account_id_type account_id=create_account(acct_name).id; + transfer(committee_account, account_id, asset(init_balance)); + bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1)); + } + auto swan_symbol = swan(db).symbol; + + + //validating normal case; total_bids =3 ; result_bids=3 + vector result_bids = db_api.get_collateral_bids(swan_symbol, 250, 0); + BOOST_CHECK_EQUAL( 3u, result_bids.size() ); + + //verify skip /// inefficient code test + //skip < total_bids; skip=1; total_bids =3 ; result_bids=2 + result_bids = db_api.get_collateral_bids(swan_symbol, 250, 1); + BOOST_CHECK_EQUAL( 2u, result_bids.size() ); + //skip= total_bids; skip=3; total_bids =3 ; result_bids=0 + result_bids = db_api.get_collateral_bids(swan_symbol, 250, 3); + BOOST_CHECK_EQUAL( 0u, result_bids.size() ); + //skip> total_bids; skip=4; total_bids =3 ; result_bids=0 + result_bids = db_api.get_collateral_bids(swan_symbol, 250, 4); + BOOST_CHECK_EQUAL( 0u, result_bids.size() ); + + //verify limit // inefficient code test + //limit= api_limit + for (int i=3; i<255; i++) { + std::string acct_name = "mytempacct" + std::to_string(i); + account_id_type account_id=create_account(acct_name).id; + transfer(committee_account, account_id, asset(init_balance)); + bid_collateral(account_id(db), back(db).amount(10), swan(db).amount(1)); + } + result_bids=db_api.get_collateral_bids(swan_symbol, 250, 0); + BOOST_CHECK_EQUAL( 250u, result_bids.size() ); + //limit> api_limit throw error + GRAPHENE_CHECK_THROW(db_api.get_collateral_bids(swan_symbol, 253, 3), fc::exception); + + } + catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE(api_limit_get_account_limit_orders) { + try { + + ACTORS((seller)); + const auto &bitcny = create_bitasset("CNY"); + const auto &core = asset_id_type()(db); + + int64_t init_balance(10000000); + transfer(committee_account, seller_id, asset(init_balance)); + + /// Create versatile orders + for (size_t i = 0; i < 250; ++i) { + BOOST_CHECK(create_sell_order(seller, core.amount(100), bitcny.amount(250+i))); + } + + graphene::app::database_api db_api( db, &( app.get_options() )); + std::vector results=db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, "CNY",250); + BOOST_REQUIRE_EQUAL( results.size(), 250u); + GRAPHENE_CHECK_THROW( db_api.get_account_limit_orders(seller.name, GRAPHENE_SYMBOL, "CNY",251), fc::exception); + + } + catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} +BOOST_AUTO_TEST_CASE( api_limit_lookup_vote_ids ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + ACTORS( (connie)(whitney)(wolverine) ); + fund(connie); + upgrade_to_lifetime_member(connie); + fund(whitney); + upgrade_to_lifetime_member(whitney); + fund(wolverine); + upgrade_to_lifetime_member(wolverine); + const auto& committee = create_committee_member( connie ); + const auto& witness = create_witness( whitney ); + const auto& worker = create_worker( wolverine_id ); + std::vector votes; + votes.push_back( committee.vote_id ); + votes.push_back( witness.vote_id ); + const auto results = db_api.lookup_vote_ids( votes ); + BOOST_REQUIRE_EQUAL( results.size(), 2u); + votes.push_back( worker.vote_for ); + GRAPHENE_CHECK_THROW(db_api.lookup_vote_ids(votes), fc::exception); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( api_limit_lookup_committee_member_accounts ) { + try{ + graphene::app::database_api db_api( db, &( app.get_options() )); + auto bob_private_key = generate_private_key("bob"); + account_id_type bob_id = create_account("bob", bob_private_key.get_public_key()).id; + transfer(account_id_type(), bob_id, asset(100)); + generate_block(); + fc::usleep(fc::milliseconds(100)); + GRAPHENE_CHECK_THROW(db_api.lookup_committee_member_accounts("bob",220), fc::exception); + std::map result =db_api.lookup_committee_member_accounts("bob",190); + BOOST_REQUIRE_EQUAL( result.size(), 10u); + + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/htlc_tests.cpp b/tests/tests/htlc_tests.cpp index b75aeed456..6ade964236 100644 --- a/tests/tests/htlc_tests.cpp +++ b/tests/tests/htlc_tests.cpp @@ -58,7 +58,7 @@ BOOST_FIXTURE_TEST_SUITE( htlc_tests, database_fixture ) void generate_random_preimage(uint16_t key_size, std::vector& vec) { - std::independent_bits_engine rbe; + std::independent_bits_engine rbe; std::generate(begin(vec), end(vec), std::ref(rbe)); return; } @@ -848,7 +848,7 @@ try { uint16_t preimage_size = 256; std::vector pre_image(256); - std::independent_bits_engine rbe; + std::independent_bits_engine rbe; std::generate(begin(pre_image), end(pre_image), std::ref(rbe)); graphene::chain::htlc_id_type alice_htlc_id_bob; graphene::chain::htlc_id_type alice_htlc_id_carl; @@ -978,6 +978,37 @@ try { full = db_api.get_full_accounts({bob.name}, false); BOOST_CHECK_EQUAL( full[bob.name].htlcs_to.size(), 1 ); + auto list = db_api.list_htlcs(graphene::chain::htlc_id_type(0), 1); + BOOST_CHECK_EQUAL( list.size(), 1 ); + BOOST_CHECK_EQUAL( list[0].id.instance(), 0 ); + + list = db_api.list_htlcs(graphene::chain::htlc_id_type(1), 1); + BOOST_CHECK_EQUAL( list.size(), 1 ); + BOOST_CHECK_EQUAL( list[0].id.instance(), 1 ); + + list = db_api.list_htlcs(graphene::chain::htlc_id_type(2), 1); + BOOST_CHECK_EQUAL( list.size(), 1 ); + BOOST_CHECK_EQUAL( list[0].id.instance(), 2 ); + + list = db_api.list_htlcs(graphene::chain::htlc_id_type(1), 2); + BOOST_CHECK_EQUAL( list.size(), 2 ); + BOOST_CHECK_EQUAL( list[0].id.instance(), 1 ); + BOOST_CHECK_EQUAL( list[1].id.instance(), 2 ); + + list = db_api.list_htlcs(graphene::chain::htlc_id_type(1), 3); + BOOST_CHECK_EQUAL( list.size(), 2 ); + BOOST_CHECK_EQUAL( list[0].id.instance(), 1 ); + BOOST_CHECK_EQUAL( list[1].id.instance(), 2 ); + + list = db_api.list_htlcs(graphene::chain::htlc_id_type(0), 100); + BOOST_CHECK_EQUAL( list.size(), 3 ); + BOOST_CHECK_EQUAL( list[0].id.instance(), 0 ); + BOOST_CHECK_EQUAL( list[1].id.instance(), 1 ); + BOOST_CHECK_EQUAL( list[2].id.instance(), 2 ); + + list = db_api.list_htlcs(graphene::chain::htlc_id_type(10), 100); + BOOST_CHECK_EQUAL( list.size(), 0 ); + } catch (fc::exception &e) { edump((e.to_detail_string())); throw; diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index ce5255f4e8..f5deed6ddd 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1705,7 +1705,7 @@ BOOST_AUTO_TEST_CASE( witness_feeds ) vector active_witnesses; for( const witness_id_type& wit_id : global_props.active_witnesses ) active_witnesses.push_back( wit_id(db).witness_account ); - BOOST_REQUIRE_EQUAL(active_witnesses.size(), 10u); + BOOST_REQUIRE_EQUAL(active_witnesses.size(), INITIAL_WITNESS_COUNT); asset_publish_feed_operation op; op.publisher = active_witnesses[0]; diff --git a/tests/tests/settle_tests.cpp b/tests/tests/settle_tests.cpp index f64719e613..6310493ae5 100644 --- a/tests/tests/settle_tests.cpp +++ b/tests/tests/settle_tests.cpp @@ -1501,4 +1501,122 @@ BOOST_AUTO_TEST_CASE( global_settle_rounding_test_after_hf_184 ) } FC_LOG_AND_RETHROW() } +/** + * Test case to reproduce https://github.com/bitshares/bitshares-core/issues/1883. + * When there is only one fill_order object in the ticker rolling buffer, it should only be rolled out once. + */ +BOOST_AUTO_TEST_CASE( global_settle_ticker_test ) +{ + try { + generate_block(); + + const auto& meta_idx = db.get_index_type>(); + const auto& ticker_idx = db.get_index_type().indices(); + const auto& history_idx = db.get_index_type().indices(); + + BOOST_CHECK_EQUAL( meta_idx.size(), 0 ); + BOOST_CHECK_EQUAL( ticker_idx.size(), 0 ); + BOOST_CHECK_EQUAL( history_idx.size(), 0 ); + + ACTORS((judge)(alice)); + + const auto& pmark = create_prediction_market("PMARK", judge_id); + const auto& core = asset_id_type()(db); + + int64_t init_balance(1000000); + transfer(committee_account, judge_id, asset(init_balance)); + transfer(committee_account, alice_id, asset(init_balance)); + + BOOST_TEST_MESSAGE( "Open position with equal collateral" ); + borrow( alice, pmark.amount(1000), asset(1000) ); + + BOOST_TEST_MESSAGE( "Globally settling" ); + force_global_settle( pmark, pmark.amount(1) / core.amount(1) ); + + generate_block(); + fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread + + { + BOOST_CHECK_EQUAL( meta_idx.size(), 1 ); + BOOST_CHECK_EQUAL( ticker_idx.size(), 1 ); + BOOST_CHECK_EQUAL( history_idx.size(), 1 ); + + const auto& meta = *meta_idx.begin(); + const auto& tick = *ticker_idx.begin(); + const auto& hist = *history_idx.begin(); + + BOOST_CHECK( meta.rolling_min_order_his_id == hist.id ); + BOOST_CHECK( meta.skip_min_order_his_id == false ); + + BOOST_CHECK( tick.base_volume == 1000 ); + BOOST_CHECK( tick.quote_volume == 1000 ); + } + + generate_blocks( db.head_block_time() + 86000 ); // less than a day + fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread + + // nothing changes + { + BOOST_CHECK_EQUAL( meta_idx.size(), 1 ); + BOOST_CHECK_EQUAL( ticker_idx.size(), 1 ); + BOOST_CHECK_EQUAL( history_idx.size(), 1 ); + + const auto& meta = *meta_idx.begin(); + const auto& tick = *ticker_idx.begin(); + const auto& hist = *history_idx.begin(); + + BOOST_CHECK( meta.rolling_min_order_his_id == hist.id ); + BOOST_CHECK( meta.skip_min_order_his_id == false ); + + BOOST_CHECK( tick.base_volume == 1000 ); + BOOST_CHECK( tick.quote_volume == 1000 ); + } + + generate_blocks( db.head_block_time() + 4000 ); // now more than 24 hours + fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread + + // the history is rolled out, new 24h volume should be 0 + { + BOOST_CHECK_EQUAL( meta_idx.size(), 1 ); + BOOST_CHECK_EQUAL( ticker_idx.size(), 1 ); + BOOST_CHECK_EQUAL( history_idx.size(), 1 ); + + const auto& meta = *meta_idx.begin(); + const auto& tick = *ticker_idx.begin(); + const auto& hist = *history_idx.begin(); + + BOOST_CHECK( meta.rolling_min_order_his_id == hist.id ); + BOOST_CHECK( meta.skip_min_order_his_id == true ); // the order should be skipped on next roll + + BOOST_CHECK( tick.base_volume == 0 ); + BOOST_CHECK( tick.quote_volume == 0 ); + } + + generate_block(); + fc::usleep(fc::milliseconds(200)); // sleep a while to execute callback in another thread + + // nothing changes + { + BOOST_CHECK_EQUAL( meta_idx.size(), 1 ); + BOOST_CHECK_EQUAL( ticker_idx.size(), 1 ); + BOOST_CHECK_EQUAL( history_idx.size(), 1 ); + + const auto& meta = *meta_idx.begin(); + const auto& tick = *ticker_idx.begin(); + const auto& hist = *history_idx.begin(); + + BOOST_CHECK( meta.rolling_min_order_his_id == hist.id ); + BOOST_CHECK( meta.skip_min_order_his_id == true ); + + BOOST_CHECK( tick.base_volume == 0 ); + BOOST_CHECK( tick.quote_volume == 0 ); + } + + + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/smartcoin_tests.cpp b/tests/tests/smartcoin_tests.cpp index 9610e456ab..7d69caaa05 100644 --- a/tests/tests/smartcoin_tests.cpp +++ b/tests/tests/smartcoin_tests.cpp @@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE(bsip36) // Check current default witnesses, default chain is configured with 10 witnesses auto witnesses = db.get_global_properties().active_witnesses; - BOOST_CHECK_EQUAL(witnesses.size(), 10u); + BOOST_CHECK_EQUAL(witnesses.size(), INITIAL_WITNESS_COUNT); BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1u); BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2u); BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3u); @@ -142,13 +142,18 @@ BOOST_AUTO_TEST_CASE(bsip36) BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7u); BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8u); BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9u); - BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 10u); // We need to activate 11 witnesses by voting for each of them. // Each witness is voted with incremental stake so last witness created will be the ones with more votes + + // by default we have 9 witnesses, we need to vote for desired witness count (11) to increase them + vote_for_committee_and_witnesses(9, 11); + int c = 0; for (auto l : witness_map) { - int stake = 100 + c + 1; + // voting stake have step of 100 + // so vote_for_committee_and_witnesses() with stake=10 does not affect the expected result + int stake = 100 * (c + 1); transfer(committee_account, l.first, asset(stake)); { account_update_operation op; @@ -206,7 +211,7 @@ BOOST_AUTO_TEST_CASE(bsip36) BOOST_CHECK_EQUAL(itr[1].first.instance.value, 17u); // Activate witness11 with voting stake, will kick the witness with less votes(witness0) out of the active list - transfer(committee_account, witness11_id, asset(121)); + transfer(committee_account, witness11_id, asset(1200)); set_expiration(db, trx); { account_update_operation op; @@ -328,7 +333,7 @@ BOOST_AUTO_TEST_CASE(bsip36) BOOST_CHECK_EQUAL(itr[0].first.instance.value, 17u); // Reactivate witness0 - transfer(committee_account, witness0_id, asset(100)); + transfer(committee_account, witness0_id, asset(1000)); set_expiration(db, trx); { account_update_operation op; diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index df92ccf061..1d61c65bc9 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -37,12 +38,53 @@ using namespace graphene::chain::test; BOOST_FIXTURE_TEST_SUITE(voting_tests, database_fixture) +BOOST_FIXTURE_TEST_CASE( committee_account_initialization_test, database_fixture ) +{ try { + // Check current default committee + // By default chain is configured with INITIAL_COMMITTEE_MEMBER_COUNT=9 members + const auto &committee_members = db.get_global_properties().active_committee_members; + const auto &committee = committee_account(db); + + BOOST_CHECK_EQUAL(committee_members.size(), INITIAL_COMMITTEE_MEMBER_COUNT); + BOOST_CHECK_EQUAL(committee.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT); + + generate_blocks(HARDFORK_533_TIME); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + set_expiration(db, trx); + + // Check that committee not changed after 533 hardfork + // vote counting method changed, but any votes are absent + const auto &committee_members_after_hf533 = db.get_global_properties().active_committee_members; + const auto &committee_after_hf533 = committee_account(db); + BOOST_CHECK_EQUAL(committee_members_after_hf533.size(), INITIAL_COMMITTEE_MEMBER_COUNT); + BOOST_CHECK_EQUAL(committee_after_hf533.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT); + + // You can't use uninitialized committee after 533 hardfork + // when any user with stake created (create_account method automatically set up votes for committee) + // committee is incomplete and consist of random active members + ACTOR(alice); + fund(alice); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + const auto &committee_after_hf533_with_stake = committee_account(db); + BOOST_CHECK_LT(committee_after_hf533_with_stake.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT); + + // Initialize committee by voting for each memeber and for desired count + vote_for_committee_and_witnesses(INITIAL_COMMITTEE_MEMBER_COUNT, INITIAL_WITNESS_COUNT); + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + + const auto &committee_members_after_hf533_and_init = db.get_global_properties().active_committee_members; + const auto &committee_after_hf533_and_init = committee_account(db); + BOOST_CHECK_EQUAL(committee_members_after_hf533_and_init.size(), INITIAL_COMMITTEE_MEMBER_COUNT); + BOOST_CHECK_EQUAL(committee_after_hf533_and_init.active.num_auths(), INITIAL_COMMITTEE_MEMBER_COUNT); + +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE(put_my_witnesses) { try { - graphene::app::database_api db_api1(db); - ACTORS( (witness0) (witness1) (witness2) @@ -90,7 +132,7 @@ BOOST_AUTO_TEST_CASE(put_my_witnesses) const witness_id_type witness12_witness_id = create_witness(witness12_id, witness12_private_key).id; const witness_id_type witness13_witness_id = create_witness(witness13_id, witness13_private_key).id; - // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time + // Create a vector with private key of all witnesses, will be used to activate 9 witnesses at a time const vector private_keys = { witness0_private_key, witness1_private_key, @@ -109,7 +151,7 @@ BOOST_AUTO_TEST_CASE(put_my_witnesses) }; - // create a map with account id and witness id of the first 11 witnesses + // create a map with account id and witness id const flat_map witness_map = { {witness0_id, witness0_witness_id}, {witness1_id, witness1_witness_id}, @@ -127,9 +169,9 @@ BOOST_AUTO_TEST_CASE(put_my_witnesses) {witness13_id, witness13_witness_id} }; - // Check current default witnesses, default chain is configured with 10 witnesses + // Check current default witnesses, default chain is configured with 9 witnesses auto witnesses = db.get_global_properties().active_witnesses; - BOOST_CHECK_EQUAL(witnesses.size(), 10u); + BOOST_CHECK_EQUAL(witnesses.size(), INITIAL_WITNESS_COUNT); BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 1u); BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 2u); BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 3u); @@ -139,7 +181,6 @@ BOOST_AUTO_TEST_CASE(put_my_witnesses) BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 7u); BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 8u); BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 9u); - BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 10u); // Activate all witnesses // Each witness is voted with incremental stake so last witness created will be the ones with more votes @@ -168,18 +209,16 @@ BOOST_AUTO_TEST_CASE(put_my_witnesses) // Check my witnesses are now in control of the system witnesses = db.get_global_properties().active_witnesses; - BOOST_CHECK_EQUAL(witnesses.size(), 11u); - BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 14u); - BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 15u); - BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 16u); - BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 17u); - BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 18u); - BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 19u); - BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 20u); - BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 21u); - BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 22u); - BOOST_CHECK_EQUAL(witnesses.begin()[9].instance.value, 23u); - BOOST_CHECK_EQUAL(witnesses.begin()[10].instance.value, 24u); + BOOST_CHECK_EQUAL(witnesses.size(), INITIAL_WITNESS_COUNT); + BOOST_CHECK_EQUAL(witnesses.begin()[0].instance.value, 16u); + BOOST_CHECK_EQUAL(witnesses.begin()[1].instance.value, 17u); + BOOST_CHECK_EQUAL(witnesses.begin()[2].instance.value, 18u); + BOOST_CHECK_EQUAL(witnesses.begin()[3].instance.value, 19u); + BOOST_CHECK_EQUAL(witnesses.begin()[4].instance.value, 20u); + BOOST_CHECK_EQUAL(witnesses.begin()[5].instance.value, 21u); + BOOST_CHECK_EQUAL(witnesses.begin()[6].instance.value, 22u); + BOOST_CHECK_EQUAL(witnesses.begin()[7].instance.value, 23u); + BOOST_CHECK_EQUAL(witnesses.begin()[8].instance.value, 24u); } FC_LOG_AND_RETHROW() } @@ -218,8 +257,6 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) { try { - graphene::app::database_api db_api1(db); - ACTORS( (committee0) (committee1) (committee2) @@ -267,7 +304,7 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) const committee_member_id_type committee12_committee_id = create_committee_member(committee12_id(db)).id; const committee_member_id_type committee13_committee_id = create_committee_member(committee13_id(db)).id; - // Create a vector with private key of all witnesses, will be used to activate 11 witnesses at a time + // Create a vector with private key of all committee members, will be used to activate 9 members at a time const vector private_keys = { committee0_private_key, committee1_private_key, @@ -285,7 +322,7 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) committee13_private_key }; - // create a map with account id and committee id of the first 11 witnesses + // create a map with account id and committee member id const flat_map committee_map = { {committee0_id, committee0_committee_id}, {committee1_id, committee1_committee_id}, @@ -303,10 +340,10 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) {committee13_id, committee13_committee_id} }; - // Check current default witnesses, default chain is configured with 10 witnesses + // Check current default committee, default chain is configured with 9 committee members auto committee_members = db.get_global_properties().active_committee_members; - BOOST_CHECK_EQUAL(committee_members.size(), 10u); + BOOST_CHECK_EQUAL(committee_members.size(), INITIAL_COMMITTEE_MEMBER_COUNT); BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 0u); BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 1u); BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 2u); @@ -316,10 +353,9 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 6u); BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 7u); BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 8u); - BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 9u); // Activate all committee - // Each witness is voted with incremental stake so last witness created will be the ones with more votes + // Each committee is voted with incremental stake so last member created will be the ones with more votes int c = 0; for (auto committee : committee_map) { int stake = 100 + c + 10; @@ -329,7 +365,10 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) account_update_operation op; op.account = committee.first; op.new_options = committee.first(db).options; + + op.new_options->votes.clear(); op.new_options->votes.insert(committee.second(db).vote_id); + op.new_options->num_committee = 1; trx.operations.push_back(op); sign(trx, private_keys.at(c)); @@ -345,21 +384,21 @@ BOOST_AUTO_TEST_CASE(put_my_committee_members) // Check my witnesses are now in control of the system committee_members = db.get_global_properties().active_committee_members; - BOOST_CHECK_EQUAL(committee_members.size(), 11u); - - /* TODO we are not in full control, seems to committee members have votes by default - BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 14); - BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 15); - BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 16); - BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 17); - BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 18); - BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 19); - BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 20); - BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 21); - BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 22); - BOOST_CHECK_EQUAL(committee_members.begin()[9].instance.value, 23); - BOOST_CHECK_EQUAL(committee_members.begin()[10].instance.value, 24); - */ + std::sort(committee_members.begin(), committee_members.end()); + + BOOST_CHECK_EQUAL(committee_members.size(), INITIAL_COMMITTEE_MEMBER_COUNT); + + // Check my committee members are now in control of the system + BOOST_CHECK_EQUAL(committee_members.begin()[0].instance.value, 15); + BOOST_CHECK_EQUAL(committee_members.begin()[1].instance.value, 16); + BOOST_CHECK_EQUAL(committee_members.begin()[2].instance.value, 17); + BOOST_CHECK_EQUAL(committee_members.begin()[3].instance.value, 18); + BOOST_CHECK_EQUAL(committee_members.begin()[4].instance.value, 19); + BOOST_CHECK_EQUAL(committee_members.begin()[5].instance.value, 20); + BOOST_CHECK_EQUAL(committee_members.begin()[6].instance.value, 21); + BOOST_CHECK_EQUAL(committee_members.begin()[7].instance.value, 22); + BOOST_CHECK_EQUAL(committee_members.begin()[8].instance.value, 23); + } FC_LOG_AND_RETHROW() }