diff --git a/.clang-format b/.clang-format index 2fffe7bae..b52934d07 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,5 @@ --- Language: Cpp -# BasedOnStyle: LLVM AccessModifierOffset: -3 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false @@ -12,7 +11,7 @@ AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false +AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortLambdasOnASingleLine: None @@ -57,6 +56,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 6 ContinuationIndentWidth: 6 Cpp11BracedListStyle: true +DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false @@ -69,12 +69,17 @@ IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 + SortPriority: 0 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 + SortPriority: 0 - Regex: '.*' Priority: 1 + SortPriority: 0 IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' IndentCaseLabels: false +IndentGotoLabels: false IndentPPDirectives: None IndentWidth: 3 IndentWrappedFunctionNames: false @@ -110,18 +115,22 @@ SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false +SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp11 +SpaceBeforeSquareBrackets: false +Standard: Latest StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 3 +UseCRLF: false UseTab: Never ... diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 369dcf8ad..e65c81c8e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ build: - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=Release .. - - make -j4 + - make -j$(nproc) artifacts: untracked: true paths: @@ -49,6 +49,7 @@ test: dependencies: - build script: + - ./build/libraries/fc/tests/all_tests - ./build/tests/betting_test --log_level=message - ./build/tests/chain_test --log_level=message - ./build/tests/cli_test --log_level=message diff --git a/.gitmodules b/.gitmodules index 4d3518d1b..e535465cb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "docs"] - path = docs - url = https://github.com/bitshares/bitshares-core.wiki.git - ignore = dirty + path = docs + url = https://github.com/bitshares/bitshares-core.wiki.git + ignore = dirty [submodule "libraries/fc"] - path = libraries/fc - url = https://github.com/peerplays-network/peerplays-fc.git - branch = latest-fc - ignore = dirty + path = libraries/fc + url = https://gitlab.com/PBSA/tools-libs/peerplays-fc.git + branch = latest-fc + ignore = dirty diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f006aa05..27c298619 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,6 @@ LIST(APPEND BOOST_COMPONENTS thread system filesystem program_options - signals serialization chrono unit_test_framework diff --git a/README.md b/README.md index bc37f0915..391218b77 100644 --- a/README.md +++ b/README.md @@ -2,95 +2,81 @@ Intro for new developers and witnesses ------------------------ This is a quick introduction to get new developers and witnesses up to speed on Peerplays blockchain. It is intended for witnesses plannig to join a live, already deployed blockchain. -# Building on Ubuntu 18.04 LTS and Installation Instructions - The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS: - ``` - sudo apt-get install autoconf bash build-essential ca-certificates cmake \ - doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \ - libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \ - wget -``` -## Build Boost 1.67.0 +# Building and Installation Instructions +Officially supported OS is Ubuntu 20.04. +Following dependencies are needed for a clean install of Ubuntu 20.04: ``` -mkdir $HOME/src -cd $HOME/src -export BOOST_ROOT=$HOME/src/boost_1_67_0 -sudo apt-get update -sudo apt-get install -y autotools-dev build-essential libbz2-dev libicu-dev python-dev -wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download'\ - -O boost_1_67_0.tar.bz2 -tar xjf boost_1_67_0.tar.bz2 -cd boost_1_67_0/ -./bootstrap.sh "--prefix=$BOOST_ROOT" -./b2 install +sudo apt-get install \ + apt-utils autoconf bash build-essential ca-certificates clang-format cmake + dnsutils doxygen expect git graphviz libboost-all-dev libbz2-dev \ + libcurl4-openssl-dev libncurses-dev libreadline-dev libsnappy-dev \ + libssl-dev libtool libzip-dev libzmq3-dev locales mc nano net-tools ntp \ + openssh-server pkg-config perl python3 python3-jinja2 sudo wget ``` ## Building Peerplays ``` +mkdir $HOME/src cd $HOME/src -export BOOST_ROOT=$HOME/src/boost_1_67_0 -git clone https://github.com/peerplays-network/peerplays.git +git clone https://gitlab.com/PBSA/peerplays.git cd peerplays git submodule update --init --recursive + # If you want to build Mainnet node -cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release +cmake -DCMAKE_BUILD_TYPE=Release + # If you want to build Testnet node -cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 +cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_PEERPLAYS_TESTNET=1 + +# Update -j flag depending on your current system specs; +# Recommended 4GB of RAM per 1 CPU core +# make -j2 for 8GB RAM +# make -j4 for 16GB RAM +# make -j8 for 32GB RAM make -j$(nproc) make install # this can install the executable files under /usr/local ``` -## Docker image +## Docker images +Install docker, and add current user to docker group. ``` -# Install docker sudo apt install docker.io - - -# Add current user to docker group sudo usermod -a -G docker $USER + # You need to restart your shell session, to apply group membership # Type 'groups' to verify that you are a member of a docker group +``` +### Official docker image for Peerplas Mainnet +``` +docker pull datasecuritynode/peerplays:latest +``` + +### Building docker image manually +``` # Build docker image (from the project root, must be a docker group member) docker build -t peerplays . +``` - -# Start docker image +### Start docker image +``` docker start peerplays - -# Exposed ports -# # rpc service: -# EXPOSE 8090 -# # p2p service: -# EXPOSE 1776 ``` - Rest of the instructions on starting the chain remains same. +Rest of the instructions on starting the chain remains same. Starting A Peerplays Node ----------------- - -For Ubuntu 14.04 LTS and up users, see -[this](https://github.com/cryptonomex/graphene/wiki/build-ubuntu) and -then proceed with: - - git clone https://github.com/peerplays-network/peerplays.git - cd peerplays - git submodule update --init --recursive - cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Release . - make - ./programs/witness_node/witness_node - Launching the witness creates required directories. Next, **stop the witness** and continue. $ vi witness_node_data_dir/config.ini diff --git a/clang-format.sh b/clang-format.sh index 3fbbf0552..7f4482859 100755 --- a/clang-format.sh +++ b/clang-format.sh @@ -3,3 +3,4 @@ find ./libraries/app -regex ".*[c|h]pp" | xargs clang-format -i find ./libraries/chain/hardfork.d -regex ".*hf" | xargs clang-format -i find ./libraries/plugins/peerplays_sidechain -regex ".*[c|h]pp" | xargs clang-format -i +find ./programs/cli_wallet -regex ".*[c|h]pp" | xargs clang-format -i diff --git a/docs b/docs index 8df8f6638..1e924950c 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 8df8f66389853df73ab8f6dd73981be2a6957df8 +Subproject commit 1e924950c2f92b166c34ceb294e8b8c4997a6c4e diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 12a6616b9..fe92face1 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -15,7 +15,7 @@ add_library( graphene_app #target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness ) target_link_libraries( graphene_app PUBLIC graphene_net graphene_utilities - graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_es_objects graphene_generate_genesis graphene_market_history ) + graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_es_objects graphene_generate_genesis graphene_market_history peerplays_sidechain ) target_include_directories( graphene_app PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index eac6d3b8d..f6b084f12 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -106,6 +106,10 @@ void login_api::enable_api(const std::string &api_name) { // can only enable this API if the plugin was loaded if (_app.get_plugin("affiliate_stats")) _affiliate_stats_api = std::make_shared(std::ref(_app)); + } else if (api_name == "sidechain_api") { + // can only enable this API if the plugin was loaded + if (_app.get_plugin("peerplays_sidechain")) + _sidechain_api = std::make_shared(std::ref(_app)); } return; } @@ -310,6 +314,11 @@ fc::api login_api::affiliate_sta return *_affiliate_stats_api; } +fc::api login_api::sidechain() const { + FC_ASSERT(_sidechain_api); + return *_sidechain_api; +} + vector history_api::get_fill_order_history(std::string asset_a, std::string asset_b, uint32_t limit) const { FC_ASSERT(_app.chain_database()); const auto &db = *_app.chain_database(); diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 1ecdd8f7d..19f1ed103 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -401,6 +401,7 @@ class application_impl : public net::node_delegate { wild_access.allowed_apis.push_back("crypto_api"); wild_access.allowed_apis.push_back("bookie_api"); wild_access.allowed_apis.push_back("affiliate_stats_api"); + wild_access.allowed_apis.push_back("sidechain_api"); _apiaccess.permission_map["*"] = wild_access; } diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 775822223..a9ea97740 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -52,6 +52,22 @@ template class fc::api; namespace graphene { namespace app { +template +optional maybe_id(const string &name_or_id) { + if (std::isdigit(name_or_id.front())) { + try { + return fc::variant(name_or_id, 1).as(1); + } catch (const fc::exception &) { // not an ID + } + } + return optional(); +} + +std::string object_id_to_string(object_id_type id) { + std::string object_id = fc::to_string(id.space()) + "." + fc::to_string(id.type()) + "." + fc::to_string(id.instance()); + return object_id; +} + class database_api_impl : public std::enable_shared_from_this { public: database_api_impl(graphene::chain::database &db); @@ -146,18 +162,22 @@ class database_api_impl : public std::enable_shared_from_this // Witnesses vector> get_witnesses(const vector &witness_ids) const; + fc::optional get_witness_by_account_id(account_id_type account) const; fc::optional get_witness_by_account(const std::string account_id_or_name) const; map lookup_witness_accounts(const string &lower_bound_name, uint32_t limit) const; uint64_t get_witness_count() const; // Committee members vector> get_committee_members(const vector &committee_member_ids) const; + fc::optional get_committee_member_by_account_id(account_id_type account) const; fc::optional get_committee_member_by_account(const std::string account_id_or_name) const; map lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const; + uint64_t get_committee_member_count() const; // SON members vector> get_sons(const vector &son_ids) const; - fc::optional get_son_by_account(account_id_type account) const; + fc::optional get_son_by_account_id(account_id_type account) const; + fc::optional get_son_by_account(const std::string account_id_or_name) const; map lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const; uint64_t get_son_count() const; @@ -173,8 +193,32 @@ class database_api_impl : public std::enable_shared_from_this fc::optional get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain) const; uint64_t get_sidechain_addresses_count() const; + // Workers + vector> get_workers(const vector &witness_ids) const; + vector get_workers_by_account_id(account_id_type account) const; + vector get_workers_by_account(const std::string account_id_or_name) const; + map lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const; + uint64_t get_worker_count() const; + // Votes vector lookup_vote_ids(const vector &votes) const; + vector get_votes_ids(const string &account_name_or_id) const; + template + vector get_votes_objects(const vector &votes, unsigned int variant_max_depth = 1) const { + static_assert(std::is_base_of::value, "Type must be an index type"); + + vector result; + const auto &idx = _db.get_index_type().indices().template get(); + for (auto id : votes) { + auto itr = idx.find(id); + if (itr != idx.end()) + result.emplace_back(variant(*itr, variant_max_depth)); + } + return result; + } + votes_info get_votes(const string &account_name_or_id) const; + vector get_voters_by_id(const vote_id_type &vote_id) const; + voters_info get_voters(const string &account_name_or_id) const; // Authority / validation std::string get_transaction_hex(const signed_transaction &trx) const; @@ -234,6 +278,9 @@ class database_api_impl : public std::enable_shared_from_this vector get_offer_history_by_item(const offer_history_id_type lower_id, const nft_id_type item, uint32_t limit) const; vector get_offer_history_by_bidder(const offer_history_id_type lower_id, const account_id_type bidder_account_id, uint32_t limit) const; + // Account Role + vector get_account_roles_by_owner(account_id_type owner) const; + uint32_t api_limit_get_lower_bound_symbol = 100; uint32_t api_limit_get_limit_orders = 300; uint32_t api_limit_get_limit_orders_by_account = 101; @@ -242,12 +289,11 @@ class database_api_impl : public std::enable_shared_from_this uint32_t api_limit_lookup_accounts = 1000; uint32_t api_limit_lookup_witness_accounts = 1000; uint32_t api_limit_lookup_committee_member_accounts = 1000; + uint32_t api_limit_lookup_son_accounts = 1000; + uint32_t api_limit_lookup_worker_accounts = 1000; uint32_t api_limit_get_trade_history = 100; uint32_t api_limit_get_trade_history_by_sequence = 100; - // Account Role - vector get_account_roles_by_owner(account_id_type owner) const; - //private: const account_object *get_account_from_string(const std::string &name_or_id, bool throw_if_not_found = true) const; @@ -1552,20 +1598,6 @@ vector> database_api::get_witnesses(const vectorget_witnesses(witness_ids); } -vector database_api::get_workers_by_account(const std::string account_id_or_name) const { - const auto &idx = my->_db.get_index_type().indices().get(); - const account_id_type account = my->get_account_from_string(account_id_or_name)->id; - auto itr = idx.find(account); - vector result; - - if (itr != idx.end() && itr->worker_account == account) { - result.emplace_back(*itr); - ++itr; - } - - return result; -} - vector> database_api_impl::get_witnesses(const vector &witness_ids) const { vector> result; result.reserve(witness_ids.size()); @@ -1578,19 +1610,27 @@ vector> database_api_impl::get_witnesses(const vector database_api::get_witness_by_account(const std::string account_id_or_name) const { - return my->get_witness_by_account(account_id_or_name); +fc::optional database_api::get_witness_by_account_id(account_id_type account) const { + return my->get_witness_by_account_id(account); } -fc::optional database_api_impl::get_witness_by_account(const std::string account_id_or_name) const { +fc::optional database_api_impl::get_witness_by_account_id(account_id_type account) const { const auto &idx = _db.get_index_type().indices().get(); - const account_id_type account = get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); if (itr != idx.end()) return *itr; return {}; } +fc::optional database_api::get_witness_by_account(const std::string account_id_or_name) const { + return my->get_witness_by_account(account_id_or_name); +} + +fc::optional database_api_impl::get_witness_by_account(const std::string account_id_or_name) const { + const account_id_type account = get_account_from_string(account_id_or_name)->id; + return get_witness_by_account_id(account); +} + map database_api::lookup_witness_accounts(const string &lower_bound_name, uint32_t limit) const { return my->lookup_witness_accounts(lower_bound_name, limit); } @@ -1649,19 +1689,27 @@ vector> database_api_impl::get_committee_membe return result; } -fc::optional database_api::get_committee_member_by_account(const std::string account_id_or_name) const { - return my->get_committee_member_by_account(account_id_or_name); +fc::optional database_api::get_committee_member_by_account_id(account_id_type account) const { + return my->get_committee_member_by_account_id(account); } -fc::optional database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const { +fc::optional database_api_impl::get_committee_member_by_account_id(account_id_type account) const { const auto &idx = _db.get_index_type().indices().get(); - const account_id_type account = get_account_from_string(account_id_or_name)->id; auto itr = idx.find(account); if (itr != idx.end()) return *itr; return {}; } +fc::optional database_api::get_committee_member_by_account(const std::string account_id_or_name) const { + return my->get_committee_member_by_account(account_id_or_name); +} + +fc::optional database_api_impl::get_committee_member_by_account(const std::string account_id_or_name) const { + const account_id_type account = get_account_from_string(account_id_or_name)->id; + return get_committee_member_by_account_id(account); +} + map database_api::lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const { return my->lookup_committee_member_accounts(lower_bound_name, limit); } @@ -1690,6 +1738,14 @@ map database_api_impl::lookup_committee_member return committee_members_by_account_name; } +uint64_t database_api::get_committee_member_count() const { + return my->get_committee_member_count(); +} + +uint64_t database_api_impl::get_committee_member_count() const { + return _db.get_index_type().indices().size(); +} + ////////////////////////////////////////////////////////////////////// // // // SON members // @@ -1712,11 +1768,11 @@ vector> database_api_impl::get_sons(const vector database_api::get_son_by_account(account_id_type account) const { - return my->get_son_by_account(account); +fc::optional database_api::get_son_by_account_id(account_id_type account) const { + return my->get_son_by_account_id(account); } -fc::optional database_api_impl::get_son_by_account(account_id_type account) const { +fc::optional database_api_impl::get_son_by_account_id(account_id_type account) const { const auto &idx = _db.get_index_type().indices().get(); auto itr = idx.find(account); if (itr != idx.end()) @@ -1724,12 +1780,24 @@ fc::optional database_api_impl::get_son_by_account(account_id_type a return {}; } +fc::optional database_api::get_son_by_account(const std::string account_id_or_name) const { + return my->get_son_by_account(account_id_or_name); +} + +fc::optional database_api_impl::get_son_by_account(const std::string account_id_or_name) const { + const account_id_type account = get_account_from_string(account_id_or_name)->id; + return get_son_by_account_id(account); +} + map database_api::lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const { return my->lookup_son_accounts(lower_bound_name, limit); } map database_api_impl::lookup_son_accounts(const string &lower_bound_name, uint32_t limit) const { - FC_ASSERT(limit <= 1000); + FC_ASSERT(limit <= api_limit_lookup_son_accounts, + "Number of querying accounts can not be greater than ${configured_limit}", + ("configured_limit", api_limit_lookup_son_accounts)); + const auto &sons_by_id = _db.get_index_type().indices().get(); // we want to order sons by account name, but that name is in the account object @@ -1875,6 +1943,91 @@ uint64_t database_api_impl::get_sidechain_addresses_count() const { return _db.get_index_type().indices().size(); } +////////////////////////////////////////////////////////////////////// +// // +// Workers // +// // +////////////////////////////////////////////////////////////////////// + +vector> database_api::get_workers(const vector &worker_ids) const { + return my->get_workers(worker_ids); +} + +vector database_api::get_workers_by_account_id(account_id_type account) const { + return my->get_workers_by_account_id(account); +} + +vector database_api::get_workers_by_account(const std::string account_id_or_name) const { + return my->get_workers_by_account(account_id_or_name); +} + +map database_api::lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const { + return my->lookup_worker_accounts(lower_bound_name, limit); +} + +uint64_t database_api::get_worker_count() const { + return my->get_worker_count(); +} + +vector> database_api_impl::get_workers(const vector &worker_ids) const { + vector> result; + result.reserve(worker_ids.size()); + std::transform(worker_ids.begin(), worker_ids.end(), std::back_inserter(result), + [this](worker_id_type id) -> optional { + if (auto o = _db.find(id)) + return *o; + return {}; + }); + return result; +} + +vector database_api_impl::get_workers_by_account_id(account_id_type account) const { + const auto &idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + vector result; + + if (itr != idx.end() && itr->worker_account == account) { + result.emplace_back(*itr); + ++itr; + } + + return result; +} + +vector database_api_impl::get_workers_by_account(const std::string account_id_or_name) const { + const account_id_type account = get_account_from_string(account_id_or_name)->id; + return get_workers_by_account_id(account); +} + +map database_api_impl::lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const { + FC_ASSERT(limit <= api_limit_lookup_worker_accounts, + "Number of querying accounts can not be greater than ${configured_limit}", + ("configured_limit", api_limit_lookup_worker_accounts)); + + const auto &workers_by_id = _db.get_index_type().indices().get(); + + // we want to order workers by account name, but that name is in the account object + // so the worker_index doesn't have a quick way to access it. + // get all the names and look them all up, sort them, then figure out what + // records to return. This could be optimized, but we expect the + // number of witnesses to be few and the frequency of calls to be rare + std::map workers_by_account_name; + for (const worker_object &worker : workers_by_id) + if (auto account_iter = _db.find(worker.worker_account)) + if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name + workers_by_account_name.insert(std::make_pair(account_iter->name, worker.id)); + + auto end_iter = workers_by_account_name.begin(); + while (end_iter != workers_by_account_name.end() && limit--) + ++end_iter; + workers_by_account_name.erase(end_iter, workers_by_account_name.end()); + return workers_by_account_name; +} + +uint64_t database_api_impl::get_worker_count() const { + return _db.get_index_type().indices().size(); +} + ////////////////////////////////////////////////////////////////////// // // // Votes // @@ -1885,6 +2038,22 @@ vector database_api::lookup_vote_ids(const vector &votes) return my->lookup_vote_ids(votes); } +vector database_api::get_votes_ids(const string &account_name_or_id) const { + return my->get_votes_ids(account_name_or_id); +} + +votes_info database_api::get_votes(const string &account_name_or_id) const { + return my->get_votes(account_name_or_id); +} + +vector database_api::get_voters_by_id(const vote_id_type &vote_id) const { + return my->get_voters_by_id(vote_id); +} + +voters_info database_api::get_voters(const string &account_name_or_id) const { + return my->get_voters(account_name_or_id); +} + 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"); @@ -1946,6 +2115,261 @@ vector database_api_impl::lookup_vote_ids(const vector &v return result; } +vector database_api_impl::get_votes_ids(const string &account_name_or_id) const { + vector result; + const account_object *account = get_account_from_string(account_name_or_id); + + //! Iterate throug votes and fill vector + for (const auto &vote : account->options.votes) { + result.emplace_back(vote); + } + + return result; +} + +votes_info database_api_impl::get_votes(const string &account_name_or_id) const { + votes_info result; + + const auto &votes_ids = get_votes_ids(account_name_or_id); + const auto &committee_ids = get_votes_objects(votes_ids); + const auto &witness_ids = get_votes_objects(votes_ids); + const auto &for_worker_ids = get_votes_objects(votes_ids); + const auto &against_worker_ids = get_votes_objects(votes_ids); + const auto &son_ids = get_votes_objects(votes_ids, 5); + + //! Fill votes info + if (!committee_ids.empty()) { + vector votes_for_committee_members; + votes_for_committee_members.reserve(committee_ids.size()); + for (const auto &committee : committee_ids) { + const auto &committee_obj = committee.as(2); + votes_for_committee_members.emplace_back(votes_info_object{committee_obj.vote_id, committee_obj.id}); + } + result.votes_for_committee_members = std::move(votes_for_committee_members); + } + + if (!witness_ids.empty()) { + vector votes_for_witnesses; + votes_for_witnesses.reserve(witness_ids.size()); + for (const auto &witness : witness_ids) { + const auto &witness_obj = witness.as(2); + votes_for_witnesses.emplace_back(votes_info_object{witness_obj.vote_id, witness_obj.id}); + } + result.votes_for_witnesses = std::move(votes_for_witnesses); + } + + if (!for_worker_ids.empty()) { + vector votes_for_workers; + votes_for_workers.reserve(for_worker_ids.size()); + for (const auto &for_worker : for_worker_ids) { + const auto &for_worker_obj = for_worker.as(2); + votes_for_workers.emplace_back(votes_info_object{for_worker_obj.vote_for, for_worker_obj.id}); + } + result.votes_for_workers = std::move(votes_for_workers); + } + + if (!against_worker_ids.empty()) { + vector votes_against_workers; + votes_against_workers.reserve(against_worker_ids.size()); + for (const auto &against_worker : against_worker_ids) { + const auto &against_worker_obj = against_worker.as(2); + votes_against_workers.emplace_back(votes_info_object{against_worker_obj.vote_against, against_worker_obj.id}); + } + result.votes_against_workers = std::move(votes_against_workers); + } + + if (!son_ids.empty()) { + vector votes_for_sons; + votes_for_sons.reserve(son_ids.size()); + for (const auto &son : son_ids) { + const auto &son_obj = son.as(6); + votes_for_sons.emplace_back(votes_info_object{son_obj.vote_id, son_obj.id}); + } + result.votes_for_sons = std::move(votes_for_sons); + } + + return result; +} + +vector database_api_impl::get_voters_by_id(const vote_id_type &vote_id) const { + vector result; + + //! We search all accounts that have voted for this vote_id + const auto &account_index = _db.get_index_type().indices().get(); + for (const auto &account : account_index) { + if (account.options.votes.count(vote_id) != 0) + result.emplace_back(account); + } + + return result; +} + +voters_info database_api_impl::get_voters(const string &account_name_or_id) const { + voters_info result; + + //! Find account name + bool owner_account_found = false; + std::string owner_account_id; + + //! Check if we have account by name + const auto &account_object = get_account_by_name(account_name_or_id); + if (account_object) { + //! It is account + owner_account_id = object_id_to_string(account_object->get_id()); + owner_account_found = true; + } else { + //! Check if we have account id + const auto &account_id = maybe_id(account_name_or_id); + if (account_id) { + //! It may be account id + const auto &account_objects = get_accounts({account_name_or_id}); + if (!account_objects.empty()) { + const auto &account_object = account_objects.front(); + if (account_object) { + //! It is account object + owner_account_id = object_id_to_string(account_object->get_id()); + owner_account_found = true; + } + } + } else { + //! Check if we have committee member id + const auto &committee_member_id = maybe_id(account_name_or_id); + if (committee_member_id) { + //! It may be committee member id + const auto &committee_member_objects = get_committee_members({*committee_member_id}); + if (!committee_member_objects.empty()) { + const auto &committee_member_object = committee_member_objects.front(); + if (committee_member_object) { + //! It is committee member object + owner_account_id = object_id_to_string(committee_member_object->committee_member_account); + owner_account_found = true; + } + } + } else { + //! Check if we have witness id + const auto &witness_id = maybe_id(account_name_or_id); + if (witness_id) { + //! It may be witness id + const auto &witness_objects = get_witnesses({*witness_id}); + if (!witness_objects.empty()) { + const auto &witness_object = witness_objects.front(); + if (witness_object) { + //! It is witness object + owner_account_id = object_id_to_string(witness_object->witness_account); + owner_account_found = true; + } + } + } else { + //! Check if we have worker id + const auto &worker_id = maybe_id(account_name_or_id); + if (worker_id) { + //! It may be worker id + const auto &worker_objects = get_workers({*worker_id}); + if (!worker_objects.empty()) { + const auto &worker_object = worker_objects.front(); + if (worker_object) { + //! It is worker object + owner_account_id = object_id_to_string(worker_object->worker_account); + owner_account_found = true; + } + } + } else { + //! Check if we have son id + const auto &son_id = maybe_id(account_name_or_id); + if (son_id) { + //! It may be son id + const auto &son_objects = get_sons({*son_id}); + if (!son_objects.empty()) { + const auto &son_object = son_objects.front(); + if (son_object) { + //! It is son object + owner_account_id = object_id_to_string(son_object->son_account); + owner_account_found = true; + } + } + } + } + } + } + } + } + + //! We didn't find who it was + if (!owner_account_found) + FC_THROW_EXCEPTION(database_query_exception, "Wrong account_name_or_id: ${account_name_or_id}", ("account_name_or_id", account_name_or_id)); + + //! Fill voters_info + const auto &committee_member_object = get_committee_member_by_account(owner_account_id); + const auto &witness_object = get_witness_by_account(owner_account_id); + const auto &worker_objects = get_workers_by_account(owner_account_id); + const auto &son_object = get_son_by_account(owner_account_id); + + //! Info for committee member voters + if (committee_member_object) { + const auto &committee_member_voters = get_voters_by_id(committee_member_object->vote_id); + voters_info_object voters_for_committee_member; + voters_for_committee_member.vote_id = committee_member_object->vote_id; + voters_for_committee_member.voters.reserve(committee_member_voters.size()); + for (const auto &voter : committee_member_voters) { + voters_for_committee_member.voters.emplace_back(voter.get_id()); + } + result.voters_for_committee_member = std::move(voters_for_committee_member); + } + + //! Info for witness voters + if (witness_object) { + const auto &witness_voters = get_voters_by_id(witness_object->vote_id); + voters_info_object voters_for_witness; + voters_for_witness.vote_id = witness_object->vote_id; + voters_for_witness.voters.reserve(witness_voters.size()); + for (const auto &voter : witness_voters) { + voters_for_witness.voters.emplace_back(voter.get_id()); + } + result.voters_for_witness = std::move(voters_for_witness); + } + + //! Info for worker voters + if (!worker_objects.empty()) { + vector voters_for_workers(worker_objects.size()); + vector voters_against_workers(worker_objects.size()); + for (const auto &worker_object : worker_objects) { + voters_info_object voters_for_worker; + const auto &for_worker_voters = get_voters_by_id(worker_object.vote_for); + voters_for_worker.vote_id = worker_object.vote_for; + voters_for_worker.voters.reserve(for_worker_voters.size()); + for (const auto &voter : for_worker_voters) { + voters_for_worker.voters.emplace_back(voter.get_id()); + } + voters_for_workers.emplace_back(std::move(voters_for_worker)); + + voters_info_object voters_against_worker; + const auto &against_worker_voters = get_voters_by_id(worker_object.vote_against); + voters_against_worker.vote_id = worker_object.vote_against; + voters_against_worker.voters.reserve(against_worker_voters.size()); + for (const auto &voter : against_worker_voters) { + voters_against_worker.voters.emplace_back(voter.get_id()); + } + voters_against_workers.emplace_back(std::move(voters_against_worker)); + } + result.voters_for_workers = std::move(voters_for_workers); + result.voters_against_workers = std::move(voters_against_workers); + } + + //! Info for son voters + if (son_object) { + const auto &son_voters = get_voters_by_id(son_object->vote_id); + voters_info_object voters_for_son; + voters_for_son.vote_id = son_object->vote_id; + voters_for_son.voters.reserve(son_voters.size()); + for (const auto &voter : son_voters) { + voters_for_son.voters.emplace_back(voter.get_id()); + } + result.voters_for_son = std::move(voters_for_son); + } + + return result; +} + ////////////////////////////////////////////////////////////////////// // // // Authority / validation // diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index c12d45bf2..98be16a6f 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -28,16 +28,15 @@ #include #include -#include -#include - -#include +#include +#include #include #include #include - -#include +#include +#include +#include #include #include @@ -405,6 +404,8 @@ class login_api { fc::api bookie() const; /// @brief Retrieve the affiliate_stats API (if available) fc::api affiliate_stats() const; + /// @brief Retrieve the sidechain_api API (if available) + fc::api sidechain() const; /// @brief Called to enable an API, not reflected. void enable_api(const string &api_name); @@ -421,6 +422,7 @@ class login_api { optional> _debug_api; optional> _bookie_api; optional> _affiliate_stats_api; + optional> _sidechain_api; }; }} // namespace graphene::app @@ -498,6 +500,7 @@ FC_API(graphene::app::login_api, (asset) (debug) (bookie) - (affiliate_stats)) + (affiliate_stats) + (sidechain)) // clang-format on diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 695eae7aa..93a2b88cc 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include @@ -558,6 +560,13 @@ class database_api { * @param account The ID of the account whose witness should be retrieved * @return The witness object, or null if the account does not have a witness */ + fc::optional get_witness_by_account_id(account_id_type account) const; + + /** + * @brief Get the witness owned by a given account + * @param account_id_or_name The ID or name 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_name_or_id) const; /** @@ -586,6 +595,13 @@ class database_api { */ vector> get_committee_members(const vector &committee_member_ids) const; + /** + * @brief Get the committee_member owned by a given account + * @param account The ID of the account whose committee_member should be retrieved + * @return The committee_member object, or null if the account does not have a committee_member + */ + fc::optional get_committee_member_by_account_id(account_id_type account) const; + /** * @brief Get the committee_member owned by a given account * @param account_id_or_name The ID or name of the account whose committee_member should be retrieved @@ -601,6 +617,11 @@ class database_api { */ map lookup_committee_member_accounts(const string &lower_bound_name, uint32_t limit) const; + /** + * @brief Get the total number of committee_members registered with the blockchain + */ + uint64_t get_committee_member_count() const; + ///////////////// // SON members // ///////////////// @@ -619,7 +640,14 @@ class database_api { * @param account The ID of the account whose SON should be retrieved * @return The SON object, or null if the account does not have a SON */ - fc::optional get_son_by_account(account_id_type account) const; + fc::optional get_son_by_account_id(account_id_type account) const; + + /** + * @brief Get the SON owned by a given account + * @param account_id_or_name The ID of the account whose SON should be retrieved + * @return The SON object, or null if the account does not have a SON + */ + fc::optional get_son_by_account(const std::string account_id_or_name) const; /** * @brief Get names and IDs for registered SONs @@ -698,15 +726,46 @@ class database_api { */ uint64_t get_sidechain_addresses_count() const; - /// WORKERS + ///////////// + // Workers // + ///////////// + + /** + * @brief Get a list of workers by ID + * @param worker_ids IDs of the workers to retrieve + * @return The workers corresponding to the provided IDs + * + * This function has semantics identical to @ref get_objects + */ + vector> get_workers(const vector &worker_ids) const; + + /** + * @brief Return the worker objects associated with this account. + * @param account The ID of the account whose workers should be retrieved + * @return The worker object or null if the account does not have a worker + */ + vector get_workers_by_account_id(account_id_type account) const; /** * @brief Return the worker objects associated with this account. - * @param account_id_or_name The ID or name of the account whose worker should be retrieved + * @param account_id_or_name The ID or name of the account whose workers should be retrieved * @return The worker object or null if the account does not have a worker */ vector get_workers_by_account(const std::string account_id_or_name) const; + /** + * @brief Get names and IDs for registered workers + * @param lower_bound_name Lower bound of the first name to return + * @param limit Maximum number of results to return -- must not exceed 1000 + * @return Map of worker names to corresponding IDs + */ + map lookup_worker_accounts(const string &lower_bound_name, uint32_t limit) const; + + /** + * @brief Get the total number of workers registered with the blockchain + */ + uint64_t get_worker_count() const; + /////////// // Votes // /////////// @@ -721,6 +780,39 @@ class database_api { */ vector lookup_vote_ids(const vector &votes) const; + /** + * @brief Get a list of vote_id_type that ID votes for + * @param account_name_or_id ID or name of the account to get votes for + * @return The list of vote_id_type ID votes for + * + */ + vector get_votes_ids(const string &account_name_or_id) const; + + /** + * @brief Return the objects account_name_or_id votes for + * @param account_name_or_id ID or name of the account to get votes for + * @return The votes_info account_name_or_id votes for + * + */ + votes_info get_votes(const string &account_name_or_id) const; + + /** + * + * @brief Get a list of accounts that votes for vote_id + * @param vote_id We search accounts that vote for this ID + * @return The accounts that votes for provided ID + * + */ + vector get_voters_by_id(const vote_id_type &vote_id) const; + + /** + * @brief Return the accounts that votes for account_name_or_id + * @param account_name_or_id ID or name of the account to get voters for + * @return The voters_info for account_name_or_id + * + */ + voters_info get_voters(const string &account_name_or_id) const; + //////////////////////////// // Authority / validation // //////////////////////////// @@ -1033,17 +1125,21 @@ FC_API(graphene::app::database_api, // Witnesses (get_witnesses) + (get_witness_by_account_id) (get_witness_by_account) (lookup_witness_accounts) (get_witness_count) // Committee members (get_committee_members) + (get_committee_member_by_account_id) (get_committee_member_by_account) (lookup_committee_member_accounts) + (get_committee_member_count) // SON members (get_sons) + (get_son_by_account_id) (get_son_by_account) (lookup_son_accounts) (get_son_count) @@ -1060,10 +1156,19 @@ FC_API(graphene::app::database_api, (get_sidechain_address_by_account_and_sidechain) (get_sidechain_addresses_count) - // workers + // Workers + (get_workers) + (get_workers_by_account_id) (get_workers_by_account) + (lookup_worker_accounts) + (get_worker_count) + // Votes (lookup_vote_ids) + (get_votes_ids) + (get_votes) + (get_voters_by_id) + (get_voters) // Authority / validation (get_transaction_hex) diff --git a/libraries/chain/betting_market_group_object.cpp b/libraries/chain/betting_market_group_object.cpp index 2ac7d7a4a..778c756ed 100644 --- a/libraries/chain/betting_market_group_object.cpp +++ b/libraries/chain/betting_market_group_object.cpp @@ -541,7 +541,7 @@ void betting_market_group_object::dispatch_new_status(database& db, betting_mark } } // graphene::chain -namespace fc { +namespace fc { // Manually reflect betting_market_group_object to variant to properly reflect "state" void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v, uint32_t max_depth) { diff --git a/libraries/chain/betting_market_object.cpp b/libraries/chain/betting_market_object.cpp index d5efd56c6..bd9408623 100644 --- a/libraries/chain/betting_market_object.cpp +++ b/libraries/chain/betting_market_object.cpp @@ -466,7 +466,7 @@ void betting_market_object::on_canceled_event(database& db) } } // graphene::chain -namespace fc { +namespace fc { // Manually reflect betting_market_object to variant to properly reflect "state" void to_variant(const graphene::chain::betting_market_object& event_obj, fc::variant& v, uint32_t max_depth) { @@ -493,4 +493,3 @@ namespace fc { const_cast(event_obj.my->state_machine.current_state())[0] = (int)status; } } //end namespace fc - diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 3f68d5981..56ac6fd05 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -2034,18 +2034,11 @@ void database::perform_son_tasks() void update_son_params(database& db) { - if( db.head_block_time() >= HARDFORK_SON2_TIME ) + if( (db.head_block_time() >= HARDFORK_SON2_TIME) && (db.head_block_time() < HARDFORK_SON3_TIME) ) { const auto& gpo = db.get_global_properties(); - const asset_object& btc_asset = gpo.parameters.btc_asset()(db); - if( btc_asset.is_transfer_restricted() ) { - db.modify( btc_asset, []( asset_object& ao ) { - ao.options.flags = asset_issuer_permission_flags::charge_market_fee | - asset_issuer_permission_flags::override_authority; - }); - } db.modify( gpo, []( global_property_object& gpo ) { - gpo.parameters.extensions.value.maximum_son_count = 7; + gpo.parameters.extensions.value.maximum_son_count = 7; }); } } @@ -2274,6 +2267,14 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; if( !p.pending_parameters->extensions.value.hive_asset.valid() ) p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; + + // the following parameters are not allowed to be changed. So take what is in global property + p.pending_parameters->extensions.value.hive_asset = p.parameters.extensions.value.hive_asset; + p.pending_parameters->extensions.value.hbd_asset = p.parameters.extensions.value.hbd_asset; + p.pending_parameters->extensions.value.btc_asset = p.parameters.extensions.value.btc_asset; + p.pending_parameters->extensions.value.son_account = p.parameters.extensions.value.son_account; + p.pending_parameters->extensions.value.gpos_period_start = p.parameters.extensions.value.gpos_period_start; + p.parameters = std::move(*p.pending_parameters); p.pending_parameters.reset(); } diff --git a/libraries/chain/event_object.cpp b/libraries/chain/event_object.cpp index 4c2fce140..2040ad4b2 100644 --- a/libraries/chain/event_object.cpp +++ b/libraries/chain/event_object.cpp @@ -47,7 +47,7 @@ namespace graphene { namespace chain { }; } } -FC_REFLECT_ENUM(graphene::chain::event_state, +FC_REFLECT_ENUM(graphene::chain::event_state, (upcoming) (frozen_upcoming) (in_progress) @@ -61,12 +61,12 @@ namespace graphene { namespace chain { namespace msm = boost::msm; namespace mpl = boost::mpl; - namespace + namespace { // Events -- most events happen when the witnesses publish an event_update operation with a new // status, so if they publish an event with the status set to `frozen`, we'll generate a `frozen_event` - struct upcoming_event + struct upcoming_event { database& db; upcoming_event(database& db) : db(db) {} @@ -76,12 +76,12 @@ namespace graphene { namespace chain { database& db; in_progress_event(database& db) : db(db) {} }; - struct frozen_event + struct frozen_event { database& db; frozen_event(database& db) : db(db) {} }; - struct finished_event + struct finished_event { database& db; finished_event(database& db) : db(db) {} @@ -104,7 +104,7 @@ namespace graphene { namespace chain { betting_market_group_resolved_event(database& db, betting_market_group_id_type resolved_group, bool was_canceled) : db(db), resolved_group(resolved_group), was_canceled(was_canceled) {} }; - // event triggered when a betting market group is closed. When we get this, + // event triggered when a betting market group is closed. When we get this, // if all child betting market groups are closed, transition to finished struct betting_market_group_closed_event { @@ -127,7 +127,7 @@ namespace graphene { namespace chain { void on_entry(const upcoming_event& event, event_state_machine_& fsm) { dlog("event ${id} -> upcoming", ("id", fsm.event_obj->id)); auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id))) try { @@ -147,7 +147,7 @@ namespace graphene { namespace chain { void on_entry(const in_progress_event& event, event_state_machine_& fsm) { dlog("event ${id} -> in_progress", ("id", fsm.event_obj->id)); auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(fsm.event_obj->id))) try { @@ -203,7 +203,7 @@ namespace graphene { namespace chain { void freeze_betting_market_groups(const frozen_event& event) { auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) { try @@ -222,7 +222,7 @@ namespace graphene { namespace chain { void close_all_betting_market_groups(const finished_event& event) { auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) { try @@ -241,7 +241,7 @@ namespace graphene { namespace chain { void cancel_all_betting_market_groups(const canceled_event& event) { auto& betting_market_group_index = event.db.template get_index_type().indices().template get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) event.db.modify(betting_market_group, [&event](betting_market_group_object& betting_market_group_obj) { betting_market_group_obj.on_canceled_event(event.db, true); @@ -252,15 +252,15 @@ namespace graphene { namespace chain { bool all_betting_market_groups_are_closed(const betting_market_group_closed_event& event) { auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) if (betting_market_group.id != event.closed_group) { betting_market_group_status status = betting_market_group.get_status(); - if (status != betting_market_group_status::closed && - status != betting_market_group_status::graded && - status != betting_market_group_status::re_grading && - status != betting_market_group_status::settled && + if (status != betting_market_group_status::closed && + status != betting_market_group_status::graded && + status != betting_market_group_status::re_grading && + status != betting_market_group_status::settled && status != betting_market_group_status::canceled) return false; } @@ -276,7 +276,7 @@ namespace graphene { namespace chain { if (event_obj->at_least_one_betting_market_group_settled) return false; auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) if (betting_market_group.id != event.resolved_group) if (betting_market_group.get_status() != betting_market_group_status::canceled) @@ -290,7 +290,7 @@ namespace graphene { namespace chain { event_obj->at_least_one_betting_market_group_settled = true; auto& betting_market_group_index = event.db.get_index_type().indices().get(); - for (const betting_market_group_object& betting_market_group : + for (const betting_market_group_object& betting_market_group : boost::make_iterator_range(betting_market_group_index.equal_range(event_obj->id))) { if (betting_market_group.id != event.resolved_group) { betting_market_group_status status = betting_market_group.get_status(); @@ -344,7 +344,6 @@ namespace graphene { namespace chain { { FC_THROW_EXCEPTION(graphene::chain::no_transition, "No transition"); } - template void no_transition(canceled_event const& e, Fsm&, int state) { @@ -372,7 +371,7 @@ namespace graphene { namespace chain { { } - event_object::event_object(const event_object& rhs) : + event_object::event_object(const event_object& rhs) : graphene::db::abstract_object(rhs), name(rhs.name), season(rhs.season), @@ -408,7 +407,7 @@ namespace graphene { namespace chain { } namespace { - + bool verify_event_status_constants() { unsigned error_count = 0; @@ -443,19 +442,19 @@ namespace graphene { namespace chain { dlog("Event status constants are correct"); else wlog("There were ${count} errors in the event status constants", ("count", error_count)); - + return error_count == 0; } } // end anonymous namespace - + event_status event_object::get_status() const { static bool state_constants_are_correct = verify_event_status_constants(); (void)&state_constants_are_correct; event_state state = (event_state)my->state_machine.current_state()[0]; - + ddump((state)); - + switch (state) { case event_state::upcoming: @@ -523,8 +522,8 @@ namespace graphene { namespace chain { my->state_machine.process_event(betting_market_group_closed_event(db, closed_group)); } - // These are the only statuses that can be explicitly set by witness operations. The missing - // status, 'settled', is automatically set when all of the betting market groups have + // These are the only statuses that can be explicitly set by witness operations. The missing + // status, 'settled', is automatically set when all of the betting market groups have // settled/canceled void event_object::dispatch_new_status(database& db, event_status new_status) { @@ -533,16 +532,16 @@ namespace graphene { namespace chain { on_upcoming_event(db); break; case event_status::in_progress: // by witnesses when the event starts - on_in_progress_event(db); + on_in_progress_event(db); break; case event_status::frozen: // by witnesses when the event needs to be frozen - on_frozen_event(db); + on_frozen_event(db); break; case event_status::finished: // by witnesses when the event is complete - on_finished_event(db); + on_finished_event(db); break; case event_status::canceled: // by witnesses to cancel the event - on_canceled_event(db); + on_canceled_event(db); break; default: FC_THROW("Status ${new_status} cannot be explicitly set", ("new_status", new_status)); @@ -551,7 +550,7 @@ namespace graphene { namespace chain { } } // graphene::chain -namespace fc { +namespace fc { // Manually reflect event_object to variant to properly reflect "state" void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v, uint32_t max_depth) { diff --git a/libraries/chain/game_object.cpp b/libraries/chain/game_object.cpp index 5874fd9e9..4a35abfc2 100644 --- a/libraries/chain/game_object.cpp +++ b/libraries/chain/game_object.cpp @@ -547,7 +547,7 @@ namespace graphene { namespace chain { } } // graphene::chain -namespace fc { +namespace fc { // Manually reflect game_object to variant to properly reflect "state" void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v, uint32_t max_depth) { diff --git a/libraries/chain/hardfork.d/SON3.hf b/libraries/chain/hardfork.d/SON3.hf new file mode 100644 index 000000000..6abf3f341 --- /dev/null +++ b/libraries/chain/hardfork.d/SON3.hf @@ -0,0 +1,7 @@ +#ifndef HARDFORK_SON3_TIME +#ifdef BUILD_PEERPLAYS_TESTNET +#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-04-30T00:00:00")) +#else +#define HARDFORK_SON3_TIME (fc::time_point_sec::from_iso_string("2022-04-30T00:00:00")) +#endif +#endif diff --git a/libraries/chain/include/graphene/chain/betting_market_object.hpp b/libraries/chain/include/graphene/chain/betting_market_object.hpp index 2abe6b20b..c16fe53d5 100644 --- a/libraries/chain/include/graphene/chain/betting_market_object.hpp +++ b/libraries/chain/include/graphene/chain/betting_market_object.hpp @@ -24,19 +24,18 @@ #pragma once #include +#include #include #include -#include -#include - #include +#include namespace graphene { namespace chain { class betting_market_object; class betting_market_group_object; } } -namespace fc { +namespace fc { void to_variant(const graphene::chain::betting_market_object& betting_market_obj, fc::variant& v, uint32_t max_depth = 1); void from_variant(const fc::variant& v, graphene::chain::betting_market_object& betting_market_obj, uint32_t max_depth = 1); void to_variant(const graphene::chain::betting_market_group_object& betting_market_group_obj, fc::variant& v, uint32_t max_depth = 1); @@ -626,10 +625,9 @@ typedef multi_index_container< typedef generic_index betting_market_position_index; - template inline Stream& operator<<( Stream& s, const betting_market_object& betting_market_obj ) -{ +{ // pack all fields exposed in the header in the usual way // instead of calling the derived pack, just serialize the one field in the base class // fc::raw::pack >(s, betting_market_obj); @@ -649,7 +647,7 @@ inline Stream& operator<<( Stream& s, const betting_market_object& betting_marke } template inline Stream& operator>>( Stream& s, betting_market_object& betting_market_obj ) -{ +{ // unpack all fields exposed in the header in the usual way //fc::raw::unpack >(s, betting_market_obj); fc::raw::unpack(s, betting_market_obj.id); @@ -663,14 +661,14 @@ inline Stream& operator>>( Stream& s, betting_market_object& betting_market_obj fc::raw::unpack(s, stringified_stream); std::istringstream stream(stringified_stream); betting_market_obj.unpack_impl(stream); - + return s; } template inline Stream& operator<<( Stream& s, const betting_market_group_object& betting_market_group_obj ) -{ +{ // pack all fields exposed in the header in the usual way // instead of calling the derived pack, just serialize the one field in the base class // fc::raw::pack >(s, betting_market_group_obj); @@ -693,7 +691,7 @@ inline Stream& operator<<( Stream& s, const betting_market_group_object& betting } template inline Stream& operator>>( Stream& s, betting_market_group_object& betting_market_group_obj ) -{ +{ // unpack all fields exposed in the header in the usual way //fc::raw::unpack >(s, betting_market_group_obj); fc::raw::unpack(s, betting_market_group_obj.id); @@ -711,15 +709,113 @@ inline Stream& operator>>( Stream& s, betting_market_group_object& betting_marke fc::raw::unpack(s, stringified_stream); std::istringstream stream(stringified_stream); betting_market_group_obj.unpack_impl(stream); - + return s; } - } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) ) -FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description)(event_id)(rules_id)(asset_id)(total_matched_bets_amount)(never_in_play)(delay_before_settling)(settling_time) ) -FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id)(description)(payout_condition)(resolution) ) + FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(end_of_delay) ) FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) ) + +namespace fc { + + template<> + template<> + inline void if_enum::from_variant(const variant &vo, graphene::chain::betting_market_object &v, uint32_t max_depth) { + from_variant(vo, v, max_depth); + } + + template<> + template<> + inline void if_enum::to_variant(const graphene::chain::betting_market_object &v, variant &vo, uint32_t max_depth) { + to_variant(v, vo, max_depth); + } + + namespace raw { namespace detail { + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::betting_market_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::betting_market_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::unpack(fc::datastream &s, graphene::chain::betting_market_object &v, uint32_t) { + s >> v; + } + + } } // namespace fc::raw::detail + + template <> + struct get_typename { + static const char *name() { + return "graphene::chain::betting_market_object"; + } + }; + template <> + struct reflector { + typedef graphene::chain::betting_market_object type; + typedef fc::true_type is_defined; + typedef fc::false_type is_enum; + }; +} // namespace fc + +namespace fc { + + template<> + template<> + inline void if_enum::from_variant(const variant &vo, graphene::chain::betting_market_group_object &v, uint32_t max_depth) { + from_variant(vo, v, max_depth); + } + + template<> + template<> + inline void if_enum::to_variant(const graphene::chain::betting_market_group_object &v, variant &vo, uint32_t max_depth) { + to_variant(v, vo, max_depth); + } + + namespace raw { namespace detail { + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::betting_market_group_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::betting_market_group_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::unpack(fc::datastream &s, graphene::chain::betting_market_group_object &v, uint32_t) { + s >> v; + } + + } } // namespace fc::raw:detail + + template <> + struct get_typename { + static const char *name() { + return "graphene::chain::betting_market_group_object"; + } + }; + template <> + struct reflector { + typedef graphene::chain::betting_market_group_object type; + typedef fc::true_type is_defined; + typedef fc::false_type is_enum; + }; +} // namespace fc diff --git a/libraries/chain/include/graphene/chain/event_object.hpp b/libraries/chain/include/graphene/chain/event_object.hpp index 56330029e..ff75c286d 100644 --- a/libraries/chain/include/graphene/chain/event_object.hpp +++ b/libraries/chain/include/graphene/chain/event_object.hpp @@ -35,7 +35,7 @@ namespace graphene { namespace chain { class event_object; } } -namespace fc { +namespace fc { void to_variant(const graphene::chain::event_object& event_obj, fc::variant& v, uint32_t max_depth = 1); void from_variant(const fc::variant& v, graphene::chain::event_object& event_obj, uint32_t max_depth = 1); } //end namespace fc @@ -56,7 +56,7 @@ class event_object : public graphene::db::abstract_object< event_object > event_object& operator=(const event_object& rhs); internationalized_string_type name; - + internationalized_string_type season; optional start_time; @@ -114,7 +114,7 @@ typedef generic_index event_object_ template inline Stream& operator<<( Stream& s, const event_object& event_obj ) - { + { fc_elog(fc::logger::get("event"), "In event_obj to_raw"); // pack all fields exposed in the header in the usual way // instead of calling the derived pack, just serialize the one field in the base class @@ -137,7 +137,7 @@ typedef generic_index event_object_ } template inline Stream& operator>>( Stream& s, event_object& event_obj ) - { + { fc_elog(fc::logger::get("event"), "In event_obj from_raw"); // unpack all fields exposed in the header in the usual way //fc::raw::unpack >(s, event_obj); @@ -154,10 +154,57 @@ typedef generic_index event_object_ fc::raw::unpack(s, stringified_stream); std::istringstream stream(stringified_stream); event_obj.unpack_impl(stream); - + return s; } } } // graphene::chain -FC_REFLECT(graphene::chain::event_object, (name)(season)(start_time)(event_group_id)(at_least_one_betting_market_group_settled)(scores)) +namespace fc { + + template<> + template<> + inline void if_enum::from_variant(const variant &vo, graphene::chain::event_object &v, uint32_t max_depth) { + from_variant(vo, v, max_depth); + } + + template<> + template<> + inline void if_enum::to_variant(const graphene::chain::event_object &v, variant &vo, uint32_t max_depth) { + to_variant(v, vo, max_depth); + } + namespace raw { namespace detail { + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::event_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::event_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::unpack(fc::datastream &s, graphene::chain::event_object &v, uint32_t) { + s >> v; + } + + } } // namespace fc::raw::detail + + template <> + struct get_typename { + static const char *name() { + return "graphene::chain::event_object"; + } + }; + template <> + struct reflector { + typedef graphene::chain::event_object type; + typedef fc::true_type is_defined; + typedef fc::false_type is_enum; + }; +} // namespace fc diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index ee2640291..406a235eb 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -23,6 +23,7 @@ */ #pragma once +#include #include #include @@ -65,19 +66,27 @@ msg \ ) -#define GRAPHENE_TRY_NOTIFY( signal, ... ) \ - try \ - { \ - signal( __VA_ARGS__ ); \ - } \ - catch( const graphene::chain::plugin_exception& e ) \ - { \ - elog( "Caught plugin exception: ${e}", ("e", e.to_detail_string() ) ); \ - throw; \ - } \ - catch( ... ) \ - { \ - wlog( "Caught unexpected exception in plugin" ); \ +#define GRAPHENE_TRY_NOTIFY( signal, ... ) \ + try \ + { \ + signal( __VA_ARGS__ ); \ + } \ + catch( const graphene::chain::plugin_exception& e ) \ + { \ + elog( "Caught plugin exception: ${e}", ("e", e.to_detail_string() ) ); \ + throw; \ + } \ + catch( const boost::exception& e ) \ + { \ + elog( "Caught plugin boost::exception: ${e}", ("e", boost::diagnostic_information(e) ) ); \ + } \ + catch( const std::exception& e ) \ + { \ + elog( "Caught plugin std::exception: ${e}", ("e", e.what() ) ); \ + } \ + catch( ... ) \ + { \ + wlog( "Caught unexpected exception in plugin" ); \ } namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/game_object.hpp b/libraries/chain/include/graphene/chain/game_object.hpp index abef14444..cf31e49d7 100644 --- a/libraries/chain/include/graphene/chain/game_object.hpp +++ b/libraries/chain/include/graphene/chain/game_object.hpp @@ -23,10 +23,8 @@ */ #pragma once -#include #include -#include -#include +#include #include #include #include @@ -35,7 +33,7 @@ namespace graphene { namespace chain { class game_object; } } -namespace fc { +namespace fc { void to_variant(const graphene::chain::game_object& game_obj, fc::variant& v, uint32_t max_depth = 1); void from_variant(const fc::variant& v, graphene::chain::game_object& game_obj, uint32_t max_depth = 1); } //end namespace fc @@ -82,7 +80,7 @@ namespace graphene { namespace chain { void on_move(database& db, const game_move_operation& op); void on_timeout(database& db); void start_game(database& db, const std::vector& players); - + // serialization functions: // for serializing to raw, go through a temporary sstream object to avoid // having to implement serialization in the header file @@ -116,7 +114,7 @@ namespace graphene { namespace chain { template inline Stream& operator<<( Stream& s, const game_object& game_obj ) - { + { // pack all fields exposed in the header in the usual way // instead of calling the derived pack, just serialize the one field in the base class // fc::raw::pack >(s, game_obj); @@ -138,7 +136,7 @@ namespace graphene { namespace chain { template inline Stream& operator>>( Stream& s, game_object& game_obj ) - { + { // unpack all fields exposed in the header in the usual way //fc::raw::unpack >(s, game_obj); fc::raw::unpack(s, game_obj.id); @@ -153,10 +151,9 @@ namespace graphene { namespace chain { fc::raw::unpack(s, stringified_stream); std::istringstream stream(stringified_stream); game_obj.unpack_impl(stream); - + return s; } - } } FC_REFLECT_ENUM(graphene::chain::game_state, @@ -165,7 +162,52 @@ FC_REFLECT_ENUM(graphene::chain::game_state, (expecting_reveal_moves) (game_complete)) -//FC_REFLECT_TYPENAME(graphene::chain::game_object) // manually serialized -FC_REFLECT(graphene::chain::game_object, (players)) +namespace fc { + + template<> + template<> + inline void if_enum::from_variant(const variant &vo, graphene::chain::game_object &v, uint32_t max_depth) { + from_variant(vo, v, max_depth); + } + template<> + template<> + inline void if_enum::to_variant(const graphene::chain::game_object &v, variant &vo, uint32_t max_depth) { + to_variant(v, vo, max_depth); + } + namespace raw { namespace detail { + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::game_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::game_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::unpack(fc::datastream &s, graphene::chain::game_object &v, uint32_t) { + s >> v; + } + + } } // namespace fc::raw::detail + + template <> + struct get_typename { + static const char *name() { + return "graphene::chain::game_object"; + } + }; + template <> + struct reflector { + typedef graphene::chain::game_object type; + typedef fc::true_type is_defined; + typedef fc::false_type is_enum; + }; +} // namespace fc diff --git a/libraries/chain/include/graphene/chain/match_object.hpp b/libraries/chain/include/graphene/chain/match_object.hpp index 72c346a72..33df4d019 100644 --- a/libraries/chain/include/graphene/chain/match_object.hpp +++ b/libraries/chain/include/graphene/chain/match_object.hpp @@ -1,8 +1,5 @@ #pragma once -#include -#include -#include -#include +#include #include #include #include @@ -11,11 +8,12 @@ namespace graphene { namespace chain { class match_object; } } -namespace fc { +namespace fc { void to_variant(const graphene::chain::match_object& match_obj, fc::variant& v, uint32_t max_depth = 1); void from_variant(const fc::variant& v, graphene::chain::match_object& match_obj, uint32_t max_depth = 1); } //end namespace fc + namespace graphene { namespace chain { class database; using namespace graphene::db; @@ -89,6 +87,7 @@ namespace graphene { namespace chain { void pack_impl(std::ostream& stream) const; void unpack_impl(std::istream& stream); + void on_initiate_match(database& db); void on_game_complete(database& db, const game_object& game); game_id_type start_next_game(database& db, match_id_type match_id); @@ -106,7 +105,7 @@ namespace graphene { namespace chain { template inline Stream& operator<<( Stream& s, const match_object& match_obj ) - { + { // pack all fields exposed in the header in the usual way // instead of calling the derived pack, just serialize the one field in the base class // fc::raw::pack >(s, match_obj); @@ -132,7 +131,7 @@ namespace graphene { namespace chain { template inline Stream& operator>>( Stream& s, match_object& match_obj ) - { + { // unpack all fields exposed in the header in the usual way //fc::raw::unpack >(s, match_obj); fc::raw::unpack(s, match_obj.id); @@ -151,10 +150,9 @@ namespace graphene { namespace chain { fc::raw::unpack(s, stringified_stream); std::istringstream stream(stringified_stream); match_obj.unpack_impl(stream); - + return s; } - } } FC_REFLECT_ENUM(graphene::chain::match_state, @@ -162,6 +160,52 @@ FC_REFLECT_ENUM(graphene::chain::match_state, (match_in_progress) (match_complete)) -//FC_REFLECT_TYPENAME(graphene::chain::match_object) // manually serialized -FC_REFLECT(graphene::chain::match_object, (players)) +namespace fc { + template<> + template<> + inline void if_enum::from_variant(const variant &vo, graphene::chain::match_object &v, uint32_t max_depth) { + from_variant(vo, v, max_depth); + } + + template<> + template<> + inline void if_enum::to_variant(const graphene::chain::match_object &v, variant &vo, uint32_t max_depth) { + to_variant(v, vo, max_depth); + } + + namespace raw { namespace detail { + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::match_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::match_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::unpack(fc::datastream &s, graphene::chain::match_object &v, uint32_t) { + s >> v; + } + + } } // namespace fc::raw::detail + + template <> + struct get_typename { + static const char *name() { + return "graphene::chain::match_object"; + } + }; + template <> + struct reflector { + typedef graphene::chain::match_object type; + typedef fc::true_type is_defined; + typedef fc::false_type is_enum; + }; +} // namespace fc diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 321b08d98..098e5dd15 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -577,6 +577,8 @@ FC_REFLECT_TYPENAME( graphene::chain::fba_accumulator_id_type ) FC_REFLECT_TYPENAME( graphene::chain::betting_market_position_id_type ) FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type ) FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::game_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::match_id_type ) FC_REFLECT_TYPENAME( graphene::chain::custom_permission_id_type ) FC_REFLECT_TYPENAME( graphene::chain::custom_account_authority_id_type ) FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type ) diff --git a/libraries/chain/include/graphene/chain/tournament_object.hpp b/libraries/chain/include/graphene/chain/tournament_object.hpp index 140770e2d..53ac38762 100644 --- a/libraries/chain/include/graphene/chain/tournament_object.hpp +++ b/libraries/chain/include/graphene/chain/tournament_object.hpp @@ -1,8 +1,7 @@ #pragma once #include -#include #include -#include +#include #include #include #include @@ -11,7 +10,7 @@ namespace graphene { namespace chain { class tournament_object; } } -namespace fc { +namespace fc { void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v, uint32_t max_depth = 1); void from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj, uint32_t max_depth = 1); } //end namespace fc @@ -154,10 +153,9 @@ namespace graphene { namespace chain { > tournament_details_object_multi_index_type; typedef generic_index tournament_details_index; - template inline Stream& operator<<( Stream& s, const tournament_object& tournament_obj ) - { + { fc_elog(fc::logger::get("tournament"), "In tournament_obj to_raw"); // pack all fields exposed in the header in the usual way // instead of calling the derived pack, just serialize the one field in the base class @@ -175,15 +173,16 @@ namespace graphene { namespace chain { std::ostringstream stream; tournament_obj.pack_impl(stream); std::string stringified_stream(stream.str()); - fc_elog(fc::logger::get("tournament"), "Serialized state ${state} to bytes ${bytes}", + fc_elog(fc::logger::get("tournament"), "Serialized state ${state} to bytes ${bytes}", ("state", tournament_obj.get_state())("bytes", fc::to_hex(stringified_stream.c_str(), stringified_stream.size()))); fc::raw::pack(s, stream.str()); return s; } + template inline Stream& operator>>( Stream& s, tournament_object& tournament_obj ) - { + { fc_elog(fc::logger::get("tournament"), "In tournament_obj from_raw"); // unpack all fields exposed in the header in the usual way //fc::raw::unpack >(s, tournament_obj); @@ -201,9 +200,9 @@ namespace graphene { namespace chain { fc::raw::unpack(s, stringified_stream); std::istringstream stream(stringified_stream); tournament_obj.unpack_impl(stream); - fc_elog(fc::logger::get("tournament"), "Deserialized state ${state} from bytes ${bytes}", + fc_elog(fc::logger::get("tournament"), "Deserialized state ${state} from bytes ${bytes}", ("state", tournament_obj.get_state())("bytes", fc::to_hex(stringified_stream.c_str(), stringified_stream.size()))); - + return s; } @@ -230,8 +229,6 @@ namespace graphene { namespace chain { flat_set before_account_ids; }; - - } } FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::object), @@ -240,8 +237,7 @@ FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::ob (payers) (players_payers) (matches)) -//FC_REFLECT_TYPENAME(graphene::chain::tournament_object) // manually serialized -FC_REFLECT(graphene::chain::tournament_object, (creator)) + FC_REFLECT_ENUM(graphene::chain::tournament_state, (accepting_registrations) (awaiting_start) @@ -249,3 +245,52 @@ FC_REFLECT_ENUM(graphene::chain::tournament_state, (registration_period_expired) (concluded)) +namespace fc { + + template<> + template<> + inline void if_enum::from_variant(const variant &vo, graphene::chain::tournament_object &v, uint32_t max_depth) { + from_variant(vo, v, max_depth); + } + + template<> + template<> + inline void if_enum::to_variant(const graphene::chain::tournament_object &v, variant &vo, uint32_t max_depth) { + to_variant(v, vo, max_depth); + } + + namespace raw { namespace detail { + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::tournament_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::pack(fc::datastream &s, const graphene::chain::tournament_object &v, uint32_t) { + s << v; + } + + template<> + template<> + inline void if_enum::unpack(fc::datastream &s, graphene::chain::tournament_object &v, uint32_t) { + s >> v; + } + + } } // namespace fc::raw::detail + + template <> + struct get_typename { + static const char *name() { + return "graphene::chain::tournament_object"; + } + }; + template <> + struct reflector { + typedef graphene::chain::tournament_object type; + typedef fc::true_type is_defined; + typedef fc::false_type is_enum; + }; +} // namespace fc diff --git a/libraries/chain/include/graphene/chain/voters_info.hpp b/libraries/chain/include/graphene/chain/voters_info.hpp new file mode 100644 index 000000000..53b0e74a4 --- /dev/null +++ b/libraries/chain/include/graphene/chain/voters_info.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace graphene { namespace chain { + + /** + * @class voters_info_object + * @ingroup object + */ + struct voters_info_object { + vote_id_type vote_id; + vector voters; + }; + + /** + * @class voters_info + * @brief tracks information about a voters info + * @ingroup object + */ + struct voters_info { + optional voters_for_committee_member; + optional voters_for_witness; + optional > voters_for_workers; + optional > voters_against_workers; + optional voters_for_son; + }; + +} } // graphene::chain + +FC_REFLECT( graphene::chain::voters_info_object, + (vote_id) + (voters) ) + +FC_REFLECT( graphene::chain::voters_info, + (voters_for_committee_member) + (voters_for_witness) + (voters_for_workers) + (voters_against_workers) + (voters_for_son) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/votes_info.hpp b/libraries/chain/include/graphene/chain/votes_info.hpp new file mode 100644 index 000000000..0a5155892 --- /dev/null +++ b/libraries/chain/include/graphene/chain/votes_info.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace graphene { namespace chain { + + /** + * @class votes_info_object + * @ingroup object + */ + struct votes_info_object { + vote_id_type vote_id; + object_id_type id; + }; + + /** + * @class votes_info + * @brief tracks information about a votes info + * @ingroup object + */ + struct votes_info { + optional< vector< votes_info_object > > votes_for_committee_members; + optional< vector< votes_info_object > > votes_for_witnesses; + optional< vector< votes_info_object > > votes_for_workers; + optional< vector< votes_info_object > > votes_against_workers; + optional< vector< votes_info_object > > votes_for_sons; + }; + +} } // graphene::chain + +FC_REFLECT( graphene::chain::votes_info_object, + (vote_id) + (id) ) + +FC_REFLECT( graphene::chain::votes_info, + (votes_for_committee_members) + (votes_for_witnesses) + (votes_for_workers) + (votes_against_workers) + (votes_for_sons) ) \ No newline at end of file diff --git a/libraries/chain/match_object.cpp b/libraries/chain/match_object.cpp index e11f0e8aa..c9e8ccb90 100644 --- a/libraries/chain/match_object.cpp +++ b/libraries/chain/match_object.cpp @@ -362,7 +362,7 @@ namespace graphene { namespace chain { } } // graphene::chain -namespace fc { +namespace fc { // Manually reflect match_object to variant to properly reflect "state" void to_variant(const graphene::chain::match_object& match_obj, fc::variant& v, uint32_t max_depth) { try { diff --git a/libraries/chain/protocol/fee_schedule.cpp b/libraries/chain/protocol/fee_schedule.cpp index 122136eae..e7ad62847 100644 --- a/libraries/chain/protocol/fee_schedule.cpp +++ b/libraries/chain/protocol/fee_schedule.cpp @@ -192,6 +192,16 @@ namespace graphene { namespace chain { FC_ASSERT( *extensions.value.betting_rake_fee_percentage <= TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE, "Rake fee percentage must not be greater than ${max}", ("max", TOURNAMENT_MAXIMAL_RAKE_FEE_PERCENTAGE)); } + + if( extensions.value.son_heartbeat_frequency.valid() && extensions.value.son_deregister_time.valid() ) + FC_ASSERT( *extensions.value.son_heartbeat_frequency < *extensions.value.son_deregister_time ); + + if( extensions.value.son_heartbeat_frequency.valid() && extensions.value.son_down_time.valid() ) + FC_ASSERT( *extensions.value.son_heartbeat_frequency < *extensions.value.son_down_time ); + + if( extensions.value.son_heartbeat_frequency.valid() && extensions.value.son_pay_time.valid() ) + FC_ASSERT( *extensions.value.son_heartbeat_frequency < *extensions.value.son_pay_time ); + } } } // graphene::chain diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index 056652e56..0d9ef969d 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -721,7 +721,7 @@ namespace graphene { namespace chain { } } } // graphene::chain -namespace fc { +namespace fc { // Manually reflect tournament_object to variant to properly reflect "state" void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v, uint32_t max_depth) { diff --git a/libraries/db/include/graphene/db/undo_database.hpp b/libraries/db/include/graphene/db/undo_database.hpp index 4b727372e..0eb00f558 100644 --- a/libraries/db/include/graphene/db/undo_database.hpp +++ b/libraries/db/include/graphene/db/undo_database.hpp @@ -34,10 +34,10 @@ namespace graphene { namespace db { struct undo_state { - unordered_map > old_values; - unordered_map old_index_next_ids; - std::unordered_set new_ids; - unordered_map > removed; + unordered_map > old_values; + unordered_map old_index_next_ids; + std::set > new_ids; + unordered_map > removed; }; diff --git a/libraries/fc b/libraries/fc index 488883921..6171e973c 160000 --- a/libraries/fc +++ b/libraries/fc @@ -1 +1 @@ -Subproject commit 488883921936139e8734b99822d3a589afe80da1 +Subproject commit 6171e973c7fcfc9e0a39eaee2f05da84416a90e6 diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 6eb3ff0c0..db3fa464c 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -79,123 +79,138 @@ account_history_plugin_impl::~account_history_plugin_impl() void account_history_plugin_impl::update_account_histories( const signed_block& b ) { - graphene::chain::database& db = database(); - vector >& hist = db.get_applied_operations(); - bool is_first = true; - auto skip_oho_id = [&is_first,&db,this]() { - if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo - { - db.remove( db.create( []( operation_history_object& obj) {} ) ); - is_first = false; - } - else - _oho_index->use_next_id(); - }; - - for( optional< operation_history_object >& o_op : hist ) - { - optional oho; - - auto create_oho = [&]() { - is_first = false; - operation_history_object result = db.create( [&]( operation_history_object& h ) + try \ + { + graphene::chain::database& db = database(); + vector >& hist = db.get_applied_operations(); + bool is_first = true; + auto skip_oho_id = [&is_first,&db,this]() { + if( is_first && db._undo_db.enabled() ) // this ensures that the current id is rolled back on undo { - if( o_op.valid() ) - h = *o_op; - } ); - o_op->id = result.id; - return optional(result); + db.remove( db.create( []( operation_history_object& obj) {} ) ); + is_first = false; + } + else + _oho_index->use_next_id(); }; - if( !o_op.valid() || ( _max_ops_per_account == 0 && _partial_operations ) ) - { - // Note: the 2nd and 3rd checks above are for better performance, when the db is not clean, - // they will break consistency of account_stats.total_ops and removed_ops and most_recent_op - skip_oho_id(); - continue; - } - else if( !_partial_operations ) - // add to the operation history index - oho = create_oho(); - - const operation_history_object& op = *o_op; - - // get the set of accounts this operation applies to - flat_set impacted; - vector other; - // fee payer is added here - operation_get_required_authorities( op.op, impacted, impacted, other, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); - - if( op.op.which() == operation::tag< account_create_operation >::value ) - impacted.insert( op.result.get() ); - else - graphene::chain::operation_get_impacted_accounts( op.op, impacted, - MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) ); - if( op.op.which() == operation::tag< lottery_end_operation >::value ) + for( optional< operation_history_object >& o_op : hist ) { - auto lop = op.op.get< lottery_end_operation >(); - auto asset_object = lop.lottery( db ); - impacted.insert( asset_object.issuer ); - for( auto benefactor : asset_object.lottery_options->benefactors ) - impacted.insert( benefactor.id ); - } + optional oho; - for( auto& a : other ) - for( auto& item : a.account_auths ) - impacted.insert( item.first ); + auto create_oho = [&]() { + is_first = false; + operation_history_object result = db.create( [&]( operation_history_object& h ) + { + if( o_op.valid() ) + h = *o_op; + } ); + o_op->id = result.id; + return optional(result); + }; + + if( !o_op.valid() || ( _max_ops_per_account == 0 && _partial_operations ) ) + { + // Note: the 2nd and 3rd checks above are for better performance, when the db is not clean, + // they will break consistency of account_stats.total_ops and removed_ops and most_recent_op + skip_oho_id(); + continue; + } + else if( !_partial_operations ) + // add to the operation history index + oho = create_oho(); + + const operation_history_object& op = *o_op; + + // get the set of accounts this operation applies to + flat_set impacted; + vector other; + // fee payer is added here + operation_get_required_authorities( op.op, impacted, impacted, other, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( db.head_block_time() ) ); + + if( op.op.which() == operation::tag< account_create_operation >::value ) + impacted.insert( op.result.get() ); + else + graphene::chain::operation_get_impacted_accounts( op.op, impacted, + MUST_IGNORE_CUSTOM_OP_REQD_AUTHS(db.head_block_time()) ); + if( op.op.which() == operation::tag< lottery_end_operation >::value ) + { + auto lop = op.op.get< lottery_end_operation >(); + auto asset_object = lop.lottery( db ); + impacted.insert( asset_object.issuer ); + for( auto benefactor : asset_object.lottery_options->benefactors ) + impacted.insert( benefactor.id ); + } - // be here, either _max_ops_per_account > 0, or _partial_operations == false, or both - // if _partial_operations == false, oho should have been created above - // so the only case should be checked here is: - // whether need to create oho if _max_ops_per_account > 0 and _partial_operations == true + for( auto& a : other ) + for( auto& item : a.account_auths ) + impacted.insert( item.first ); - // for each operation this account applies to that is in the config link it into the history - if( _tracked_accounts.size() == 0 ) // tracking all accounts - { - // if tracking all accounts, when impacted is not empty (although it will always be), - // still need to create oho if _max_ops_per_account > 0 and _partial_operations == true - // so always need to create oho if not done - if (!impacted.empty() && !oho.valid()) { oho = create_oho(); } + // be here, either _max_ops_per_account > 0, or _partial_operations == false, or both + // if _partial_operations == false, oho should have been created above + // so the only case should be checked here is: + // whether need to create oho if _max_ops_per_account > 0 and _partial_operations == true - if( _max_ops_per_account > 0 ) + // for each operation this account applies to that is in the config link it into the history + if( _tracked_accounts.size() == 0 ) // tracking all accounts { - // Note: the check above is for better performance, when the db is not clean, - // it breaks consistency of account_stats.total_ops and removed_ops and most_recent_op, - // but it ensures it's safe to remove old entries in add_account_history(...) - for( auto& account_id : impacted ) + // if tracking all accounts, when impacted is not empty (although it will always be), + // still need to create oho if _max_ops_per_account > 0 and _partial_operations == true + // so always need to create oho if not done + if (!impacted.empty() && !oho.valid()) { oho = create_oho(); } + + if( _max_ops_per_account > 0 ) { - // we don't do index_account_keys here anymore, because - // that indexing now happens in observers' post_evaluate() + // Note: the check above is for better performance, when the db is not clean, + // it breaks consistency of account_stats.total_ops and removed_ops and most_recent_op, + // but it ensures it's safe to remove old entries in add_account_history(...) + for( auto& account_id : impacted ) + { + // we don't do index_account_keys here anymore, because + // that indexing now happens in observers' post_evaluate() - // add history - add_account_history( account_id, oho->id ); + // add history + add_account_history( account_id, oho->id ); + } } } - } - else // tracking a subset of accounts - { - // whether need to create oho if _max_ops_per_account > 0 and _partial_operations == true ? - // the answer: only need to create oho if a tracked account is impacted and need to save history - - if( _max_ops_per_account > 0 ) + else // tracking a subset of accounts { - // Note: the check above is for better performance, when the db is not clean, - // it breaks consistency of account_stats.total_ops and removed_ops and most_recent_op, - // but it ensures it's safe to remove old entries in add_account_history(...) - for( auto account_id : _tracked_accounts ) + // whether need to create oho if _max_ops_per_account > 0 and _partial_operations == true ? + // the answer: only need to create oho if a tracked account is impacted and need to save history + + if( _max_ops_per_account > 0 ) { - if( impacted.find( account_id ) != impacted.end() ) + // Note: the check above is for better performance, when the db is not clean, + // it breaks consistency of account_stats.total_ops and removed_ops and most_recent_op, + // but it ensures it's safe to remove old entries in add_account_history(...) + for( auto account_id : _tracked_accounts ) { - if (!oho.valid()) { oho = create_oho(); } - // add history - add_account_history( account_id, oho->id ); + if( impacted.find( account_id ) != impacted.end() ) + { + if (!oho.valid()) { oho = create_oho(); } + // add history + add_account_history( account_id, oho->id ); + } } } } + if (_partial_operations && ! oho.valid()) + skip_oho_id(); } - if (_partial_operations && ! oho.valid()) - skip_oho_id(); + } + catch( const boost::exception& e ) + { + elog( "Caught account_history_plugin::update_account_histories(...) boost::exception: ${e}", ("e", boost::diagnostic_information(e) ) ); + } + catch( const std::exception& e ) + { + elog( "Caught account_history_plugin::update_account_histories(...) std::exception: ${e}", ("e", e.what() ) ); + } + catch( ... ) + { + wlog( "Caught unexpected exception in account_history_plugin::update_account_histories(...)" ); } } diff --git a/libraries/plugins/affiliate_stats/include/graphene/affiliate_stats/affiliate_stats_api.hpp b/libraries/plugins/affiliate_stats/include/graphene/affiliate_stats/affiliate_stats_api.hpp index 29c45d9f0..7ec1a0883 100644 --- a/libraries/plugins/affiliate_stats/include/graphene/affiliate_stats/affiliate_stats_api.hpp +++ b/libraries/plugins/affiliate_stats/include/graphene/affiliate_stats/affiliate_stats_api.hpp @@ -29,7 +29,6 @@ #include #include -#include #include #include diff --git a/libraries/plugins/bookie/bookie_api.cpp b/libraries/plugins/bookie/bookie_api.cpp index 9caa9c625..98bd2d232 100644 --- a/libraries/plugins/bookie/bookie_api.cpp +++ b/libraries/plugins/bookie/bookie_api.cpp @@ -157,37 +157,45 @@ fc::variants bookie_api_impl::get_objects(const vector& ids) con { case event_id_type::type_id: { - auto& persistent_events_by_event_id = db->get_index_type().indices().get(); - auto iter = persistent_events_by_event_id.find(id.as()); - if (iter != persistent_events_by_event_id.end()) - return iter->ephemeral_event_object.to_variant(); + const auto &idx = db->get_index_type(); + const auto &aidx = dynamic_cast(idx); + const auto &refs = aidx.get_secondary_index(); + auto iter = refs.ephemeral_event_object.find(id.as()); + if (iter != refs.ephemeral_event_object.end()) + return iter->second.to_variant(); else return {}; } case bet_id_type::type_id: { - auto& persistent_bets_by_bet_id = db->get_index_type().indices().get(); - auto iter = persistent_bets_by_bet_id.find(id.as()); - if (iter != persistent_bets_by_bet_id.end()) - return iter->ephemeral_bet_object.to_variant(); + const auto &idx = db->get_index_type(); + const auto &aidx = dynamic_cast(idx); + const auto &refs = aidx.get_secondary_index(); + auto iter = refs.internal.find(id.as()); + if (iter != refs.internal.end()) + return iter->second.ephemeral_bet_object.to_variant(); else return {}; } case betting_market_object::type_id: - { - auto& persistent_betting_markets_by_betting_market_id = db->get_index_type().indices().get(); - auto iter = persistent_betting_markets_by_betting_market_id.find(id.as()); - if (iter != persistent_betting_markets_by_betting_market_id.end()) - return iter->ephemeral_betting_market_object.to_variant(); + { + const auto &idx = db->get_index_type(); + const auto &aidx = dynamic_cast(idx); + const auto &refs = aidx.get_secondary_index(); + auto iter = refs.ephemeral_betting_market_object.find(id.as()); + if (iter != refs.ephemeral_betting_market_object.end()) + return iter->second.to_variant(); else return {}; } case betting_market_group_object::type_id: - { - auto& persistent_betting_market_groups_by_betting_market_group_id = db->get_index_type().indices().get(); - auto iter = persistent_betting_market_groups_by_betting_market_group_id.find(id.as()); - if (iter != persistent_betting_market_groups_by_betting_market_group_id.end()) - return iter->ephemeral_betting_market_group_object.to_variant(); + { + const auto &idx = db->get_index_type(); + const auto &aidx = dynamic_cast(idx); + const auto &refs = aidx.get_secondary_index(); + auto iter = refs.internal.find(id.as()); + if (iter != refs.internal.end()) + return iter->second.ephemeral_betting_market_group_object.to_variant(); else return {}; } @@ -203,25 +211,28 @@ std::vector bookie_api_impl::get_matched_bets_for_bettor(acc { std::vector result; std::shared_ptr db = app.chain_database(); - auto& persistent_bets_by_bettor_id = db->get_index_type().indices().get(); - auto iter = persistent_bets_by_bettor_id.lower_bound(std::make_tuple(bettor_id, true)); - while (iter != persistent_bets_by_bettor_id.end() && - iter->get_bettor_id() == bettor_id && - iter->is_matched()) + const auto &idx = db->get_index_type(); + const auto &aidx = dynamic_cast(idx); + const auto &refs = aidx.get_secondary_index(); + + for( const auto& bet_pair : refs.internal ) { - matched_bet_object match; - match.id = iter->ephemeral_bet_object.id; - match.bettor_id = iter->ephemeral_bet_object.bettor_id; - match.betting_market_id = iter->ephemeral_bet_object.betting_market_id; - match.amount_to_bet = iter->ephemeral_bet_object.amount_to_bet; - match.back_or_lay = iter->ephemeral_bet_object.back_or_lay; - match.end_of_delay = iter->ephemeral_bet_object.end_of_delay; - match.amount_matched = iter->amount_matched; - match.associated_operations = iter->associated_operations; - result.emplace_back(std::move(match)); - - ++iter; + const auto& bet = bet_pair.second; + if( bet.get_bettor_id() == bettor_id && bet.is_matched() ) + { + matched_bet_object match; + match.id = bet.ephemeral_bet_object.id; + match.bettor_id = bet.ephemeral_bet_object.bettor_id; + match.betting_market_id = bet.ephemeral_bet_object.betting_market_id; + match.amount_to_bet = bet.ephemeral_bet_object.amount_to_bet; + match.back_or_lay = bet.ephemeral_bet_object.back_or_lay; + match.end_of_delay = bet.ephemeral_bet_object.end_of_delay; + match.amount_matched = bet.amount_matched; + match.associated_operations = bet.associated_operations; + result.emplace_back(std::move(match)); + } } + return result; } @@ -231,29 +242,32 @@ std::vector bookie_api_impl::get_all_matched_bets_for_bettor std::vector result; std::shared_ptr db = app.chain_database(); - auto& persistent_bets_by_bettor_id = db->get_index_type().indices().get(); - persistent_bet_multi_index_type::index::type::iterator iter; - if (start == bet_id_type()) - iter = persistent_bets_by_bettor_id.lower_bound(std::make_tuple(bettor_id, true)); - else - iter = persistent_bets_by_bettor_id.lower_bound(std::make_tuple(bettor_id, true, start)); - while (iter != persistent_bets_by_bettor_id.end() && - iter->get_bettor_id() == bettor_id && - iter->is_matched() && - result.size() < limit) + const auto &idx = db->get_index_type(); + const auto &aidx = dynamic_cast(idx); + const auto &refs = aidx.get_secondary_index(); + + for( const auto& bet_pair : refs.internal ) { - matched_bet_object match; - match.id = iter->ephemeral_bet_object.id; - match.bettor_id = iter->ephemeral_bet_object.bettor_id; - match.betting_market_id = iter->ephemeral_bet_object.betting_market_id; - match.amount_to_bet = iter->ephemeral_bet_object.amount_to_bet; - match.back_or_lay = iter->ephemeral_bet_object.back_or_lay; - match.end_of_delay = iter->ephemeral_bet_object.end_of_delay; - match.amount_matched = iter->amount_matched; - result.emplace_back(std::move(match)); - - ++iter; + const auto& bet_id = bet_pair.first; + const auto& bet = bet_pair.second; + if( bet.get_bettor_id() == bettor_id && + bet.is_matched() && + bet_id > start && + result.size() < limit ) + { + matched_bet_object match; + match.id = bet.ephemeral_bet_object.id; + match.bettor_id = bet.ephemeral_bet_object.bettor_id; + match.betting_market_id = bet.ephemeral_bet_object.betting_market_id; + match.amount_to_bet = bet.ephemeral_bet_object.amount_to_bet; + match.back_or_lay = bet.ephemeral_bet_object.back_or_lay; + match.end_of_delay = bet.ephemeral_bet_object.end_of_delay; + match.amount_matched = bet.amount_matched; + match.associated_operations = bet.associated_operations; + result.emplace_back(std::move(match)); + } } + return result; } diff --git a/libraries/plugins/bookie/bookie_plugin.cpp b/libraries/plugins/bookie/bookie_plugin.cpp index 5bc31f14c..4e9a77100 100644 --- a/libraries/plugins/bookie/bookie_plugin.cpp +++ b/libraries/plugins/bookie/bookie_plugin.cpp @@ -59,143 +59,80 @@ namespace detail * We do this by creating a secondary index on bet_object. We don't actually use it * to index any property of the bet, we just use it to register for callbacks. */ -class persistent_bet_object_helper : public secondary_index -{ - public: - virtual ~persistent_bet_object_helper() {} - - 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; - void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; } - private: - bookie_plugin* _bookie_plugin; -}; -void persistent_bet_object_helper::object_inserted(const object& obj) +void persistent_bet_index::object_inserted(const object& obj) { const bet_object& bet_obj = *boost::polymorphic_downcast(&obj); - _bookie_plugin->database().create([&](persistent_bet_object& saved_bet_obj) { - saved_bet_obj.ephemeral_bet_object = bet_obj; - }); + if(0 == internal.count(bet_obj.id)) + internal.insert( {bet_obj.id, bet_obj} ); + else + internal[bet_obj.id] = bet_obj; } -void persistent_bet_object_helper::object_modified(const object& after) +void persistent_bet_index::object_modified(const object& after) { - database& db = _bookie_plugin->database(); - auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); const bet_object& bet_obj = *boost::polymorphic_downcast(&after); - auto iter = persistent_bets_by_bet_id.find(bet_obj.id); - assert (iter != persistent_bets_by_bet_id.end()); - if (iter != persistent_bets_by_bet_id.end()) - db.modify(*iter, [&](persistent_bet_object& saved_bet_obj) { - saved_bet_obj.ephemeral_bet_object = bet_obj; - }); + auto iter = internal.find(bet_obj.id); + assert (iter != internal.end()); + if (iter != internal.end()) + iter->second = bet_obj; } //////////// end bet_object /////////////////// -class persistent_betting_market_object_helper : public secondary_index -{ - public: - virtual ~persistent_betting_market_object_helper() {} - - 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; - void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; } - private: - bookie_plugin* _bookie_plugin; -}; -void persistent_betting_market_object_helper::object_inserted(const object& obj) +void persistent_betting_market_index::object_inserted(const object& obj) { const betting_market_object& betting_market_obj = *boost::polymorphic_downcast(&obj); - _bookie_plugin->database().create([&](persistent_betting_market_object& saved_betting_market_obj) { - saved_betting_market_obj.ephemeral_betting_market_object = betting_market_obj; - }); + if(0 == ephemeral_betting_market_object.count(betting_market_obj.id)) + ephemeral_betting_market_object.insert( {betting_market_obj.id, betting_market_obj} ); + else + ephemeral_betting_market_object[betting_market_obj.id] = betting_market_obj; + } -void persistent_betting_market_object_helper::object_modified(const object& after) +void persistent_betting_market_index::object_modified(const object& after) { - database& db = _bookie_plugin->database(); - auto& persistent_betting_markets_by_betting_market_id = db.get_index_type().indices().get(); const betting_market_object& betting_market_obj = *boost::polymorphic_downcast(&after); - auto iter = persistent_betting_markets_by_betting_market_id.find(betting_market_obj.id); - assert (iter != persistent_betting_markets_by_betting_market_id.end()); - if (iter != persistent_betting_markets_by_betting_market_id.end()) - db.modify(*iter, [&](persistent_betting_market_object& saved_betting_market_obj) { - saved_betting_market_obj.ephemeral_betting_market_object = betting_market_obj; - }); + auto iter = ephemeral_betting_market_object.find(betting_market_obj.id); + assert (iter != ephemeral_betting_market_object.end()); + if (iter != ephemeral_betting_market_object.end()) + iter->second = betting_market_obj; } //////////// end betting_market_object /////////////////// -class persistent_betting_market_group_object_helper : public secondary_index -{ - public: - virtual ~persistent_betting_market_group_object_helper() {} - - 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; - void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; } - private: - bookie_plugin* _bookie_plugin; -}; -void persistent_betting_market_group_object_helper::object_inserted(const object& obj) +void persistent_betting_market_group_index::object_inserted(const object& obj) { const betting_market_group_object& betting_market_group_obj = *boost::polymorphic_downcast(&obj); - _bookie_plugin->database().create([&](persistent_betting_market_group_object& saved_betting_market_group_obj) { - saved_betting_market_group_obj.ephemeral_betting_market_group_object = betting_market_group_obj; - }); + if(0 == internal.count(betting_market_group_obj.id)) + internal.insert( {betting_market_group_obj.id, betting_market_group_obj} ); + else + internal[betting_market_group_obj.id] = betting_market_group_obj; } -void persistent_betting_market_group_object_helper::object_modified(const object& after) +void persistent_betting_market_group_index::object_modified(const object& after) { - database& db = _bookie_plugin->database(); - auto& persistent_betting_market_groups_by_betting_market_group_id = db.get_index_type().indices().get(); const betting_market_group_object& betting_market_group_obj = *boost::polymorphic_downcast(&after); - auto iter = persistent_betting_market_groups_by_betting_market_group_id.find(betting_market_group_obj.id); - assert (iter != persistent_betting_market_groups_by_betting_market_group_id.end()); - if (iter != persistent_betting_market_groups_by_betting_market_group_id.end()) - db.modify(*iter, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) { - saved_betting_market_group_obj.ephemeral_betting_market_group_object = betting_market_group_obj; - }); + auto iter = internal.find(betting_market_group_obj.id); + assert (iter != internal.end()); + if (iter != internal.end()) + iter->second = betting_market_group_obj; } //////////// end betting_market_group_object /////////////////// -class persistent_event_object_helper : public secondary_index -{ - public: - virtual ~persistent_event_object_helper() {} - - 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; - void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; } - private: - bookie_plugin* _bookie_plugin; -}; -void persistent_event_object_helper::object_inserted(const object& obj) +void persistent_event_index::object_inserted(const object& obj) { const event_object& event_obj = *boost::polymorphic_downcast(&obj); - _bookie_plugin->database().create([&](persistent_event_object& saved_event_obj) { - saved_event_obj.ephemeral_event_object = event_obj; - }); + if(0 == ephemeral_event_object.count(event_obj.id)) + ephemeral_event_object.insert( {event_obj.id, event_obj} ); + else + ephemeral_event_object[event_obj.id] = event_obj; } -void persistent_event_object_helper::object_modified(const object& after) +void persistent_event_index::object_modified(const object& after) { - database& db = _bookie_plugin->database(); - auto& persistent_events_by_event_id = db.get_index_type().indices().get(); const event_object& event_obj = *boost::polymorphic_downcast(&after); - auto iter = persistent_events_by_event_id.find(event_obj.id); - assert (iter != persistent_events_by_event_id.end()); - if (iter != persistent_events_by_event_id.end()) - db.modify(*iter, [&](persistent_event_object& saved_event_obj) { - saved_event_obj.ephemeral_event_object = event_obj; - }); + auto iter = ephemeral_event_object.find(event_obj.id); + assert (iter != ephemeral_event_object.end()); + if (iter != ephemeral_event_object.end()) + iter->second = event_obj; } //////////// end event_object /////////////////// @@ -207,7 +144,6 @@ class bookie_plugin_impl { } virtual ~bookie_plugin_impl(); - /** * Called After a block has been applied and committed. The callback * should not yield and should execute quickly. @@ -299,27 +235,35 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) const asset& amount_bet = bet_matched_op.amount_bet; // object may no longer exist //const bet_object& bet = bet_matched_op.bet_id(db); - auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); - auto bet_iter = persistent_bets_by_bet_id.find(bet_matched_op.bet_id); - assert(bet_iter != persistent_bets_by_bet_id.end()); - if (bet_iter != persistent_bets_by_bet_id.end()) + const auto &idx_bet_object = db.get_index_type(); + const auto &aidx_bet_object = dynamic_cast(idx_bet_object); + const auto &refs_bet_object = aidx_bet_object.get_secondary_index(); + auto& nonconst_refs_bet_object = const_cast(refs_bet_object); + + auto bet_iter = nonconst_refs_bet_object.internal.find(bet_matched_op.bet_id); + assert(bet_iter != nonconst_refs_bet_object.internal.end()); + if (bet_iter != nonconst_refs_bet_object.internal.end()) { - db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { - obj.amount_matched += amount_bet.amount; - if (is_operation_history_object_stored(op.id)) - obj.associated_operations.emplace_back(op.id); - }); - const bet_object& bet_obj = bet_iter->ephemeral_bet_object; - - auto& persistent_betting_market_idx = db.get_index_type().indices().get(); - auto persistent_betting_market_object_iter = persistent_betting_market_idx.find(bet_obj.betting_market_id); - FC_ASSERT(persistent_betting_market_object_iter != persistent_betting_market_idx.end()); - const betting_market_object& betting_market = persistent_betting_market_object_iter->ephemeral_betting_market_object; - - auto& persistent_betting_market_group_idx = db.get_index_type().indices().get(); - auto persistent_betting_market_group_object_iter = persistent_betting_market_group_idx.find(betting_market.group_id); - FC_ASSERT(persistent_betting_market_group_object_iter != persistent_betting_market_group_idx.end()); - const betting_market_group_object& betting_market_group = persistent_betting_market_group_object_iter->ephemeral_betting_market_group_object; + bet_iter->second.amount_matched += amount_bet.amount; + if (is_operation_history_object_stored(op.id)) + bet_iter->second.associated_operations.emplace_back(op.id); + + const bet_object& bet_obj = bet_iter->second.ephemeral_bet_object; + + const auto &idx_betting_market = db.get_index_type(); + const auto &aidx_betting_market = dynamic_cast(idx_betting_market); + const auto &refs_betting_market = aidx_betting_market.get_secondary_index(); + auto persistent_betting_market_object_iter = refs_betting_market.ephemeral_betting_market_object.find(bet_obj.betting_market_id); + FC_ASSERT(persistent_betting_market_object_iter != refs_betting_market.ephemeral_betting_market_object.end()); + const betting_market_object& betting_market = persistent_betting_market_object_iter->second; + + const auto &idx_betting_market_group = db.get_index_type(); + const auto &aidx_betting_market_group = dynamic_cast(idx_betting_market_group); + const auto &refs_betting_market_group = aidx_betting_market_group.get_secondary_index(); + auto& nonconst_refs_betting_market_group = const_cast(refs_betting_market_group); + auto persistent_betting_market_group_object_iter = nonconst_refs_betting_market_group.internal.find(betting_market.group_id); + FC_ASSERT(persistent_betting_market_group_object_iter != nonconst_refs_betting_market_group.internal.end()); + const betting_market_group_object& betting_market_group = persistent_betting_market_group_object_iter->second.ephemeral_betting_market_group_object; // if the object is still in the main database, keep the running total there // otherwise, add it directly to the persistent version @@ -330,9 +274,7 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) obj.total_matched_bets_amount += amount_bet.amount; }); else - db.modify( *persistent_betting_market_group_object_iter, [&]( persistent_betting_market_group_object& obj ){ - obj.ephemeral_betting_market_group_object.total_matched_bets_amount += amount_bet.amount; - }); + persistent_betting_market_group_object_iter->second.total_matched_bets_amount += amount_bet.amount; } } else if( op.op.which() == operation::tag::value ) @@ -364,33 +306,35 @@ void bookie_plugin_impl::on_block_applied( const signed_block& ) else if ( op.op.which() == operation::tag::value ) { const bet_canceled_operation& bet_canceled_op = op.op.get(); - auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); - auto bet_iter = persistent_bets_by_bet_id.find(bet_canceled_op.bet_id); - assert(bet_iter != persistent_bets_by_bet_id.end()); - if (bet_iter != persistent_bets_by_bet_id.end()) + const auto &idx_bet_object = db.get_index_type(); + const auto &aidx_bet_object = dynamic_cast(idx_bet_object); + const auto &refs_bet_object = aidx_bet_object.get_secondary_index(); + auto& nonconst_refs_bet_object = const_cast(refs_bet_object); + + auto bet_iter = nonconst_refs_bet_object.internal.find(bet_canceled_op.bet_id); + assert(bet_iter != nonconst_refs_bet_object.internal.end()); + if (bet_iter != nonconst_refs_bet_object.internal.end()) { // ilog("Adding bet_canceled_operation ${canceled_id} to bet ${bet_id}'s associated operations", // ("canceled_id", op.id)("bet_id", bet_canceled_op.bet_id)); - if (is_operation_history_object_stored(op.id)) - db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { - obj.associated_operations.emplace_back(op.id); - }); + bet_iter->second.associated_operations.emplace_back(op.id); } } else if ( op.op.which() == operation::tag::value ) { const bet_adjusted_operation& bet_adjusted_op = op.op.get(); - auto& persistent_bets_by_bet_id = db.get_index_type().indices().get(); - auto bet_iter = persistent_bets_by_bet_id.find(bet_adjusted_op.bet_id); - assert(bet_iter != persistent_bets_by_bet_id.end()); - if (bet_iter != persistent_bets_by_bet_id.end()) + const auto &idx_bet_object = db.get_index_type(); + const auto &aidx_bet_object = dynamic_cast(idx_bet_object); + const auto &refs_bet_object = aidx_bet_object.get_secondary_index(); + auto& nonconst_refs_bet_object = const_cast(refs_bet_object); + + auto bet_iter = nonconst_refs_bet_object.internal.find(bet_adjusted_op.bet_id); + assert(bet_iter != nonconst_refs_bet_object.internal.end()); + if (bet_iter != nonconst_refs_bet_object.internal.end()) { // ilog("Adding bet_adjusted_operation ${adjusted_id} to bet ${bet_id}'s associated operations", // ("adjusted_id", op.id)("bet_id", bet_adjusted_op.bet_id)); - if (is_operation_history_object_stored(op.id)) - db.modify(*bet_iter, [&]( persistent_bet_object& obj ) { - obj.associated_operations.emplace_back(op.id); - }); + bet_iter->second.associated_operations.emplace_back(op.id); } } @@ -472,31 +416,21 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma database().new_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { my->on_objects_new(ids); }); database().removed_objects.connect([this](const vector& ids, const vector& objs, const flat_set& impacted_accounts) { my->on_objects_removed(ids); }); - - //auto event_index = - database().add_index >(); - database().add_index >(); - database().add_index >(); - database().add_index >(); const primary_index& bet_object_idx = database().get_index_type >(); primary_index& nonconst_bet_object_idx = const_cast&>(bet_object_idx); - detail::persistent_bet_object_helper* persistent_bet_object_helper_index = nonconst_bet_object_idx.add_secondary_index(); - persistent_bet_object_helper_index->set_plugin_instance(this); + nonconst_bet_object_idx.add_secondary_index(); const primary_index& betting_market_object_idx = database().get_index_type >(); primary_index& nonconst_betting_market_object_idx = const_cast&>(betting_market_object_idx); - detail::persistent_betting_market_object_helper* persistent_betting_market_object_helper_index = nonconst_betting_market_object_idx.add_secondary_index(); - persistent_betting_market_object_helper_index->set_plugin_instance(this); + nonconst_betting_market_object_idx.add_secondary_index(); const primary_index& betting_market_group_object_idx = database().get_index_type >(); primary_index& nonconst_betting_market_group_object_idx = const_cast&>(betting_market_group_object_idx); - detail::persistent_betting_market_group_object_helper* persistent_betting_market_group_object_helper_index = nonconst_betting_market_group_object_idx.add_secondary_index(); - persistent_betting_market_group_object_helper_index->set_plugin_instance(this); + nonconst_betting_market_group_object_idx.add_secondary_index(); const primary_index& event_object_idx = database().get_index_type >(); primary_index& nonconst_event_object_idx = const_cast&>(event_object_idx); - detail::persistent_event_object_helper* persistent_event_object_helper_index = nonconst_event_object_idx.add_secondary_index(); - persistent_event_object_helper_index->set_plugin_instance(this); + nonconst_event_object_idx.add_secondary_index(); ilog("bookie plugin: plugin_startup() end"); } diff --git a/libraries/plugins/bookie/include/graphene/bookie/bookie_objects.hpp b/libraries/plugins/bookie/include/graphene/bookie/bookie_objects.hpp index 1166ced34..8ec58bd63 100644 --- a/libraries/plugins/bookie/include/graphene/bookie/bookie_objects.hpp +++ b/libraries/plugins/bookie/include/graphene/bookie/bookie_objects.hpp @@ -29,39 +29,21 @@ namespace graphene { namespace bookie { using namespace chain; -enum bookie_object_type -{ - persistent_event_object_type, - persistent_betting_market_group_object_type, - persistent_betting_market_object_type, - persistent_bet_object_type, - BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types -}; - namespace detail { -class persistent_event_object : public graphene::db::abstract_object +/** + * @brief This secondary index will allow a reverse lookup of all events that happened + */ +class persistent_event_index : public secondary_index { - public: - static const uint8_t space_id = bookie_objects; - static const uint8_t type_id = persistent_event_object_type; - - event_object ephemeral_event_object; +public: + virtual void object_inserted( const object& obj ) override; + virtual void object_modified( const object& after ) override; - event_id_type get_event_id() const { return ephemeral_event_object.id; } + map< event_id_type, event_object > ephemeral_event_object; }; -typedef object_id persistent_event_id_type; - -struct by_event_id; -typedef multi_index_container< - persistent_event_object, - indexed_by< - ordered_unique, member >, - ordered_unique, const_mem_fun > > > persistent_event_multi_index_type; -typedef generic_index persistent_event_index; - #if 0 // we no longer have competitors, just leaving this here as an example of how to do a secondary index class events_by_competitor_index : public secondary_index { @@ -101,95 +83,122 @@ void events_by_competitor_index::object_modified( const object& after ) } #endif -//////////// betting_market_group_object ////////////////// -class persistent_betting_market_group_object : public graphene::db::abstract_object +/** + * @brief This secondary index will allow a reverse lookup of all betting_market_group that happened + */ +class persistent_betting_market_group_index : public secondary_index { - public: - static const uint8_t space_id = bookie_objects; - static const uint8_t type_id = persistent_betting_market_group_object_type; +public: + struct internal_type + { + internal_type() = default; - betting_market_group_object ephemeral_betting_market_group_object; + internal_type(const betting_market_group_object& other) + : ephemeral_betting_market_group_object{other} + {} + + internal_type& operator=(const betting_market_group_object& other) + { + ephemeral_betting_market_group_object = other; + return *this; + } + friend bool operator==(const internal_type& lhs, const internal_type& rhs); + friend bool operator<(const internal_type& lhs, const internal_type& rhs); + friend bool operator>(const internal_type& lhs, const internal_type& rhs); + + betting_market_group_object ephemeral_betting_market_group_object; share_type total_matched_bets_amount; + }; - betting_market_group_id_type get_betting_market_group_id() const { return ephemeral_betting_market_group_object.id; } -}; +public: + virtual void object_inserted( const object& obj ) override; + virtual void object_modified( const object& after ) override; -struct by_betting_market_group_id; -typedef multi_index_container< - persistent_betting_market_group_object, - indexed_by< - ordered_unique, member >, - ordered_unique, const_mem_fun > > > persistent_betting_market_group_multi_index_type; + map< betting_market_group_id_type, internal_type > internal; +}; -typedef generic_index persistent_betting_market_group_index; +inline bool operator==(const persistent_betting_market_group_index::internal_type& lhs, const persistent_betting_market_group_index::internal_type& rhs) +{ + return lhs.ephemeral_betting_market_group_object == rhs.ephemeral_betting_market_group_object; +} -//////////// betting_market_object ////////////////// -class persistent_betting_market_object : public graphene::db::abstract_object +inline bool operator<(const persistent_betting_market_group_index::internal_type& lhs, const persistent_betting_market_group_index::internal_type& rhs) { - public: - static const uint8_t space_id = bookie_objects; - static const uint8_t type_id = persistent_betting_market_object_type; + return lhs.ephemeral_betting_market_group_object < rhs.ephemeral_betting_market_group_object; +} - betting_market_object ephemeral_betting_market_object; +inline bool operator>(const persistent_betting_market_group_index::internal_type& lhs, const persistent_betting_market_group_index::internal_type& rhs) +{ + return !operator<(lhs, rhs); +} - share_type total_matched_bets_amount; +/** + * @brief This secondary index will allow a reverse lookup of all betting_market_object that happened + */ +class persistent_betting_market_index : public secondary_index +{ +public: + virtual void object_inserted( const object& obj ) override; + virtual void object_modified( const object& after ) override; - betting_market_id_type get_betting_market_id() const { return ephemeral_betting_market_object.id; } + map< betting_market_id_type, betting_market_object > ephemeral_betting_market_object; }; -struct by_betting_market_id; -typedef multi_index_container< - persistent_betting_market_object, - indexed_by< - ordered_unique, member >, - ordered_unique, const_mem_fun > > > persistent_betting_market_multi_index_type; +/** + * @brief This secondary index will allow a reverse lookup of all bet_object that happened + */ +class persistent_bet_index : public secondary_index +{ +public: + struct internal_type + { + internal_type() = default; -typedef generic_index persistent_betting_market_index; + internal_type(const bet_object& other) + : ephemeral_bet_object{other} + {} -//////////// bet_object ////////////////// -class persistent_bet_object : public graphene::db::abstract_object -{ - public: - static const uint8_t space_id = bookie_objects; - static const uint8_t type_id = persistent_bet_object_type; + internal_type& operator=(const bet_object& other) + { + ephemeral_bet_object = other; + return *this; + } - bet_object ephemeral_bet_object; + account_id_type get_bettor_id() const { return ephemeral_bet_object.bettor_id; } + bool is_matched() const { return amount_matched != share_type(); } + + friend bool operator==(const internal_type& lhs, const internal_type& rhs); + friend bool operator<(const internal_type& lhs, const internal_type& rhs); + friend bool operator>(const internal_type& lhs, const internal_type& rhs); + bet_object ephemeral_bet_object; // total amount of the bet that matched share_type amount_matched; - std::vector associated_operations; + }; - bet_id_type get_bet_id() const { return ephemeral_bet_object.id; } - account_id_type get_bettor_id() const { return ephemeral_bet_object.bettor_id; } - bool is_matched() const { return amount_matched != share_type(); } +public: + virtual void object_inserted( const object& obj ) override; + virtual void object_modified( const object& after ) override; + + map< bet_id_type, internal_type > internal; }; -struct by_bet_id; -struct by_bettor_id; -typedef multi_index_container< - persistent_bet_object, - indexed_by< - ordered_unique, member >, - ordered_unique, const_mem_fun >, - ordered_unique, - composite_key< - persistent_bet_object, - const_mem_fun, - const_mem_fun, - const_mem_fun >, - composite_key_compare< - std::less, - std::less, - std::greater > > > > persistent_bet_multi_index_type; - -typedef generic_index persistent_bet_index; +inline bool operator==(const persistent_bet_index::internal_type& lhs, const persistent_bet_index::internal_type& rhs) +{ + return lhs.ephemeral_bet_object == rhs.ephemeral_bet_object; +} -} } } //graphene::bookie::detail +inline bool operator<(const persistent_bet_index::internal_type& lhs, const persistent_bet_index::internal_type& rhs) +{ + return lhs.ephemeral_bet_object < rhs.ephemeral_bet_object; +} -FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (ephemeral_event_object) ) -FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_group_object, (graphene::db::object), (ephemeral_betting_market_group_object)(total_matched_bets_amount) ) -FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_object, (graphene::db::object), (ephemeral_betting_market_object) ) -FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_bet_object, (graphene::db::object), (ephemeral_bet_object)(amount_matched)(associated_operations) ) +inline bool operator>(const persistent_bet_index::internal_type& lhs, const persistent_bet_index::internal_type& rhs) +{ + return !operator<(lhs, rhs); +} + +} } } //graphene::bookie::detail diff --git a/libraries/plugins/delayed_node/CMakeLists.txt b/libraries/plugins/delayed_node/CMakeLists.txt index d481eb84c..ab01b78db 100644 --- a/libraries/plugins/delayed_node/CMakeLists.txt +++ b/libraries/plugins/delayed_node/CMakeLists.txt @@ -4,7 +4,7 @@ add_library( graphene_delayed_node delayed_node_plugin.cpp ) -target_link_libraries( graphene_delayed_node PRIVATE graphene_plugin graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_market_history ) +target_link_libraries( graphene_delayed_node PRIVATE graphene_plugin graphene_accounts_list graphene_affiliate_stats graphene_bookie graphene_debug_witness graphene_elasticsearch graphene_market_history peerplays_sidechain ) target_include_directories( graphene_delayed_node PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp index 8777c06d0..491c011d4 100644 --- a/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp +++ b/libraries/plugins/elasticsearch/elasticsearch_plugin.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include #include #include #include @@ -33,6 +34,15 @@ namespace graphene { namespace elasticsearch { namespace detail { +const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix) +{ + auto block_date_string = block_date.to_iso_string(); + std::vector parts; + boost::split(parts, block_date_string, boost::is_any_of("-")); + std::string index_name = _elasticsearch_index_prefix + parts[0] + "-" + parts[1]; + return index_name; +} + class elasticsearch_plugin_impl { public: @@ -48,6 +58,9 @@ class elasticsearch_plugin_impl return _self.database(); } + friend class graphene::elasticsearch::elasticsearch_plugin; + + private: elasticsearch_plugin& _self; primary_index< operation_history_index >* _oho_index; @@ -75,6 +88,8 @@ class elasticsearch_plugin_impl std::string bulk_line; std::string index_name; bool is_sync = false; + bool is_es_version_7_or_above = true; + private: bool add_elasticsearch( const account_id_type account_id, const optional& oho, const uint32_t block_number ); const account_transaction_history_object& addNewEntry(const account_statistics_object& stats_obj, @@ -91,6 +106,7 @@ class elasticsearch_plugin_impl void createBulkLine(const account_transaction_history_object& ath); void prepareBulk(const account_transaction_history_id_type& ath_id); void populateESstruct(); + void init_program_options(const boost::program_options::variables_map& options); }; elasticsearch_plugin_impl::~elasticsearch_plugin_impl() @@ -105,7 +121,7 @@ elasticsearch_plugin_impl::~elasticsearch_plugin_impl() bool elasticsearch_plugin_impl::update_account_histories( const signed_block& b ) { checkState(b.timestamp); - index_name = graphene::utilities::generateIndexName(b.timestamp, _elasticsearch_index_prefix); + index_name = generateIndexName(b.timestamp, _elasticsearch_index_prefix); graphene::chain::database& db = database(); const vector >& hist = db.get_applied_operations(); @@ -229,6 +245,113 @@ void elasticsearch_plugin_impl::getOperationType(const optional op.which(); } +struct adaptor_struct +{ + variant adapt(const variant_object& op) + { + fc::mutable_variant_object o(op); + vector keys_to_rename; + for (auto& i : o) + { + auto& element = i.value(); + if (element.is_object()) + { + const string& name = i.key(); + const auto& vo = element.get_object(); + if (vo.contains(name.c_str())) + keys_to_rename.emplace_back(name); + element = adapt(vo); + } + else if (element.is_array()) + adapt(element.get_array()); + } + for (const auto& i : keys_to_rename) + { + string new_name = i + "_"; + o[new_name] = variant(o[i]); + o.erase(i); + } + + if (o.find("memo") != o.end()) + { + auto& memo = o["memo"]; + if (memo.is_string()) + { + o["memo_"] = o["memo"]; + o.erase("memo"); + } + else if (memo.is_object()) + { + fc::mutable_variant_object tmp(memo.get_object()); + if (tmp.find("nonce") != tmp.end()) + { + tmp["nonce"] = tmp["nonce"].as_string(); + o["memo"] = tmp; + } + } + } + if (o.find("new_parameters") != o.end()) + { + auto& tmp = o["new_parameters"]; + if (tmp.is_object()) + { + fc::mutable_variant_object tmp2(tmp.get_object()); + if (tmp2.find("current_fees") != tmp2.end()) + { + tmp2.erase("current_fees"); + o["new_parameters"] = tmp2; + } + } + } + if (o.find("owner") != o.end() && o["owner"].is_string()) + { + o["owner_"] = o["owner"].as_string(); + o.erase("owner"); + } + if (o.find("proposed_ops") != o.end()) + { + o["proposed_ops"] = fc::json::to_string(o["proposed_ops"]); + } + if (o.find("initializer") != o.end()) + { + o["initializer"] = fc::json::to_string(o["initializer"]); + } + if (o.find("policy") != o.end()) + { + o["policy"] = fc::json::to_string(o["policy"]); + } + if (o.find("predicates") != o.end()) + { + o["predicates"] = fc::json::to_string(o["predicates"]); + } + if (o.find("active_special_authority") != o.end()) + { + o["active_special_authority"] = fc::json::to_string(o["active_special_authority"]); + } + if (o.find("owner_special_authority") != o.end()) + { + o["owner_special_authority"] = fc::json::to_string(o["owner_special_authority"]); + } + + variant v; + fc::to_variant(o, v, FC_PACK_MAX_DEPTH); + return v; + } + + void adapt(fc::variants& v) + { + for (auto& array_element : v) + { + if (array_element.is_object()) + array_element = adapt(array_element.get_object()); + else if (array_element.is_array()) + adapt(array_element.get_array()); + else + array_element = array_element.as_string(); + } + } +}; + void elasticsearch_plugin_impl::doOperationHistory(const optional & oho) { os.trx_in_block = oho->trx_in_block; @@ -255,6 +378,61 @@ void elasticsearch_plugin_impl::doBlock(uint32_t trx_in_block, const signed_bloc bs.trx_id = trx_id; } +struct operation_visitor +{ + using result_type = void; + + share_type fee_amount; + asset_id_type fee_asset; + + asset_id_type transfer_asset_id; + share_type transfer_amount; + account_id_type transfer_from; + account_id_type transfer_to; + + void operator()( const graphene::chain::transfer_operation& o ) + { + fee_asset = o.fee.asset_id; + fee_amount = o.fee.amount; + + transfer_asset_id = o.amount.asset_id; + transfer_amount = o.amount.amount; + transfer_from = o.from; + transfer_to = o.to; + } + + object_id_type fill_order_id; + account_id_type fill_account_id; + asset_id_type fill_pays_asset_id; + share_type fill_pays_amount; + asset_id_type fill_receives_asset_id; + share_type fill_receives_amount; + //double fill_fill_price; + //bool fill_is_maker; + + void operator()( const graphene::chain::fill_order_operation& o ) + { + fee_asset = o.fee.asset_id; + fee_amount = o.fee.amount; + + fill_order_id = o.order_id; + fill_account_id = o.account_id; + fill_pays_asset_id = o.pays.asset_id; + fill_pays_amount = o.pays.amount; + fill_receives_asset_id = o.receives.asset_id; + fill_receives_amount = o.receives.amount; + //fill_fill_price = o.fill_price.to_real(); + //fill_is_maker = o.is_maker; + } + + template + void operator()( const T& o ) + { + fee_asset = o.fee.asset_id; + fee_amount = o.fee.amount; + } +}; + void elasticsearch_plugin_impl::doVisitor(const optional & oho) { graphene::chain::database& db = database(); @@ -380,7 +558,8 @@ void elasticsearch_plugin_impl::prepareBulk(const account_transaction_history_id const std::string _id = fc::json::to_string(ath_id); fc::mutable_variant_object bulk_header; bulk_header["_index"] = index_name; - bulk_header["_type"] = "data"; + if( !is_es_version_7_or_above ) + bulk_header["_type"] = "_doc"; bulk_header["_id"] = fc::to_string(ath_id.space_id) + "." + fc::to_string(ath_id.type_id) + "." + fc::to_string(ath_id.instance.value); prepare = graphene::utilities::createBulk(bulk_header, std::move(bulk_line)); @@ -428,6 +607,43 @@ void elasticsearch_plugin_impl::populateESstruct() es.query = ""; } +void elasticsearch_plugin_impl::init_program_options(const boost::program_options::variables_map& options) +{ + if (options.count("elasticsearch-node-url")) { + _elasticsearch_node_url = options["elasticsearch-node-url"].as(); + } + if (options.count("elasticsearch-bulk-replay")) { + _elasticsearch_bulk_replay = options["elasticsearch-bulk-replay"].as(); + } + if (options.count("elasticsearch-bulk-sync")) { + _elasticsearch_bulk_sync = options["elasticsearch-bulk-sync"].as(); + } + if (options.count("elasticsearch-visitor")) { + _elasticsearch_visitor = options["elasticsearch-visitor"].as(); + } + if (options.count("elasticsearch-basic-auth")) { + _elasticsearch_basic_auth = options["elasticsearch-basic-auth"].as(); + } + if (options.count("elasticsearch-index-prefix")) { + _elasticsearch_index_prefix = options["elasticsearch-index-prefix"].as(); + } + if (options.count("elasticsearch-operation-object")) { + _elasticsearch_operation_object = options["elasticsearch-operation-object"].as(); + } + if (options.count("elasticsearch-start-es-after-block")) { + _elasticsearch_start_es_after_block = options["elasticsearch-start-es-after-block"].as(); + } + if (options.count("elasticsearch-operation-string")) { + _elasticsearch_operation_string = options["elasticsearch-operation-string"].as(); + } + if (options.count("elasticsearch-mode")) { + const auto option_number = options["elasticsearch-mode"].as(); + if(option_number > mode::all) + FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Elasticsearch mode not valid"); + _elasticsearch_mode = static_cast(options["elasticsearch-mode"].as()); + } +} + } // end namespace detail elasticsearch_plugin::elasticsearch_plugin() : @@ -480,42 +696,12 @@ void elasticsearch_plugin::plugin_set_program_options( void elasticsearch_plugin::plugin_initialize(const boost::program_options::variables_map& options) { + ilog("elasticsearch ACCOUNT HISTORY: plugin_initialize() begin"); + my->_oho_index = database().add_index< primary_index< operation_history_index > >(); database().add_index< primary_index< account_transaction_history_index > >(); - if (options.count("elasticsearch-node-url")) { - my->_elasticsearch_node_url = options["elasticsearch-node-url"].as(); - } - if (options.count("elasticsearch-bulk-replay")) { - my->_elasticsearch_bulk_replay = options["elasticsearch-bulk-replay"].as(); - } - if (options.count("elasticsearch-bulk-sync")) { - my->_elasticsearch_bulk_sync = options["elasticsearch-bulk-sync"].as(); - } - if (options.count("elasticsearch-visitor")) { - my->_elasticsearch_visitor = options["elasticsearch-visitor"].as(); - } - if (options.count("elasticsearch-basic-auth")) { - my->_elasticsearch_basic_auth = options["elasticsearch-basic-auth"].as(); - } - if (options.count("elasticsearch-index-prefix")) { - my->_elasticsearch_index_prefix = options["elasticsearch-index-prefix"].as(); - } - if (options.count("elasticsearch-operation-object")) { - my->_elasticsearch_operation_object = options["elasticsearch-operation-object"].as(); - } - if (options.count("elasticsearch-start-es-after-block")) { - my->_elasticsearch_start_es_after_block = options["elasticsearch-start-es-after-block"].as(); - } - if (options.count("elasticsearch-operation-string")) { - my->_elasticsearch_operation_string = options["elasticsearch-operation-string"].as(); - } - if (options.count("elasticsearch-mode")) { - const auto option_number = options["elasticsearch-mode"].as(); - if(option_number > mode::all) - FC_THROW_EXCEPTION(graphene::chain::plugin_exception, "Elasticsearch mode not valid"); - my->_elasticsearch_mode = static_cast(options["elasticsearch-mode"].as()); - } + my->init_program_options( options ); if(my->_elasticsearch_mode != mode::only_query) { if (my->_elasticsearch_mode == mode::all && !my->_elasticsearch_operation_string) @@ -528,10 +714,7 @@ void elasticsearch_plugin::plugin_initialize(const boost::program_options::varia "Error populating ES database, we are going to keep trying."); }); } -} -void elasticsearch_plugin::plugin_startup() -{ graphene::utilities::ES es; es.curl = my->curl; es.elasticsearch_url = my->_elasticsearch_node_url; @@ -539,7 +722,17 @@ void elasticsearch_plugin::plugin_startup() if(!graphene::utilities::checkES(es)) FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_elasticsearch_node_url)); + + graphene::utilities::checkESVersion7OrAbove(es, my->is_es_version_7_or_above); + + ilog("elasticsearch ACCOUNT HISTORY: plugin_initialize() end"); +} + +void elasticsearch_plugin::plugin_startup() +{ ilog("elasticsearch ACCOUNT HISTORY: plugin_startup() begin"); + // Nothing to do + ilog("elasticsearch ACCOUNT HISTORY: plugin_startup() end"); } operation_history_object elasticsearch_plugin::get_operation_by_id(operation_history_id_type id) @@ -655,7 +848,7 @@ graphene::utilities::ES elasticsearch_plugin::prepareHistoryQuery(string query) es.curl = curl; es.elasticsearch_url = my->_elasticsearch_node_url; es.index_prefix = my->_elasticsearch_index_prefix; - es.endpoint = es.index_prefix + "*/data/_search"; + es.endpoint = es.index_prefix + "*/_doc/_search"; es.query = query; return es; diff --git a/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp b/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp index e1441c58e..18e9b2970 100644 --- a/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp +++ b/libraries/plugins/elasticsearch/include/graphene/elasticsearch/elasticsearch_plugin.hpp @@ -79,62 +79,6 @@ class elasticsearch_plugin : public graphene::app::plugin graphene::utilities::ES prepareHistoryQuery(string query); }; - -struct operation_visitor -{ - typedef void result_type; - - share_type fee_amount; - asset_id_type fee_asset; - - asset_id_type transfer_asset_id; - share_type transfer_amount; - account_id_type transfer_from; - account_id_type transfer_to; - - void operator()( const graphene::chain::transfer_operation& o ) - { - fee_asset = o.fee.asset_id; - fee_amount = o.fee.amount; - - transfer_asset_id = o.amount.asset_id; - transfer_amount = o.amount.amount; - transfer_from = o.from; - transfer_to = o.to; - } - - object_id_type fill_order_id; - account_id_type fill_account_id; - asset_id_type fill_pays_asset_id; - share_type fill_pays_amount; - asset_id_type fill_receives_asset_id; - share_type fill_receives_amount; - //double fill_fill_price; - //bool fill_is_maker; - - void operator()( const graphene::chain::fill_order_operation& o ) - { - fee_asset = o.fee.asset_id; - fee_amount = o.fee.amount; - - fill_order_id = o.order_id; - fill_account_id = o.account_id; - fill_pays_asset_id = o.pays.asset_id; - fill_pays_amount = o.pays.amount; - fill_receives_asset_id = o.receives.asset_id; - fill_receives_amount = o.receives.amount; - //fill_fill_price = o.fill_price.to_real(); - //fill_is_maker = o.is_maker; - } - - template - void operator()( const T& o ) - { - fee_asset = o.fee.asset_id; - fee_amount = o.fee.amount; - } -}; - struct operation_history_struct { int trx_in_block; int op_in_trx; @@ -197,113 +141,6 @@ struct bulk_struct { optional additional_data; }; -struct adaptor_struct { - variant adapt(const variant_object& op) - { - fc::mutable_variant_object o(op); - vector keys_to_rename; - for (auto i = o.begin(); i != o.end(); ++i) - { - auto& element = (*i).value(); - if (element.is_object()) - { - const string& name = (*i).key(); - auto& vo = element.get_object(); - if (vo.contains(name.c_str())) - keys_to_rename.emplace_back(name); - element = adapt(vo); - } - else if (element.is_array()) - adapt(element.get_array()); - } - for (const auto& i : keys_to_rename) - { - string new_name = i + "_"; - o[new_name] = variant(o[i]); - o.erase(i); - } - - if (o.find("memo") != o.end()) - { - auto& memo = o["memo"]; - if (memo.is_string()) - { - o["memo_"] = o["memo"]; - o.erase("memo"); - } - else if (memo.is_object()) - { - fc::mutable_variant_object tmp(memo.get_object()); - if (tmp.find("nonce") != tmp.end()) - { - tmp["nonce"] = tmp["nonce"].as_string(); - o["memo"] = tmp; - } - } - } - if (o.find("new_parameters") != o.end()) - { - auto& tmp = o["new_parameters"]; - if (tmp.is_object()) - { - fc::mutable_variant_object tmp2(tmp.get_object()); - if (tmp2.find("current_fees") != tmp2.end()) - { - tmp2.erase("current_fees"); - o["new_parameters"] = tmp2; - } - } - } - if (o.find("owner") != o.end() && o["owner"].is_string()) - { - o["owner_"] = o["owner"].as_string(); - o.erase("owner"); - } - if (o.find("proposed_ops") != o.end()) - { - o["proposed_ops"] = fc::json::to_string(o["proposed_ops"]); - } - if (o.find("initializer") != o.end()) - { - o["initializer"] = fc::json::to_string(o["initializer"]); - } - if (o.find("policy") != o.end()) - { - o["policy"] = fc::json::to_string(o["policy"]); - } - if (o.find("predicates") != o.end()) - { - o["predicates"] = fc::json::to_string(o["predicates"]); - } - if (o.find("active_special_authority") != o.end()) - { - o["active_special_authority"] = fc::json::to_string(o["active_special_authority"]); - } - if (o.find("owner_special_authority") != o.end()) - { - o["owner_special_authority"] = fc::json::to_string(o["owner_special_authority"]); - } - - - variant v; - fc::to_variant(o, v, FC_PACK_MAX_DEPTH); - return v; - } - - void adapt(fc::variants& v) - { - for (auto& array_element : v) - { - if (array_element.is_object()) - array_element = adapt(array_element.get_object()); - else if (array_element.is_array()) - adapt(array_element.get_array()); - else - array_element = array_element.as_string(); - } - } -}; - } } //graphene::elasticsearch FC_REFLECT_ENUM( graphene::elasticsearch::mode, (only_save)(only_query)(all) ) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index 1e09116ad..a2261d6ac 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -66,6 +66,9 @@ class es_objects_plugin_impl bool genesis(); void remove_from_database(object_id_type id, std::string index); + friend class graphene::es_objects::es_objects_plugin; + + private: es_objects_plugin& _self; std::string _es_objects_elasticsearch_url = "http://localhost:9200/"; std::string _es_objects_auth = ""; @@ -97,10 +100,12 @@ class es_objects_plugin_impl uint32_t block_number; fc::time_point_sec block_time; + bool is_es_version_7_or_above = true; private: template void prepareTemplate(T blockchain_object, string index_name); + void init_program_options(const boost::program_options::variables_map& options); }; bool es_objects_plugin_impl::genesis() @@ -523,7 +528,8 @@ void es_objects_plugin_impl::remove_from_database( object_id_type id, std::strin fc::mutable_variant_object delete_line; delete_line["_id"] = string(id); delete_line["_index"] = _es_objects_index_prefix + index; - delete_line["_type"] = "data"; + if( !is_es_version_7_or_above ) + delete_line["_type"] = "_doc"; fc::mutable_variant_object final_delete_line; final_delete_line["delete"] = delete_line; prepare.push_back(fc::json::to_string(final_delete_line)); @@ -537,7 +543,8 @@ void es_objects_plugin_impl::prepareTemplate(T blockchain_object, string index_n { fc::mutable_variant_object bulk_header; bulk_header["_index"] = _es_objects_index_prefix + index_name; - bulk_header["_type"] = "data"; + if( !is_es_version_7_or_above ) + bulk_header["_type"] = "_doc"; if(_es_objects_keep_only_current) { bulk_header["_id"] = string(blockchain_object.id); @@ -567,6 +574,72 @@ es_objects_plugin_impl::~es_objects_plugin_impl() } return; } +void es_objects_plugin_impl::init_program_options(const boost::program_options::variables_map& options) +{ + if (options.count("es-objects-elasticsearch-url")) { + _es_objects_elasticsearch_url = options["es-objects-elasticsearch-url"].as(); + } + if (options.count("es-objects-auth")) { + _es_objects_auth = options["es-objects-auth"].as(); + } + if (options.count("es-objects-bulk-replay")) { + _es_objects_bulk_replay = options["es-objects-bulk-replay"].as(); + } + if (options.count("es-objects-bulk-sync")) { + _es_objects_bulk_sync = options["es-objects-bulk-sync"].as(); + } + if (options.count("es-objects-proposals")) { + _es_objects_proposals = options["es-objects-proposals"].as(); + } + if (options.count("es-objects-accounts")) { + _es_objects_accounts = options["es-objects-accounts"].as(); + } + if (options.count("es-objects-assets")) { + _es_objects_assets = options["es-objects-assets"].as(); + } + if (options.count("es-objects-balances")) { + _es_objects_balances = options["es-objects-balances"].as(); + } + if (options.count("es-objects-limit-orders")) { + _es_objects_limit_orders = options["es-objects-limit-orders"].as(); + } + if (options.count("es-objects-asset-bitasset")) { + _es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as(); + } + if (options.count("es-objects-account-role")) { + _es_objects_balances = options["es-objects-account-role"].as(); + } + if (options.count("es-objects-committee-member")) { + _es_objects_balances = options["es-objects-committee-member"].as(); + } + if (options.count("es-objects-nft")) { + _es_objects_balances = options["es-objects-nft"].as(); + } + if (options.count("es-objects-son")) { + _es_objects_balances = options["es-objects-son"].as(); + } + if (options.count("es-objects-transaction")) { + _es_objects_balances = options["es-objects-transaction"].as(); + } + if (options.count("es-objects-vesting-balance")) { + _es_objects_balances = options["es-objects-vesting-balance"].as(); + } + if (options.count("es-objects-witness")) { + _es_objects_balances = options["es-objects-witness"].as(); + } + if (options.count("es-objects-worker")) { + _es_objects_balances = options["es-objects-worker"].as(); + } + if (options.count("es-objects-index-prefix")) { + _es_objects_index_prefix = options["es-objects-index-prefix"].as(); + } + if (options.count("es-objects-keep-only-current")) { + _es_objects_keep_only_current = options["es-objects-keep-only-current"].as(); + } + if (options.count("es-objects-start-es-after-block")) { + _es_objects_start_es_after_block = options["es-objects-start-es-after-block"].as(); + } +} } // end namespace detail @@ -627,69 +700,9 @@ void es_objects_plugin::plugin_set_program_options( void es_objects_plugin::plugin_initialize(const boost::program_options::variables_map& options) { - if (options.count("es-objects-elasticsearch-url")) { - my->_es_objects_elasticsearch_url = options["es-objects-elasticsearch-url"].as(); - } - if (options.count("es-objects-auth")) { - my->_es_objects_auth = options["es-objects-auth"].as(); - } - if (options.count("es-objects-bulk-replay")) { - my->_es_objects_bulk_replay = options["es-objects-bulk-replay"].as(); - } - if (options.count("es-objects-bulk-sync")) { - my->_es_objects_bulk_sync = options["es-objects-bulk-sync"].as(); - } - if (options.count("es-objects-proposals")) { - my->_es_objects_proposals = options["es-objects-proposals"].as(); - } - if (options.count("es-objects-accounts")) { - my->_es_objects_accounts = options["es-objects-accounts"].as(); - } - if (options.count("es-objects-assets")) { - my->_es_objects_assets = options["es-objects-assets"].as(); - } - if (options.count("es-objects-balances")) { - my->_es_objects_balances = options["es-objects-balances"].as(); - } - if (options.count("es-objects-limit-orders")) { - my->_es_objects_limit_orders = options["es-objects-limit-orders"].as(); - } - if (options.count("es-objects-asset-bitasset")) { - my->_es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as(); - } - if (options.count("es-objects-account-role")) { - my->_es_objects_balances = options["es-objects-account-role"].as(); - } - if (options.count("es-objects-committee-member")) { - my->_es_objects_balances = options["es-objects-committee-member"].as(); - } - if (options.count("es-objects-nft")) { - my->_es_objects_balances = options["es-objects-nft"].as(); - } - if (options.count("es-objects-son")) { - my->_es_objects_balances = options["es-objects-son"].as(); - } - if (options.count("es-objects-transaction")) { - my->_es_objects_balances = options["es-objects-transaction"].as(); - } - if (options.count("es-objects-vesting-balance")) { - my->_es_objects_balances = options["es-objects-vesting-balance"].as(); - } - if (options.count("es-objects-witness")) { - my->_es_objects_balances = options["es-objects-witness"].as(); - } - if (options.count("es-objects-worker")) { - my->_es_objects_balances = options["es-objects-worker"].as(); - } - if (options.count("es-objects-index-prefix")) { - my->_es_objects_index_prefix = options["es-objects-index-prefix"].as(); - } - if (options.count("es-objects-keep-only-current")) { - my->_es_objects_keep_only_current = options["es-objects-keep-only-current"].as(); - } - if (options.count("es-objects-start-es-after-block")) { - my->_es_objects_start_es_after_block = options["es-objects-start-es-after-block"].as(); - } + ilog("elasticsearch OBJECTS: plugin_initialize() begin"); + + my->init_program_options( options ); database().applied_block.connect([this](const signed_block &b) { if(b.block_num() == 1 && my->_es_objects_start_es_after_block == 0) { @@ -721,10 +734,7 @@ void es_objects_plugin::plugin_initialize(const boost::program_options::variable "Error deleting object from ES database, we are going to keep trying."); } }); -} -void es_objects_plugin::plugin_startup() -{ graphene::utilities::ES es; es.curl = my->curl; es.elasticsearch_url = my->_es_objects_elasticsearch_url; @@ -733,7 +743,17 @@ void es_objects_plugin::plugin_startup() if(!graphene::utilities::checkES(es)) FC_THROW_EXCEPTION(fc::exception, "ES database is not up in url ${url}", ("url", my->_es_objects_elasticsearch_url)); - ilog("elasticsearch OBJECTS: plugin_startup() begin"); + + graphene::utilities::checkESVersion7OrAbove(es, my->is_es_version_7_or_above); + + ilog("elasticsearch OBJECTS: plugin_initialize() end"); +} + +void es_objects_plugin::plugin_startup() +{ + ilog("elasticsearch OBJECTS: plugin_startup() begin"); + // Nothing to do + ilog("elasticsearch OBJECTS: plugin_startup() end"); } } } \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index bb74df29f..9ea2ce347 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -2,6 +2,7 @@ file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp") add_library( peerplays_sidechain peerplays_sidechain_plugin.cpp + sidechain_api.cpp sidechain_net_manager.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp @@ -23,19 +24,11 @@ add_library( peerplays_sidechain ) if (ENABLE_DEV_FEATURES) - set(ENABLE_MULTIPLE_SONS 1) set(ENABLE_PEERPLAYS_ASSET_DEPOSITS 1) endif() unset(ENABLE_DEV_FEATURES) unset(ENABLE_DEV_FEATURES CACHE) -if (ENABLE_MULTIPLE_SONS) - message ("Multiple SONs per software instance are supported") - target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_MULTIPLE_SONS) -endif() -unset(ENABLE_MULTIPLE_SONS) -unset(ENABLE_MULTIPLE_SONS CACHE) - if (ENABLE_PEERPLAYS_ASSET_DEPOSITS) message ("Depositing Peerplays assets enabled") target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_PEERPLAYS_ASSET_DEPOSITS) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 6adfe9442..92591d0af 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -51,6 +51,7 @@ class peerplays_sidechain_plugin : public graphene::app::plugin { fc::ecc::private_key get_private_key(chain::public_key_type public_key); void log_son_proposal_retry(int op_type, object_id_type object_id); bool can_son_participate(int op_type, object_id_type object_id); + std::map> get_son_listener_log(); }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp new file mode 100644 index 000000000..b4636537f --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace graphene { namespace app { +class application; +}} // namespace graphene::app + +namespace graphene { namespace peerplays_sidechain { + +namespace detail { +class sidechain_api_impl; +} + +class sidechain_api { +public: + sidechain_api(app::application &_app); + virtual ~sidechain_api(); + + std::shared_ptr my; + + std::map> get_son_listener_log(); +}; + +}} // namespace graphene::peerplays_sidechain + +FC_API(graphene::peerplays_sidechain::sidechain_api, + (get_son_listener_log)) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index 21526d055..3aa4465cf 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -47,6 +47,9 @@ class sidechain_net_handler { virtual std::string send_sidechain_transaction(const sidechain_transaction_object &sto) = 0; virtual bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount) = 0; + void add_to_son_listener_log(std::string trx_id); + std::vector get_son_listener_log(); + protected: peerplays_sidechain_plugin &plugin; graphene::chain::database &database; @@ -56,6 +59,8 @@ class sidechain_net_handler { std::map private_keys; + std::vector son_listener_log; + void on_applied_block(const signed_block &b); private: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 9b08bc159..9c067e0e1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include @@ -22,6 +22,23 @@ class btc_txout { }; class bitcoin_rpc_client { +public: + enum class multi_type { + script, + address + }; + struct multi_params { + multi_params(multi_type _type, const std::string &_address_or_script, const std::string &_label = "") : + type{_type}, + address_or_script{_address_or_script}, + label{_label} { + } + + multi_type type; + std::string address_or_script; + std::string label; + }; + public: bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password, bool _debug_rpc_calls); @@ -39,18 +56,20 @@ class bitcoin_rpc_client { std::string getaddressinfo(const std::string &address); std::string getblock(const std::string &block_hash, int32_t verbosity = 2); std::string getrawtransaction(const std::string &txid, const bool verbose = false); + std::string getnetworkinfo(); std::string gettransaction(const std::string &txid, const bool include_watch_only = false); std::string getblockchaininfo(); void importaddress(const std::string &address_or_script, const std::string &label = "", const bool rescan = true, const bool p2sh = false); + void importmulti(const std::vector &address_or_script_array, const bool rescan = true); std::vector listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999); std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999); std::string loadwallet(const std::string &filename); std::string sendrawtransaction(const std::string &tx_hex); std::string signrawtransactionwithwallet(const std::string &tx_hash); std::string unloadwallet(const std::string &filename); - //std::string walletlock(); + std::string walletlock(); std::string walletprocesspsbt(std::string const &tx_psbt); - //bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); + bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); private: fc::http::reply send_post_request(std::string body, bool show_log); @@ -105,6 +124,7 @@ class sidechain_net_handler_bitcoin : public sidechain_net_handler { std::string ip; uint32_t zmq_port; uint32_t rpc_port; + uint32_t bitcoin_major_version; std::string rpc_user; std::string rpc_password; std::string wallet; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index a35c19336..8bfda125a 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -25,6 +25,8 @@ class sidechain_net_manager { void send_sidechain_transactions(); void settle_sidechain_transactions(); + std::map> get_son_listener_log(); + private: peerplays_sidechain_plugin &plugin; graphene::chain::database &database; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 67a1783f9..ed80fbfc2 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,7 @@ class peerplays_sidechain_plugin_impl { fc::ecc::private_key get_private_key(chain::public_key_type public_key); void log_son_proposal_retry(int op_type, object_id_type object_id); bool can_son_participate(int op_type, object_id_type object_id); + std::map> get_son_listener_log(); void schedule_heartbeat_loop(); void heartbeat_loop(); @@ -176,12 +178,6 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt } config_ready_son = config_ready_son && !sons.empty(); -#ifndef ENABLE_MULTIPLE_SONS - if (sons.size() > 1) { - FC_THROW("Invalid configuration, multiple SON IDs provided"); - } -#endif - if (options.count("peerplays-private-key")) { const std::vector key_id_to_wif_pair_strings = options["peerplays-private-key"].as>(); for (const std::string &key_id_to_wif_pair_string : key_id_to_wif_pair_strings) { @@ -534,6 +530,10 @@ bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id return (itr == son_retry_count.end() || itr->second < retries_threshold); } +std::map> peerplays_sidechain_plugin_impl::get_son_listener_log() { + return net_manager->get_son_listener_log(); +} + void peerplays_sidechain_plugin_impl::approve_proposals() { auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { @@ -794,4 +794,8 @@ bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type return my->can_son_participate(op_type, object_id); } +std::map> peerplays_sidechain_plugin::get_son_listener_log() { + return my->get_son_listener_log(); +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_api.cpp b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp new file mode 100644 index 000000000..2a85d0347 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp @@ -0,0 +1,48 @@ +#include + +namespace graphene { namespace peerplays_sidechain { + +namespace detail { + +class sidechain_api_impl { +public: + sidechain_api_impl(app::application &app); + virtual ~sidechain_api_impl(); + + std::shared_ptr get_plugin(); + + std::map> get_son_listener_log(); + +private: + app::application &app; +}; + +sidechain_api_impl::sidechain_api_impl(app::application &_app) : + app(_app) { +} + +sidechain_api_impl::~sidechain_api_impl() { +} + +std::shared_ptr sidechain_api_impl::get_plugin() { + return app.get_plugin("peerplays_sidechain"); +} + +std::map> sidechain_api_impl::get_son_listener_log() { + return get_plugin()->get_son_listener_log(); +} + +} // namespace detail + +sidechain_api::sidechain_api(graphene::app::application &_app) : + my(std::make_shared(_app)) { +} + +sidechain_api::~sidechain_api() { +} + +std::map> sidechain_api::get_son_listener_log() { + return my->get_son_listener_log(); +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 06a765025..45fab4eea 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -589,7 +589,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { if (settle_amount.amount != 0) { if (sto.object_id.is()) { asset_issue_operation ai_op; - ai_op.fee = asset(2001000); + ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op); ai_op.issuer = gpo.parameters.son_account(); ai_op.asset_to_issue = settle_amount; ai_op.issue_to_account = database.get(sto.object_id).peerplays_from; @@ -598,7 +598,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { if (sto.object_id.is()) { asset_reserve_operation ar_op; - ar_op.fee = asset(2001000); + ar_op.fee = database.current_fee_schedule().calculate_fee(ar_op); ar_op.payer = gpo.parameters.son_account(); ar_op.amount_to_reserve = settle_amount; proposal_op.proposed_ops.emplace_back(ar_op); @@ -618,6 +618,17 @@ void sidechain_net_handler::settle_sidechain_transactions() { }); } +void sidechain_net_handler::add_to_son_listener_log(std::string trx_id) { + son_listener_log.insert(son_listener_log.begin(), trx_id); + if (son_listener_log.size() > 33) { + son_listener_log.erase(son_listener_log.end()); + } +} + +std::vector sidechain_net_handler::get_son_listener_log() { + return son_listener_log; +} + void sidechain_net_handler::on_applied_block(const signed_block &b) { const chain::global_property_object &gpo = plugin.database().get_global_properties(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 1ad0d9c8d..583effdeb 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -41,534 +41,690 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::stri std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, const std::vector public_keys) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", " "\"method\": \"addmultisigaddress\", \"params\": ["); - std::string params = std::to_string(nrequired) + ", ["; - std::string pubkeys = ""; - for (std::string pubkey : public_keys) { - if (!pubkeys.empty()) { - pubkeys = pubkeys + ","; + try { + std::string params = std::to_string(nrequired) + ", ["; + std::string pubkeys = ""; + for (std::string pubkey : public_keys) { + if (!pubkeys.empty()) { + pubkeys = pubkeys + ","; + } + pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); } - pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); - } - params = params + pubkeys + std::string("]"); - body = body + params + std::string(", null, \"bech32\"] }"); + params = params + pubkeys + std::string("]"); + body = body + params + std::string(", null, \"bech32\"] }"); - const auto reply = send_post_request(body, debug_rpc_calls); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return ss.str(); - } + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::combinepsbt(const vector &psbts) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"combinepsbt\", \"method\": " "\"combinepsbt\", \"params\": [["); - std::string params = ""; - for (std::string psbt : psbts) { - if (!params.empty()) { - params = params + ","; + try { + std::string params = ""; + for (std::string psbt : psbts) { + if (!params.empty()) { + params = params + ","; + } + params = params + std::string("\"") + psbt + std::string("\""); } - params = params + std::string("\"") + psbt + std::string("\""); - } - body = body + params + std::string("]] }"); - const auto reply = send_post_request(body, debug_rpc_calls); + body = body + params + std::string("]] }"); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::createmultisig(const uint32_t nrequired, const std::vector public_keys) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createmultisig\", " "\"method\": \"createmultisig\", \"params\": ["); - std::string params = std::to_string(nrequired) + ", ["; - std::string pubkeys = ""; - for (std::string pubkey : public_keys) { - if (!pubkeys.empty()) { - pubkeys = pubkeys + ","; + try { + std::string params = std::to_string(nrequired) + ", ["; + std::string pubkeys = ""; + for (std::string pubkey : public_keys) { + if (!pubkeys.empty()) { + pubkeys = pubkeys + ","; + } + pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); } - pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); - } - params = params + pubkeys + std::string("]"); - body = body + params + std::string(", \"p2sh-segwit\" ] }"); + params = params + pubkeys + std::string("]"); + body = body + params + std::string(", \"p2sh-segwit\" ] }"); - const auto reply = send_post_request(body, debug_rpc_calls); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return ss.str(); - } + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::createpsbt(const std::vector &ins, const fc::flat_map outs) { std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", " "\"method\": \"createpsbt\", \"params\": ["); - body += "["; - bool first = true; - for (const auto &entry : ins) { - if (!first) - body += ","; - body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; - first = false; - } - body += "],["; - first = true; - for (const auto &entry : outs) { - if (!first) - body += ","; - body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; - first = false; - } - body += std::string("]] }"); - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + try { + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.status == 200) { - if (json.find("result") != json.not_found()) { - return json.get("result"); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + return json.get("result"); + } } - } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " "\"method\": \"createrawtransaction\", \"params\": ["); - body += "["; - bool first = true; - for (const auto &entry : ins) { - if (!first) - body += ","; - body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; - first = false; - } - body += "],["; - first = true; - for (const auto &entry : outs) { - if (!first) - body += ","; - body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; - first = false; - } - body += std::string("]] }"); - - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + try { + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.status == 200) { - if (json.find("result") != json.not_found()) { - return json.get("result"); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; } - } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + return json.get("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createwallet\", \"method\": " "\"createwallet\", \"params\": [\"" + wallet_name + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decodepsbt\", \"method\": " "\"decodepsbt\", \"params\": [\"" + tx_psbt + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decoderawtransaction\", \"method\": " "\"decoderawtransaction\", \"params\": [\"" + tx_hex + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"encryptwallet\", \"method\": " "\"encryptwallet\", \"params\": [\"" + passphrase + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimatesmartfee\", " "\"method\": \"estimatesmartfee\", \"params\": [" + std::to_string(conf_target) + std::string("] }")); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return 0; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return 0; + } - if (reply.status == 200) { - if (json.find("result") != json.not_found()) { - auto json_result = json.get_child("result"); - if (json_result.find("feerate") != json_result.not_found()) { - auto feerate_str = json_result.get("feerate"); - feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); - return std::stoll(feerate_str); - } + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.find("feerate") != json_result.not_found()) { + auto feerate_str = json_result.get("feerate"); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); + } - if (json_result.find("errors") != json_result.not_found()) { - wlog("Bitcoin RPC call ${function} with body ${body} executed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json_result.find("errors") != json_result.not_found()) { + wlog("Bitcoin RPC call ${function} with body ${body} executed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } } } - } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return 20000; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return 20000; } - return 20000; } std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": " "\"finalizepsbt\", \"params\": [\"" + tx_psbt + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return ss.str(); - } + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getaddressinfo\", \"method\": " "\"getaddressinfo\", \"params\": [\"" + address + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblock\", \"method\": " "\"getblock\", \"params\": [\"" + block_hash + "\", " + std::to_string(verbosity) + "] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); return ""; } +} - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); +std::string bitcoin_rpc_client::getnetworkinfo() { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getnetworkinfo\", \"method\": " + "\"getnetworkinfo\", \"params\": [] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getrawtransaction\", \"method\": " "\"getrawtransaction\", \"params\": ["); + try { + std::string params = "\"" + txid + "\", " + (verbose ? "true" : "false"); + body = body + params + "] }"; - std::string params = "\"" + txid + "\", " + (verbose ? "true" : "false"); - body = body + params + "] }"; - - const auto reply = send_post_request(body, debug_rpc_calls); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return ss.str(); - } + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": " "\"gettransaction\", \"params\": ["); + try { + std::string params = "\"" + txid + "\", " + (include_watch_only ? "true" : "false"); + body = body + params + "] }"; - std::string params = "\"" + txid + "\", " + (include_watch_only ? "true" : "false"); - body = body + params + "] }"; - - const auto reply = send_post_request(body, debug_rpc_calls); + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return ss.str(); - } + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::getblockchaininfo() { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblockchaininfo\", \"method\": " "\"getblockchaininfo\", \"params\": [] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } void bitcoin_rpc_client::importaddress(const std::string &address_or_script, const std::string &label, const bool rescan, const bool p2sh) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", " "\"method\": \"importaddress\", \"params\": ["); + try { + std::string params = "\"" + address_or_script + "\", " + + "\"" + label + "\", " + + (rescan ? "true" : "false") + ", " + + (p2sh ? "true" : "false"); + body = body + params + "] }"; - std::string params = "\"" + address_or_script + "\", " + - "\"" + label + "\", " + - (rescan ? "true" : "false") + ", " + - (p2sh ? "true" : "false"); - body = body + params + "] }"; + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return; + } - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return; + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return; + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); } +} - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); +void bitcoin_rpc_client::importmulti(const std::vector &address_or_script_array, const bool rescan) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importmulti\", " + "\"method\": \"importmulti\", \"params\": ["); + try { + std::string argument_1 = "["; + for (const auto ¶m : address_or_script_array) { + argument_1 += "{\"scriptPubKey\": "; + if (param.type == multi_type::address) { + argument_1 += "{\"address\": \"" + param.address_or_script + "\"}, \"label\": \"" + param.label + "\""; + } else if (param.type == multi_type::script) { + argument_1 += "\"" + param.address_or_script + "\", \"internal\": true"; + } else { + FC_THROW("Invalid multi_type."); + } + argument_1 += ", \"timestamp\": " + std::to_string(fc::time_point_sec::from_iso_string("2022-01-01T00:00:00").sec_since_epoch()) + "}"; + + //! Note + /* Creation time of the key expressed in UNIX epoch time, + or the string "now" to substitute the current synced blockchain time. The timestamp of the oldest + key will determine how far back blockchain rescans need to begin for missing wallet transactions. + "now" can be specified to bypass scanning, for keys which are known to never have been used, and + 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key + creation time of all keys being imported by the importmulti call will be scanned.*/ + + if (¶m != &address_or_script_array.back()) { + argument_1 += ", "; + } + } + argument_1 += "]"; - if (reply.status == 200) { - return; - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + std::string argument_2 = std::string{"{\"rescan\": "} + (rescan ? "true" : "false") + "}"; + body += argument_1 + ", " + argument_2 + "]}"; + + const auto reply = send_post_request(body, debug_rpc_calls); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return; + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); } } @@ -577,35 +733,39 @@ std::vector bitcoin_rpc_client::listunspent(const uint32_t minconf, c "\"listunspent\", \"params\": [" + std::to_string(minconf) + "," + std::to_string(maxconf) + "] }"); - const auto reply = send_post_request(body, debug_rpc_calls); - std::vector result; + try { + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return result; - } - - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return result; + } - if (reply.status == 200) { - if (json.count("result")) { - for (auto &entry : json.get_child("result")) { - btc_txout txo; - txo.txid_ = entry.second.get_child("txid").get_value(); - txo.out_num_ = entry.second.get_child("vout").get_value(); - string amount = entry.second.get_child("amount").get_value(); - amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); - txo.amount_ = std::stoll(amount); - result.push_back(txo); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + string amount = entry.second.get_child("amount").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + txo.amount_ = std::stoll(amount); + result.push_back(txo); + } } + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + return result; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return result; } - return result; } std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount, const uint32_t minconf, const uint32_t maxconf) { @@ -618,88 +778,102 @@ std::vector bitcoin_rpc_client::listunspent_by_address_and_amount(con body += std::to_string(minimum_amount); body += std::string("} ] }"); - const auto reply = send_post_request(body, debug_rpc_calls); - std::vector result; - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return result; - } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.status == 200) { - if (json.count("result")) { - for (auto &entry : json.get_child("result")) { - btc_txout txo; - txo.txid_ = entry.second.get_child("txid").get_value(); - txo.out_num_ = entry.second.get_child("vout").get_value(); - string amount = entry.second.get_child("amount").get_value(); - amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); - txo.amount_ = std::stoll(amount); - result.push_back(txo); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return result; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.count("result")) { + for (auto &entry : json.get_child("result")) { + btc_txout txo; + txo.txid_ = entry.second.get_child("txid").get_value(); + txo.out_num_ = entry.second.get_child("vout").get_value(); + string amount = entry.second.get_child("amount").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + txo.amount_ = std::stoll(amount); + result.push_back(txo); + } } + } else if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } - } else if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + return result; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return result; } - return result; } std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"loadwallet\", \"method\": " "\"loadwallet\", \"params\": [\"" + filename + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " "\"method\": \"sendrawtransaction\", \"params\": [") + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); - - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return json.get("result"); - } + if (reply.status == 200) { + return json.get("result"); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) { @@ -708,134 +882,155 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string & std::string params = "\"" + tx_hash + "\""; body = body + params + std::string("]}"); - const auto reply = send_post_request(body, debug_rpc_calls); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return ""; - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (reply.status == 200) { - return ss.str(); - } + if (reply.status == 200) { + return ss.str(); + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"unloadwallet\", \"method\": " "\"unloadwallet\", \"params\": [\"" + filename + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - const auto reply = send_post_request(body, debug_rpc_calls); + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); return ""; } +} - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); +std::string bitcoin_rpc_client::walletlock() { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": " + "\"walletlock\", \"params\": [] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.status == 200) { - std::stringstream ss; - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return ""; } - return ""; } -//std::string bitcoin_rpc_client::walletlock() { -// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": " -// "\"walletlock\", \"params\": [] }"); -// -// const auto reply = send_post_request(body, debug_rpc_calls); -// -// if (reply.body.empty()) { -// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); -// return ""; -// } -// -// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); -// boost::property_tree::ptree json; -// boost::property_tree::read_json(ss, json); -// -// if (reply.status == 200) { -// std::stringstream ss; -// boost::property_tree::json_parser::write_json(ss, json.get_child("result")); -// return ss.str(); -// } -// -// if (json.count("error") && !json.get_child("error").empty()) { -// wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); -// } -// return ""; -//} - std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": " "\"walletprocesspsbt\", \"params\": [\"" + tx_psbt + "\"] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } - const auto reply = send_post_request(body, debug_rpc_calls); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } - if (reply.body.empty()) { - wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); return ""; } +} - std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); +bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " + "\"walletpassphrase\", \"params\": [\"" + + passphrase + "\", " + std::to_string(timeout) + "] }"); + try { + const auto reply = send_post_request(body, debug_rpc_calls); - if (reply.status == 200) { - return ss.str(); - } + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return false; + } - if (json.count("error") && !json.get_child("error").empty()) { - wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return true; + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return false; + } catch (const boost::exception &ex) { + wlog("Bitcoin RPC call ${function} with body ${body} generate exception: '${exception}'", ("function", __FUNCTION__)("body", body)("exception", boost::diagnostic_information(ex))); + return false; } - return ""; } -//bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { -// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " -// "\"walletpassphrase\", \"params\": [\"" + -// passphrase + "\", " + std::to_string(timeout) + "] }"); -// -// const auto reply = send_post_request(body, debug_rpc_calls); -// -// if (reply.body.empty()) { -// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); -// return false; -// } -// -// std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); -// boost::property_tree::ptree json; -// boost::property_tree::read_json(ss, json); -// -// if (reply.status == 200) { -// return true; -// } -// -// if (json.count("error") && !json.get_child("error").empty()) { -// wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); -// } -// return false; -//} - fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool show_log) { fc::http::connection conn; conn.connect_to(fc::ip::endpoint(fc::ip::address(ip), rpc_port)); @@ -849,10 +1044,11 @@ fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool sho fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); if (show_log) { - ilog("### Request URL: ${url}", ("url", url)); - ilog("### Request: ${body}", ("body", body)); + ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request: ${body}", ("body", body)); std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - ilog("### Response: ${ss}", ("ss", ss.str())); + ilog("### Response status: ${status}", ("status", reply.status)); + ilog("### Response: ${ss}", ("ss", ss.str())); } return reply; @@ -871,16 +1067,11 @@ zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : std::vector zmq_listener::receive_multipart() { std::vector msgs; - int32_t more; - size_t more_size = sizeof(more); - while (true) { - zmq::message_t msg; - socket.recv(&msg, 0); - socket.getsockopt(ZMQ_RCVMORE, &more, &more_size); - - if (!more) - break; - msgs.push_back(std::move(msg)); + auto res = zmq::recv_multipart(socket, std::back_inserter(msgs)); + FC_ASSERT(res); + if (3 != *res) { + elog("zmq::recv_multipart returned: ${res}", ("res", *res)); + throw zmq::error_t(); } return msgs; @@ -902,6 +1093,7 @@ void zmq_listener::handle_zmq() { const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); event_received(block_hash); } catch (zmq::error_t &e) { + elog("handle_zmq recv_multipart exception ${str}", ("str", e.what())); } } } @@ -970,6 +1162,14 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } } + std::string network_info_str = bitcoin_client->getnetworkinfo(); + std::stringstream network_info_ss(network_info_str); + boost::property_tree::ptree network_info_json; + boost::property_tree::read_json(network_info_ss, network_info_json); + + bitcoin_major_version = network_info_json.get("result.version") / 10000; + ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version)); + listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); listener->event_received.connect([this](const std::string &event_data) { std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); @@ -1120,10 +1320,17 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) std::string tx_vout_s = input.second.get("n"); tx_vout = std::stoll(tx_vout_s); if (tx_vout == swdo_vout) { - for (auto &address : input.second.get_child("scriptPubKey.addresses")) { - if (address.second.data() == swdo_address) { - tx_address = address.second.data(); - break; + if (bitcoin_major_version > 21) { + std::string address = input.second.get("scriptPubKey.address"); + if (address == swdo_address) { + tx_address = address; + } + } else { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + if (address.second.data() == swdo_address) { + tx_address = address.second.data(); + break; + } } } std::string tx_amount_s = input.second.get("value"); @@ -1535,12 +1742,21 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { if (sto.object_id.is()) { for (auto &input : tx_json.get_child("result.vout")) { - for (auto &address : input.second.get_child("scriptPubKey.addresses")) { - if (address.second.data() == tx_address) { + if (bitcoin_major_version > 21) { + std::string address = input.second.get("scriptPubKey.address"); + if (address == tx_address) { std::string tx_amount_s = input.second.get("value"); tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); tx_amount = std::stoll(tx_amount_s); - break; + } + } else { + for (auto &address : input.second.get_child("scriptPubKey.addresses")) { + if (address.second.data() == tx_address) { + std::string tx_amount_s = input.second.get("value"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_amount = std::stoll(tx_amount_s); + break; + } } } } @@ -1617,7 +1833,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con } if (fee_rate >= total_amount) { - elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address)); + elog("Not enough BTC to pay the transfer fee, address ${fa}", ("fa", prev_pw_address)); return ""; } } @@ -1651,6 +1867,12 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_ uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; fee_rate = std::max(fee_rate, min_fee_rate); + + if (fee_rate >= deposit_amount) { + elog("Not enough BTC to pay the transfer fee, address ${da}", ("da", swdo.sidechain_from)); + return ""; + } + deposit_amount -= fee_rate; // Deduct minimum relay fee double transfer_amount = (double)deposit_amount / 100000000.0; @@ -1701,7 +1923,7 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s } if ((swwo.withdraw_amount.value > total_amount) || (fee_rate >= swwo.withdraw_amount.value)) { - elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); + elog("Not enough BTC to pay the transfer fee, address ${pw}", ("pw", pw_address)); return ""; } } @@ -1808,6 +2030,8 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) if (block.empty()) return; + add_to_son_listener_log("BLOCK : " + event_data); + auto vins = extract_info_from_block(block); scoped_lock interlock(event_handler_mutex); const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); @@ -1837,6 +2061,9 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) sed.peerplays_to = database.get_global_properties().parameters.son_account(); price btc_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; sed.peerplays_asset = asset(sed.sidechain_amount * btc_price.base.amount / btc_price.quote.amount); + + add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id); + sidechain_event_data_received(sed); } } @@ -1879,11 +2106,16 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block for (const auto &o : tx.get_child("vout")) { const auto script = o.second.get_child("scriptPubKey"); - if (!script.count("addresses")) - continue; + if (bitcoin_major_version > 21) { + if (!script.count("address")) + continue; + } else { + if (!script.count("addresses")) + continue; + } - for (const auto &addr : script.get_child("addresses")) { // in which cases there can be more addresses? - const auto address_base58 = addr.second.get_value(); + auto sort_out_vin = [&](std::string address) { + const auto address_base58 = address; info_for_vin vin; vin.out.hash_tx = tx.get_child("txid").get_value(); string amount = o.second.get_child("value").get_value(); @@ -1892,6 +2124,14 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block vin.out.n_vout = o.second.get_child("n").get_value(); vin.address = address_base58; result.push_back(vin); + }; + + if (bitcoin_major_version > 21) { + std::string address = script.get("address"); + sort_out_vin(address); + } else { + for (const auto &addr : script.get_child("addresses")) // in which cases there can be more addresses? + sort_out_vin(addr.second.get_value()); } } } @@ -1912,6 +2152,8 @@ void sidechain_net_handler_bitcoin::on_changed_objects(const vector &ids, const flat_set &accounts) { + std::vector address_or_script_array; + for (auto id : ids) { if (id.is()) { const auto &swi = database.get_index_type().indices().get(); @@ -1923,16 +2165,31 @@ void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector("address"); - bitcoin_client->importaddress(pw_address); + address_or_script_array.emplace_back(bitcoin_rpc_client::multi_params{bitcoin_rpc_client::multi_type::address, pw_address}); } if (pw_pt.count("redeemScript")) { std::string pw_redeem_script = pw_pt.get("redeemScript"); - bitcoin_client->importaddress(pw_redeem_script, "", true, true); + address_or_script_array.emplace_back(bitcoin_rpc_client::multi_params{bitcoin_rpc_client::multi_type::script, pw_redeem_script}); } } } } + + //! importmulti all addreses in one bulk + if (!address_or_script_array.empty()) { + //! Unlock wallet + if (!wallet_password.empty()) { + if (!bitcoin_client->walletpassphrase(wallet_password)) + return; + } + + //! importmulti + bitcoin_client->importmulti(address_or_script_array); + + //! Lock wallet + bitcoin_client->walletlock(); + } } // ============================================================================= diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 499f1e654..d63e6743e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -627,7 +627,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object proposal_op.proposed_ops.emplace_back(swdp_op); asset_issue_operation ai_op; - ai_op.fee = asset(2001000); + ai_op.fee = database.current_fee_schedule().calculate_fee(ai_op); ai_op.issuer = gpo.parameters.son_account(); ai_op.asset_to_issue = asset_to_issue; ai_op.issue_to_account = swdo.peerplays_from; @@ -846,6 +846,7 @@ void sidechain_net_handler_hive::hive_listener_loop() { void sidechain_net_handler_hive::handle_event(const std::string &event_data) { std::string block = node_rpc_client->block_api_get_block(std::atoll(event_data.c_str())); if (block != "") { + add_to_son_listener_log("BLOCK : " + event_data); std::stringstream ss(block); boost::property_tree::ptree block_json; boost::property_tree::read_json(ss, block_json); @@ -932,6 +933,9 @@ void sidechain_net_handler_hive::handle_event(const std::string &event_data) { sed.peerplays_from = accn; sed.peerplays_to = database.get_global_properties().parameters.son_account(); sed.peerplays_asset = asset(sed.sidechain_amount * sidechain_currency_price.base.amount / sidechain_currency_price.quote.amount); + + add_to_son_listener_log("TRX : " + sed.sidechain_transaction_id); + sidechain_event_data_received(sed); } } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index ff876b0c0..e2cb16085 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -98,6 +98,14 @@ void sidechain_net_manager::settle_sidechain_transactions() { } } +std::map> sidechain_net_manager::get_son_listener_log() { + std::map> result; + for (size_t i = 0; i < net_handlers.size(); i++) { + result[net_handlers.at(i)->get_sidechain()] = net_handlers.at(i)->get_son_listener_log(); + } + return result; +} + void sidechain_net_manager::on_applied_block(const signed_block &b) { } diff --git a/libraries/utilities/elasticsearch.cpp b/libraries/utilities/elasticsearch.cpp index 11a9561bf..df82695fe 100644 --- a/libraries/utilities/elasticsearch.cpp +++ b/libraries/utilities/elasticsearch.cpp @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -47,8 +46,36 @@ bool checkES(ES& es) if(doCurl(curl_request).empty()) return false; return true; +} + +const std::string getESVersion(ES& es) +{ + graphene::utilities::CurlRequest curl_request; + curl_request.handler = es.curl; + curl_request.url = es.elasticsearch_url; + curl_request.auth = es.auth; + curl_request.type = "GET"; + + fc::variant response = fc::json::from_string(doCurl(curl_request)); + + return response["version"]["number"].as_string(); +} +void checkESVersion7OrAbove(ES& es, bool& result) noexcept +{ + static const int64_t version_7 = 7; + try { + const auto es_version = graphene::utilities::getESVersion(es); + auto dot_pos = es_version.find('.'); + result = ( std::stoi(es_version.substr(0,dot_pos)) >= version_7 ); + } + catch( ... ) + { + wlog( "Unable to get ES version, assuming it is 7 or above" ); + result = true; + } } + const std::string simpleQuery(ES& es) { graphene::utilities::CurlRequest curl_request; @@ -118,13 +145,13 @@ bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer) return true; } -const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data) +const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string&& data) { std::vector bulk; fc::mutable_variant_object final_bulk_header; final_bulk_header["index"] = bulk_header; bulk.push_back(fc::json::to_string(final_bulk_header)); - bulk.push_back(data); + bulk.emplace_back(std::move(data)); return bulk; } @@ -154,15 +181,6 @@ const std::string getEndPoint(ES& es) return doCurl(curl_request); } -const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix) -{ - auto block_date_string = block_date.to_iso_string(); - std::vector parts; - boost::split(parts, block_date_string, boost::is_any_of("-")); - std::string index_name = _elasticsearch_index_prefix + parts[0] + "-" + parts[1]; - return index_name; -} - const std::string doCurl(CurlRequest& curl) { std::string CurlReadBuffer; diff --git a/libraries/utilities/include/graphene/utilities/elasticsearch.hpp b/libraries/utilities/include/graphene/utilities/elasticsearch.hpp index 898464b16..45749e27b 100644 --- a/libraries/utilities/include/graphene/utilities/elasticsearch.hpp +++ b/libraries/utilities/include/graphene/utilities/elasticsearch.hpp @@ -54,13 +54,14 @@ namespace graphene { namespace utilities { }; bool SendBulk(ES& es); - const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string& data); + const std::vector createBulk(const fc::mutable_variant_object& bulk_header, const std::string&& data); bool checkES(ES& es); + const std::string getESVersion(ES& es); + void checkESVersion7OrAbove(ES& es, bool& result) noexcept; const std::string simpleQuery(ES& es); bool deleteAll(ES& es); bool handleBulkResponse(long http_code, const std::string& CurlReadBuffer); const std::string getEndPoint(ES& es); - const std::string generateIndexName(const fc::time_point_sec& block_date, const std::string& _elasticsearch_index_prefix); const std::string doCurl(CurlRequest& curl); const std::string joinBulkLines(const std::vector& bulk); long getResponseCode(CURL *handler); diff --git a/libraries/wallet/Doxyfile.in b/libraries/wallet/Doxyfile.in index d83573229..7c654601b 100644 --- a/libraries/wallet/Doxyfile.in +++ b/libraries/wallet/Doxyfile.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.9.1 +# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -179,6 +187,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -226,7 +244,12 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = @@ -264,17 +287,26 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # @@ -285,7 +317,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,6 +325,15 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -318,7 +359,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -343,6 +384,13 @@ IDL_PROPERTY_SUPPORT = YES DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -417,6 +465,12 @@ EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -471,8 +525,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -495,7 +549,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = NO @@ -682,7 +736,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -727,11 +781,18 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -755,7 +816,7 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = ${CMAKE_CURRENT_SOURCE_DIR}/include/graphene/wallet/wallet.hpp @@ -763,7 +824,7 @@ INPUT = ${CMAKE_CURRENT_SOURCE_DIR}/include/graphene/wallet/wal # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -771,12 +832,19 @@ INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = @@ -862,6 +930,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -871,6 +943,10 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = @@ -923,7 +999,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -955,12 +1031,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -982,6 +1058,35 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1100,7 +1205,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1129,12 +1234,24 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1158,13 +1275,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1203,7 +1320,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1279,7 +1396,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1287,7 +1404,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1296,7 +1413,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1304,7 +1421,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1312,7 +1429,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1405,7 +1522,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1416,8 +1533,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1444,8 +1567,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1487,7 +1610,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1506,7 +1629,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1519,7 +1642,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1571,21 +1694,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1604,9 +1741,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1703,12 +1843,28 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1748,9 +1904,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1759,8 +1915,8 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = @@ -1846,6 +2002,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = NO +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1878,9 +2041,9 @@ DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sf.net) file that captures the -# structure of the code including all documentation. Note that this feature is -# still experimental and incomplete at the moment. +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -2047,12 +2210,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2066,15 +2223,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2093,7 +2241,7 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. HAVE_DOT = NO @@ -2207,7 +2355,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2218,7 +2367,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2241,11 +2391,17 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif and svg. +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2296,6 +2452,11 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index ff32ac109..02790e19d 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1346,6 +1346,21 @@ class wallet_api */ map list_committee_members(const string& lowerbound, uint32_t limit); + /** Lists all workers in the blockchain. + * This returns a list of all account names that own worker, and the associated worker id, + * sorted by name. This lists workers whether they are currently voted in or not. + * + * Use the \c lowerbound and limit parameters to page through the list. To retrieve all workers, + * start by setting \c lowerbound to the empty string \c "", and then each iteration, pass + * the last worker name returned as the \c lowerbound for the next \c list_workers() call. + * + * @param lowerbound the name of the first worker to return. If the named worker does not exist, + * the list will start at the worker that comes after \c lowerbound + * @param limit the maximum number of worker to return (max: 1000) + * @returns a list of worker mapping worker names to worker ids + */ + map list_workers(const string& lowerbound, uint32_t limit); + /** Returns information about the given SON. * @param owner_account the name or id of the SON account owner, or the id of the SON * @returns the information about the SON stored in the block chain @@ -1370,6 +1385,12 @@ class wallet_api */ committee_member_object get_committee_member(string owner_account); + /** Returns information about the given worker. + * @param owner_account the name or id of the worker account owner, or the id of the worker + * @returns the information about the workers stored in the block chain + */ + vector get_workers(string owner_account); + /** Creates a SON object owned by the given account. * @@ -1765,6 +1786,55 @@ class wallet_api uint16_t desired_number_of_sons, bool broadcast = false); + + /** Broadcast signed transaction for manually sidechain deposit + * @param son_name_or_id ID or name of the son account + * @param sidechain Sidechain type (bitcoin, HIVE, etc) + * @param transaction_id ID of transaction + * @param operation_index Index of operation + * @param sidechain_from Sidechain address transaction from + * @param sidechain_to Sidechain address transaction to + * @param sidechain_currency Sidechain currency + * @param sidechain_amount Sidechain amount to deposit + * @param peerplays_from_name_or_id ID or name of the account transaction from + * @param peerplays_to_name_or_id ID or name of the account transaction to + * @returns the signed transaction. + */ + signed_transaction sidechain_deposit_transaction( const string &son_name_or_id, + const sidechain_type& sidechain, + const string &transaction_id, + uint32_t operation_index, + const string &sidechain_from, + const string &sidechain_to, + const string &sidechain_currency, + int64_t sidechain_amount, + const string &peerplays_from_name_or_id, + const string &peerplays_to_name_or_id); + + /** Broadcast signed transaction for manually sidechain withdrawal + * @param son_name_or_id ID or name of the son account + * @param block_num Block number where original withdrawal transaction is executed + * @param sidechain Sidechain type (bitcoin, HIVE, etc) + * @param peerplays_uid peerplays_uid + * @param peerplays_transaction_id ID of transaction + * @param peerplays_from Sidechain address transaction from + * @param withdraw_sidechain Withdraw sidechain + * @param withdraw_address Withdraw address + * @param withdraw_currency Withdraw currency + * @param withdraw_amount Withdraw amount + * @returns the signed transaction. + */ + signed_transaction sidechain_withdrawal_transaction(const string &son_name_or_id, + uint32_t block_num, + const sidechain_type& sidechain, + const std::string &peerplays_uid, + const std::string &peerplays_transaction_id, + const chain::account_id_type &peerplays_from, + const sidechain_type& withdraw_sidechain, + const std::string &withdraw_address, + const std::string &withdraw_currency, + const string &withdraw_amount); + /** Vote for a given witness. * * An account can publish a list of all witnesses they approve of. This @@ -2487,9 +2557,46 @@ class wallet_api bool broadcast = false, bool to_temp = false ); - std::map> get_result_formatters() const; + /** + * @brief Get a list of vote_id_type that ID votes for + * @param account_name_or_id ID or name of the account to get votes for + * @return The list of vote_id_type ID votes for + * + */ + vector get_votes_ids(const string &account_name_or_id) const; + + /** + * @brief Return the objects account_name_or_id votes for + * @param account_name_or_id ID or name of the account to get votes for + * @return The votes_info account_name_or_id votes for + * + */ + votes_info get_votes(const string &account_name_or_id) const; + + /** + * @brief Get a list of accounts that votes for vote_id + * @param vote_id We search accounts that vote for this ID + * @return The accounts that votes for provided ID + * + */ + vector get_voters_by_id(const vote_id_type &vote_id) const; + + /** + * @brief Return the accounts that votes for account_name_or_id + * @param account_name_or_id ID or name of the account to get voters for + * @return The voters_info for account_name_or_id + * + */ + voters_info get_voters(const string &account_name_or_id) const; + + /** + * @brief Demo plugin api + * @return The hello world string + */ + std::map> get_son_listener_log() const; + fc::signal lock_changed; std::shared_ptr my; void encrypt_keys(); @@ -2626,8 +2733,10 @@ FC_API( graphene::wallet::wallet_api, (get_witness) (is_witness) (get_committee_member) + (get_workers) (list_witnesses) (list_committee_members) + (list_workers) (create_son) (try_create_son) (update_son) @@ -2643,6 +2752,7 @@ FC_API( graphene::wallet::wallet_api, (get_son_wallets) (add_sidechain_address) (delete_sidechain_address) + (sidechain_withdrawal_transaction) (get_sidechain_addresses_by_account) (get_sidechain_addresses_by_sidechain) (get_sidechain_address_by_account_and_sidechain) @@ -2657,6 +2767,7 @@ FC_API( graphene::wallet::wallet_api, (vote_for_committee_member) (vote_for_son) (update_son_votes) + (sidechain_deposit_transaction) (vote_for_witness) (update_witness_votes) (set_voting_proxy) @@ -2795,4 +2906,9 @@ FC_API( graphene::wallet::wallet_api, (get_custom_account_authorities_by_permission_id) (get_custom_account_authorities_by_permission_name) (get_active_custom_account_authorities_by_operation) + (get_votes_ids) + (get_votes) + (get_voters_by_id) + (get_voters) + (get_son_listener_log) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 8edfbd65e..0232bc445 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -84,6 +84,7 @@ #include #include #include +#include #ifndef WIN32 # include @@ -749,7 +750,7 @@ class wallet_api_impl { std::string account_id = account_id_to_string(id); auto rec = _remote_db->get_accounts({account_id}).front(); - FC_ASSERT(rec); + FC_ASSERT(rec, "Accout id: ${account_id} doesn't exist", ("account_id", account_id)); return *rec; } account_object get_account(string account_name_or_id) const @@ -762,7 +763,7 @@ class wallet_api_impl return get_account(*id); } else { auto rec = _remote_db->lookup_account_names({account_name_or_id}).front(); - FC_ASSERT( rec && rec->name == account_name_or_id ); + FC_ASSERT( rec && rec->name == account_name_or_id, "Account name or id: ${account_name_or_id} doesn't exist", ("account_name_or_id",account_name_or_id ) ); return *rec; } } @@ -1946,7 +1947,7 @@ class wallet_api_impl try { account_id_type owner_account_id = get_account_id(owner_account); - fc::optional son = _remote_db->get_son_by_account(owner_account_id); + fc::optional son = _remote_db->get_son_by_account_id(owner_account_id); if (son) return *son; else @@ -1961,6 +1962,49 @@ class wallet_api_impl FC_CAPTURE_AND_RETHROW( (owner_account) ) } + vector get_workers(string owner_account) + { + try + { + fc::optional worker_id = maybe_id(owner_account); + if (worker_id) + { + std::vector ids_to_get; + ids_to_get.push_back(*worker_id); + std::vector> worker_objects = _remote_db->get_workers(ids_to_get); + + if(!worker_objects.empty()) { + std::vector result; + for (const auto &worker_object : worker_objects) { + if (worker_object) + result.emplace_back(*worker_object); + } + return result; + } + else + FC_THROW("No workers is registered for id ${id}", ("id", owner_account)); + } + else + { + // then maybe it's the owner account + try + { + std::string owner_account_id = account_id_to_string(get_account_id(owner_account)); + auto workers = _remote_db->get_workers_by_account(owner_account_id); + if (!workers.empty()) + return workers; + else + FC_THROW("No workers is registered for account ${account}", ("account", owner_account)); + } + catch (const fc::exception&) + { + FC_THROW("No account or worker named ${account}", ("account", owner_account)); + } + } + } + FC_CAPTURE_AND_RETHROW( (owner_account) ) + } + bool is_witness(string owner_account) { try @@ -2075,7 +2119,7 @@ class wallet_api_impl son_create_op.pay_vb = pay_vb_id; son_create_op.sidechain_public_keys = sidechain_public_keys; - if (_remote_db->get_son_by_account(son_create_op.owner_account)) + if (_remote_db->get_son_by_account_id(son_create_op.owner_account)) FC_THROW("Account ${owner_account} is already a SON", ("owner_account", owner_account)); signed_transaction tx; @@ -2202,11 +2246,8 @@ class wallet_api_impl vector owners; for(auto obj: son_objects) { - if (obj) - { - std::string acc_id = account_id_to_string(obj->son_account); - owners.push_back(acc_id); - } + std::string acc_id = account_id_to_string(obj->son_account); + owners.push_back(acc_id); } vector< optional< account_object> > accs = _remote_db->get_accounts(owners); std::remove_if(son_objects.begin(), son_objects.end(), @@ -2216,9 +2257,7 @@ class wallet_api_impl std::inserter(result, result.end()), [](fc::optional& acct, fc::optional son) { FC_ASSERT(acct, "Invalid active SONs list in global properties."); - if (son.valid() && son->status != son_status::deregistered) - return std::make_pair(string(acct->name), std::move(son->id)); - return std::make_pair(string(acct->name), std::move(son_id_type())); + return std::make_pair(string(acct->name), std::move(son->id)); }); return result; } FC_CAPTURE_AND_RETHROW() } @@ -2323,6 +2362,64 @@ class wallet_api_impl } FC_CAPTURE_AND_RETHROW() } + signed_transaction sidechain_withdrawal_transaction(const string &son_name_or_id, + uint32_t block_num, + const sidechain_type& sidechain, + const std::string &peerplays_uid, + const std::string &peerplays_transaction_id, + const chain::account_id_type &peerplays_from, + const sidechain_type& withdraw_sidechain, + const std::string &withdraw_address, + const std::string &withdraw_currency, + const string &withdraw_amount) + { try{ + const global_property_object& gpo = get_global_properties(); + const auto dynamic_props = get_dynamic_global_properties(); + const auto son_obj = get_son(son_name_or_id); + + fc::optional peerplays_asset = get_asset(withdraw_currency); + FC_ASSERT(peerplays_asset, "Could not find asset matching ${asset}", ("asset", peerplays_asset)); + const auto asset_val = peerplays_asset->amount_from_string(withdraw_amount); + const auto asset_price = peerplays_asset->options.core_exchange_rate; + + price withdraw_currency_price = {}; + if ("BTC" == withdraw_currency) { + fc::optional a = get_asset( gpo.parameters.btc_asset()); + withdraw_currency_price = a->options.core_exchange_rate; + } else + if ("HBD" == withdraw_currency) { + fc::optional a = get_asset( gpo.parameters.hbd_asset()); + withdraw_currency_price = a->options.core_exchange_rate; + } else + if ("HIVE" == withdraw_currency) { + fc::optional a = get_asset( gpo.parameters.hive_asset()); + withdraw_currency_price = a->options.core_exchange_rate; + } else { + FC_THROW("withdraw_currency ${withdraw_currency}", ("withdraw_currency", withdraw_currency)); + } + + //! Create transaction + signed_transaction son_wallet_withdraw_create_transaction; + son_wallet_withdraw_create_operation op; + op.payer = son_obj.son_account; + op.son_id = son_obj.id; + op.timestamp = dynamic_props.time; + op.block_num = block_num; + op.sidechain = sidechain; + op.peerplays_uid = peerplays_uid; + op.peerplays_transaction_id = peerplays_transaction_id; + op.peerplays_from = peerplays_from; + op.peerplays_asset = asset(asset_val.amount * asset_price.base.amount / asset_price.quote.amount); + op.withdraw_sidechain = withdraw_sidechain; + op.withdraw_address = withdraw_address; + op.withdraw_currency = withdraw_currency; + op.withdraw_amount = asset_val.amount; + + son_wallet_withdraw_create_transaction.operations.push_back(op); + + return sign_transaction(son_wallet_withdraw_create_transaction, true); + } FC_CAPTURE_AND_RETHROW( (withdraw_currency) ) } + signed_transaction create_witness(string owner_account, string url, bool broadcast /* = false */) @@ -2717,7 +2814,7 @@ class wallet_api_impl account_object voting_account_object = get_account(voting_account); account_id_type son_account_id = get_account_id(son); - fc::optional son_obj = _remote_db->get_son_by_account(son_account_id); + fc::optional son_obj = _remote_db->get_son_by_account_id(son_account_id); if (!son_obj) FC_THROW("Account ${son} is not registered as a son", ("son", son)); if (approve) @@ -2761,7 +2858,7 @@ class wallet_api_impl for (const std::string& son : sons_to_approve) { account_id_type son_owner_account_id = get_account_id(son); - fc::optional son_obj = _remote_db->get_son_by_account(son_owner_account_id); + fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); if (!son_obj) FC_THROW("Account ${son} is not registered as a SON", ("son", son)); auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); @@ -2771,7 +2868,7 @@ class wallet_api_impl for (const std::string& son : sons_to_reject) { account_id_type son_owner_account_id = get_account_id(son); - fc::optional son_obj = _remote_db->get_son_by_account(son_owner_account_id); + fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); if (!son_obj) FC_THROW("Account ${son} is not registered as a SON", ("son", son)); unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); @@ -2792,6 +2889,81 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(sons_to_approve)(sons_to_reject)(desired_number_of_sons)(broadcast) ) } + signed_transaction sidechain_deposit_transaction( const string &son_name_or_id, + const sidechain_type& sidechain, + const string &transaction_id, + uint32_t operation_index, + const string &sidechain_from, + const string &sidechain_to, + const string &sidechain_currency, + int64_t sidechain_amount, + const string &peerplays_from_name_or_id, + const string &peerplays_to_name_or_id ) + { + //! Get data we need to procced transaction + const auto dynamic_global_props = get_dynamic_global_properties(); + const auto global_props = get_global_properties(); + const auto son_obj = get_son(son_name_or_id); + const std::string sidechain_str = [&sidechain](){ + switch (sidechain) { + case sidechain_type::peerplays : return "peerplays"; + case sidechain_type::bitcoin : return "bitcoin"; + case sidechain_type::hive : return "hive"; + default: + FC_THROW("Wrong sidechain type: ${sidechain}", ("sidechain", sidechain)); + } + }(); + const auto peerplays_from_obj = get_account(peerplays_from_name_or_id); + const auto peerplays_to_obj = get_account(peerplays_to_name_or_id); + const price sidechain_currency_price = [this, &sidechain_currency, &global_props](){ + if(sidechain_currency == "BTC") + { + fc::optional asset_obj = get_asset(object_id_to_string(global_props.parameters.btc_asset())); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", "BTC")); + return asset_obj->options.core_exchange_rate; + } + else if(sidechain_currency == "HBD") + { + fc::optional asset_obj = get_asset(object_id_to_string(global_props.parameters.hbd_asset())); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", "HBD")); + return asset_obj->options.core_exchange_rate; + } + else if(sidechain_currency == "HIVE") + { + fc::optional asset_obj = get_asset(object_id_to_string(global_props.parameters.hive_asset())); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", "HIVE")); + return asset_obj->options.core_exchange_rate; + } + else + { + fc::optional asset_obj = get_asset(sidechain_currency); + FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", sidechain_currency)); + return asset_obj->options.core_exchange_rate; + } + }(); + + //! Create transaction + signed_transaction son_wallet_deposit_create_transaction; + son_wallet_deposit_create_operation op; + op.payer = son_obj.son_account; + op.son_id = son_obj.id; + op.timestamp = dynamic_global_props.time; + op.block_num = dynamic_global_props.head_block_number; + op.sidechain = sidechain; + op.sidechain_uid = sidechain_str + "-" + transaction_id + "-" + std::to_string(operation_index); + op.sidechain_transaction_id = transaction_id; + op.sidechain_from = sidechain_from; + op.sidechain_to = sidechain_to; + op.sidechain_currency = sidechain_currency; + op.sidechain_amount = sidechain_amount; + op.peerplays_from = peerplays_from_obj.id; + op.peerplays_to = peerplays_to_obj.id; + op.peerplays_asset = asset(op.sidechain_amount * sidechain_currency_price.base.amount / sidechain_currency_price.quote.amount); + son_wallet_deposit_create_transaction.operations.push_back(op); + + return sign_transaction(son_wallet_deposit_create_transaction, true); + } + signed_transaction vote_for_witness(string voting_account, string witness, bool approve, @@ -4007,6 +4179,26 @@ class wallet_api_impl } } + void use_sidechain_api() + { + if( _remote_sidechain ) + return; + try + { + _remote_sidechain = _remote_api->sidechain(); + } + catch( const fc::exception& e ) + { + std::cerr << "\nCouldn't get sidechain API. You probably are not configured\n" + "to access the sidechain API on the node you are connecting to.\n" + "\n" + "To fix this problem:\n" + "- Please ensure peerplays_sidechain plugin is enabled.\n" + "- Please follow the instructions in README.md to set up an apiaccess file.\n" + "\n"; + } + } + void network_add_nodes( const vector& nodes ) { use_network_node_api(); @@ -4087,6 +4279,52 @@ class wallet_api_impl return it->second; } + vector get_votes_ids(const string &account_name_or_id) const + { + try + { + return _remote_db->get_votes_ids(account_name_or_id); + } + FC_CAPTURE_AND_RETHROW( (account_name_or_id) ) + } + + votes_info get_votes(const string &account_name_or_id) const + { + try + { + return _remote_db->get_votes(account_name_or_id); + } + FC_CAPTURE_AND_RETHROW( (account_name_or_id) ) + } + + vector get_voters_by_id(const vote_id_type &vote_id) const + { + try + { + return _remote_db->get_voters_by_id(vote_id); + } + FC_CAPTURE_AND_RETHROW( (vote_id) ) + } + + voters_info get_voters(const string &account_name_or_id) const + { + try + { + return _remote_db->get_voters(account_name_or_id); + } + FC_CAPTURE_AND_RETHROW( (account_name_or_id) ) + } + + std::map> get_son_listener_log() + { + use_sidechain_api(); + try + { + return (*_remote_sidechain)->get_son_listener_log(); + } + FC_CAPTURE_AND_RETHROW() + } + string _wallet_filename; wallet_data _wallet; @@ -4101,6 +4339,7 @@ class wallet_api_impl fc::api _remote_bookie; optional< fc::api > _remote_net_node; optional< fc::api > _remote_debug; + optional< fc::api > _remote_sidechain; flat_map _prototype_ops; @@ -4184,27 +4423,33 @@ string operation_printer::operator()(const transfer_operation& op) const std::string memo; if( op.memo ) { - if( wallet.is_locked() ) - { - out << " -- Unlock wallet to see memo."; - } else { - try { - FC_ASSERT(wallet._keys.count(op.memo->to) || wallet._keys.count(op.memo->from), "Memo is encrypted to a key ${to} or ${from} not in this wallet.", ("to", op.memo->to)("from",op.memo->from)); - if( wallet._keys.count(op.memo->to) ) { - auto my_key = wif_to_key(wallet._keys.at(op.memo->to)); - FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); - memo = op.memo->get_message(*my_key, op.memo->from); - out << " -- Memo: " << memo; - } else { - auto my_key = wif_to_key(wallet._keys.at(op.memo->from)); - FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); - memo = op.memo->get_message(*my_key, op.memo->to); - out << " -- Memo: " << memo; + bool is_encrypted = ((op.memo->from != public_key_type()) && (op.memo->to != public_key_type())); + if (is_encrypted) { + if( wallet.is_locked() ) + { + out << " -- Unlock wallet to see memo."; + } else { + try { + FC_ASSERT(wallet._keys.count(op.memo->to) || wallet._keys.count(op.memo->from), "Memo is encrypted to a key ${to} or ${from} not in this wallet.", ("to", op.memo->to)("from",op.memo->from)); + if( wallet._keys.count(op.memo->to) ) { + auto my_key = wif_to_key(wallet._keys.at(op.memo->to)); + FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); + memo = op.memo->get_message(*my_key, op.memo->from); + out << " -- Memo: " << memo; + } else { + auto my_key = wif_to_key(wallet._keys.at(op.memo->from)); + FC_ASSERT(my_key, "Unable to recover private key to decrypt memo. Wallet may be corrupted."); + memo = op.memo->get_message(*my_key, op.memo->to); + out << " -- Memo: " << memo; + } + } catch (const fc::exception& e) { + out << " -- could not decrypt memo"; + elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string())); } - } catch (const fc::exception& e) { - out << " -- could not decrypt memo"; - elog("Error when decrypting memo: ${e}", ("e", e.to_detail_string())); } + } else { + memo = op.memo->get_message(private_key_type(), public_key_type()); + out << " -- Memo: " << memo; } } fee(op.fee); @@ -4434,6 +4679,7 @@ asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const vector wallet_api::get_account_history(string name, int limit) const { + FC_ASSERT(my->get_account(name).name == name, "Account name: ${account_name} doesn't exist", ("account_name", name)); vector result; while (limit > 0) @@ -4488,6 +4734,8 @@ vector wallet_api::get_relative_account_history(string name, u { FC_ASSERT( start > 0 || limit <= 100 ); + FC_ASSERT(my->get_account(name).name == name, "Account name: ${account_name} doesn't exist", ("account_name", name)); + vector result; @@ -4663,7 +4911,7 @@ account_object wallet_api::get_account(string account_name_or_id) const asset_object wallet_api::get_asset(string asset_name_or_id) const { auto a = my->find_asset(asset_name_or_id); - FC_ASSERT(a); + FC_ASSERT(a, "Asset name or id: ${asset_name_or_id} doesn't exist", ("asset_name_or_id", asset_name_or_id) ); return *a; } @@ -4686,7 +4934,7 @@ asset_id_type wallet_api::get_asset_id(string asset_symbol_or_id) const bool wallet_api::import_key(string account_name_or_id, string wif_key) { - FC_ASSERT(!is_locked()); + FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available"); // backup wallet fc::optional optional_private_key = wif_to_key(wif_key); if (!optional_private_key) @@ -4705,7 +4953,7 @@ bool wallet_api::import_key(string account_name_or_id, string wif_key) map wallet_api::import_accounts( string filename, string password ) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); FC_ASSERT( fc::exists( filename ) ); const auto imported_keys = fc::json::from_file( filename ); @@ -4775,7 +5023,7 @@ map wallet_api::import_accounts( string filename, string password bool wallet_api::import_account_keys( string filename, string password, string src_account_name, string dest_account_name ) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); FC_ASSERT( fc::exists( filename ) ); bool is_my_account = false; @@ -4990,6 +5238,11 @@ map wallet_api::list_committee_members(const st return my->_remote_db->lookup_committee_member_accounts(lowerbound, limit); } +map wallet_api::list_workers(const string& lowerbound, uint32_t limit) +{ + return my->_remote_db->lookup_worker_accounts(lowerbound, limit); +} + son_object wallet_api::get_son(string owner_account) { return my->get_son(owner_account); @@ -5010,6 +5263,11 @@ committee_member_object wallet_api::get_committee_member(string owner_account) return my->get_committee_member(owner_account); } +vector wallet_api::get_workers(string owner_account) +{ + return my->get_workers(owner_account); +} + signed_transaction wallet_api::create_vesting_balance(string owner_account, string amount, string asset_symbol, @@ -5140,6 +5398,29 @@ signed_transaction wallet_api::delete_sidechain_address(string account, return my->delete_sidechain_address(account, sidechain, broadcast); } +signed_transaction wallet_api::sidechain_withdrawal_transaction(const string &son_name_or_id, + uint32_t block_num, + const sidechain_type& sidechain, + const std::string &peerplays_uid, + const std::string &peerplays_transaction_id, + const chain::account_id_type &peerplays_from, + const sidechain_type& withdraw_sidechain, + const std::string &withdraw_address, + const std::string &withdraw_currency, + const string &withdraw_amount) +{ + return my->sidechain_withdrawal_transaction(son_name_or_id, + block_num, + sidechain, + peerplays_uid, + peerplays_transaction_id, + peerplays_from, + withdraw_sidechain, + withdraw_address, + withdraw_currency, + withdraw_amount); +} + vector> wallet_api::get_sidechain_addresses_by_account(string account) { account_id_type account_id = get_account_id(account); @@ -5248,6 +5529,29 @@ signed_transaction wallet_api::update_son_votes(string voting_account, return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, desired_number_of_sons, broadcast); } +signed_transaction wallet_api::sidechain_deposit_transaction( const string &son_name_or_id, + const sidechain_type& sidechain, + const string &transaction_id, + uint32_t operation_index, + const string &sidechain_from, + const string &sidechain_to, + const string &sidechain_currency, + int64_t sidechain_amount, + const string &peerplays_from_name_or_id, + const string &peerplays_to_name_or_id) +{ + return my->sidechain_deposit_transaction(son_name_or_id, + sidechain, + transaction_id, + operation_index, + sidechain_from, + sidechain_to, + sidechain_currency, + sidechain_amount, + peerplays_from_name_or_id, + peerplays_to_name_or_id); +} + signed_transaction wallet_api::vote_for_witness(string voting_account, string witness, bool approve, @@ -5314,13 +5618,13 @@ operation wallet_api::get_prototype_operation(string operation_name) void wallet_api::dbg_make_uia(string creator, string symbol) { - FC_ASSERT(!is_locked()); + FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available"); my->dbg_make_uia(creator, symbol); } void wallet_api::dbg_make_mia(string creator, string symbol) { - FC_ASSERT(!is_locked()); + FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available"); my->dbg_make_mia(creator, symbol); } @@ -5356,7 +5660,7 @@ vector< variant > wallet_api::network_get_connected_peers() void wallet_api::flood_network(string prefix, uint32_t number_of_transactions) { - FC_ASSERT(!is_locked()); + FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available"); my->flood_network(prefix, number_of_transactions); } @@ -5835,13 +6139,13 @@ signed_transaction wallet_api::buy( string buyer_account, signed_transaction wallet_api::borrow_asset(string seller_name, string amount_to_sell, string asset_symbol, string amount_of_collateral, bool broadcast) { - FC_ASSERT(!is_locked()); + FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available"); return my->borrow_asset(seller_name, amount_to_sell, asset_symbol, amount_of_collateral, broadcast); } signed_transaction wallet_api::cancel_order(object_id_type order_id, bool broadcast) { - FC_ASSERT(!is_locked()); + FC_ASSERT(!is_locked(), "Wallet is locked, please unlock to get all operations available"); return my->cancel_order(order_id, broadcast); } @@ -5891,7 +6195,7 @@ map wallet_api::get_blind_accounts()const } map wallet_api::get_my_blind_accounts()const { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); map result; for( const auto& item : my->_wallet.labeled_keys ) { @@ -5903,7 +6207,7 @@ map wallet_api::get_my_blind_accounts()const public_key_type wallet_api::create_blind_account( string label, string brain_key ) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); auto label_itr = my->_wallet.labeled_keys.get().find(label); if( label_itr != my->_wallet.labeled_keys.get().end() ) @@ -6031,7 +6335,7 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label, blind_confirmation confirm; try { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); public_key_type from_key = get_public_key(from_key_or_label); public_key_type to_key = get_public_key(to_key_or_label); @@ -6200,7 +6504,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name vector> to_amounts, bool broadcast ) { try { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); idump((to_amounts)); blind_confirmation confirm; @@ -6280,7 +6584,7 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name blind_receipt wallet_api::receive_blind_transfer( string confirmation_receipt, string opt_from, string opt_memo ) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); stealth_confirmation conf(confirmation_receipt); FC_ASSERT( conf.to ); @@ -6422,7 +6726,7 @@ signed_transaction wallet_api::propose_create_sport( internationalized_string_type name, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; sport_create_operation sport_create_op; @@ -6450,7 +6754,7 @@ signed_transaction wallet_api::propose_update_sport( fc::optional name, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; sport_update_operation sport_update_op; @@ -6478,7 +6782,7 @@ signed_transaction wallet_api::propose_delete_sport( sport_id_type sport_id, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; sport_delete_operation sport_delete_op; @@ -6506,7 +6810,7 @@ signed_transaction wallet_api::propose_create_event_group( sport_id_type sport_id, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; event_group_create_operation event_group_create_op; @@ -6536,7 +6840,7 @@ signed_transaction wallet_api::propose_update_event_group( fc::optional name, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; event_group_update_operation event_group_update_op; @@ -6565,7 +6869,7 @@ signed_transaction wallet_api::propose_delete_event_group( event_group_id_type event_group, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; event_group_delete_operation event_group_delete_op; @@ -6595,7 +6899,7 @@ signed_transaction wallet_api::propose_create_event( event_group_id_type event_group_id, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; event_create_operation event_create_op; @@ -6630,7 +6934,7 @@ signed_transaction wallet_api::propose_update_event( fc::optional start_time, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; event_update_operation event_update_op; @@ -6663,7 +6967,7 @@ signed_transaction wallet_api::propose_create_betting_market_rules( internationalized_string_type description, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_rules_create_operation betting_market_rules_create_op; @@ -6693,7 +6997,7 @@ signed_transaction wallet_api::propose_update_betting_market_rules( fc::optional description, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_rules_update_operation betting_market_rules_update_op; @@ -6725,7 +7029,7 @@ signed_transaction wallet_api::propose_create_betting_market_group( asset_id_type asset_id, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_group_create_operation betting_market_group_create_op; @@ -6758,7 +7062,7 @@ signed_transaction wallet_api::propose_update_betting_market_group( fc::optional status, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_group_update_operation betting_market_group_update_op; @@ -6790,7 +7094,7 @@ signed_transaction wallet_api::propose_create_betting_market( internationalized_string_type payout_condition, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_create_operation betting_market_create_op; @@ -6822,7 +7126,7 @@ signed_transaction wallet_api::propose_update_betting_market( fc::optional payout_condition, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked() , "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_update_operation betting_market_update_op; @@ -6854,7 +7158,7 @@ signed_transaction wallet_api::place_bet(string betting_account, double backer_multiplier, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); fc::optional asset_obj = get_asset(asset_symbol); FC_ASSERT(asset_obj, "Could not find asset matching ${asset}", ("asset", asset_symbol)); @@ -6879,7 +7183,7 @@ signed_transaction wallet_api::cancel_bet(string betting_account, bet_id_type bet_id, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; @@ -6902,7 +7206,7 @@ signed_transaction wallet_api::propose_resolve_betting_market_group( const std::map& resolutions, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_group_resolve_operation betting_market_group_resolve_op; @@ -6930,7 +7234,7 @@ signed_transaction wallet_api::propose_cancel_betting_market_group( betting_market_group_id_type betting_market_group_id, bool broadcast /*= false*/) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); const chain_parameters& current_params = get_global_properties().parameters; betting_market_group_cancel_unmatched_bets_operation betting_market_group_cancel_unmatched_bets_op; @@ -6953,7 +7257,7 @@ signed_transaction wallet_api::propose_cancel_betting_market_group( signed_transaction wallet_api::tournament_create( string creator, tournament_options options, bool broadcast ) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); account_object creator_account_obj = get_account(creator); signed_transaction tx; @@ -6974,7 +7278,7 @@ signed_transaction wallet_api::tournament_join( string payer_account, string buy_in_asset_symbol, bool broadcast ) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); account_object payer_account_obj = get_account(payer_account); account_object player_account_obj = get_account(player_account); //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); @@ -7001,7 +7305,7 @@ signed_transaction wallet_api::tournament_leave( string canceling_account, tournament_id_type tournament_id, bool broadcast) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); account_object player_account_obj = get_account(player_account); account_object canceling_account_obj = get_account(canceling_account); //graphene::chain::tournament_object tournament_obj = my->get_object(tournament_id); @@ -7046,7 +7350,7 @@ signed_transaction wallet_api::rps_throw(game_id_type game_id, rock_paper_scissors_gesture gesture, bool broadcast) { - FC_ASSERT( !is_locked() ); + FC_ASSERT( !is_locked(), "Wallet is locked, please unlock to get all operations available" ); // check whether the gesture is appropriate for the game we're playing graphene::chain::game_object game_obj = my->get_object(game_id); @@ -7582,6 +7886,31 @@ std::vector wallet_api::get_all_matched_bets_for_bettor(acco return( my->_remote_bookie->get_all_matched_bets_for_bettor(bettor_id, start, limit) ); } +vector wallet_api::get_votes_ids(const string &account_name_or_id) const +{ + return my->get_votes_ids(account_name_or_id); +} + +votes_info wallet_api::get_votes(const string &account_name_or_id) const +{ + return my->get_votes(account_name_or_id); +} + +vector wallet_api::get_voters_by_id(const vote_id_type &vote_id) const +{ + return my->get_voters_by_id(vote_id); +} + +voters_info wallet_api::get_voters(const string &account_name_or_id) const +{ + return my->get_voters(account_name_or_id); +} + +std::map> wallet_api::get_son_listener_log() const +{ + return my->get_son_listener_log(); +} + // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info( const signed_block& block ) : signed_block( block ) diff --git a/programs/build_helpers/check_reflect.py b/programs/build_helpers/check_reflect.py index 0f41f355f..24dc21669 100755 --- a/programs/build_helpers/check_reflect.py +++ b/programs/build_helpers/check_reflect.py @@ -3,7 +3,7 @@ import json import os import re -import xml.etree.ElementTree as etree +import defusedxml.ElementTree as etree def process_node(path, node): """ diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 5a0ee3bad..05f68465c 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -27,8 +27,18 @@ #include #include +#include +#include +#include +#include + +#include #include #include +#include +#include +#include +#include #include #include #include @@ -40,25 +50,14 @@ #include #include #include +#include #include #include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - #ifdef WIN32 -# include +#include #else -# include +#include #endif using namespace graphene::app; @@ -68,44 +67,54 @@ using namespace graphene::wallet; using namespace std; namespace bpo = boost::program_options; -int main( int argc, char** argv ) -{ +int main(int argc, char **argv) { try { boost::program_options::options_description opts; - opts.add_options() - ("help,h", "Print this help message and exit.") - ("version", "Display the version info and exit") - ("server-rpc-endpoint,s", bpo::value()->implicit_value("ws://127.0.0.1:8090"), "Server websocket RPC endpoint") - ("server-rpc-user,u", bpo::value(), "Server Username") - ("server-rpc-password,p", bpo::value(), "Server Password") - ("rpc-endpoint,r", bpo::value()->implicit_value("127.0.0.1:8091"), "Endpoint for wallet websocket RPC to listen on") - ("rpc-tls-endpoint,t", bpo::value()->implicit_value("127.0.0.1:8092"), "Endpoint for wallet websocket TLS RPC to listen on") - ("rpc-tls-certificate,c", bpo::value()->implicit_value("server.pem"), "PEM certificate for wallet websocket TLS RPC") - ("rpc-http-endpoint,H", bpo::value()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on") - ("daemon,d", "Run the wallet in daemon mode" ) - ("wallet-file,w", bpo::value()->implicit_value("wallet.json"), "wallet to load") - ("chain-id", bpo::value(), "chain ID to connect to"); + opts.add_options()("help,h", "Print this help message and exit."); + opts.add_options()("version,v", "Display the version info and exit"); + opts.add_options()("server-rpc-endpoint,s", bpo::value()->implicit_value("ws://127.0.0.1:8090"), "Server websocket RPC endpoint"); + opts.add_options()("server-rpc-user,u", bpo::value(), "Server Username"); + opts.add_options()("server-rpc-password,p", bpo::value(), "Server Password"); + opts.add_options()("rpc-endpoint,r", bpo::value()->implicit_value("127.0.0.1:8091"), "Endpoint for wallet websocket RPC to listen on"); + opts.add_options()("rpc-tls-endpoint,t", bpo::value()->implicit_value("127.0.0.1:8092"), "Endpoint for wallet websocket TLS RPC to listen on"); + opts.add_options()("rpc-tls-certificate,c", bpo::value()->implicit_value("server.pem"), "PEM certificate for wallet websocket TLS RPC"); + opts.add_options()("rpc-http-endpoint,H", bpo::value()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on"); + opts.add_options()("daemon,d", "Run the wallet in daemon mode"); + opts.add_options()("wallet-file,w", bpo::value()->implicit_value("wallet.json"), "wallet to load"); + opts.add_options()("chain-id", bpo::value(), "chain ID to connect to"); bpo::variables_map options; - bpo::store( bpo::parse_command_line(argc, argv, opts), options ); + try { + bpo::parsed_options po = bpo::command_line_parser(argc, argv).options(opts).allow_unregistered().run(); + std::vector unrecognized = bpo::collect_unrecognized(po.options, bpo::include_positional); + if (unrecognized.size() > 0) { + std::cout << "Unknown parameter(s): " << std::endl; + for (auto s : unrecognized) { + std::cout << " " << s << std::endl; + } + return 0; + } + bpo::store(po, options); + } catch (const boost::program_options::invalid_command_line_syntax &e) { + std::cout << e.what() << std::endl; + return 0; + } - if( options.count("help") ) - { + if (options.count("help")) { std::cout << opts << "\n"; return 0; } - if (options.count("version")) - { + if (options.count("version")) { std::string wallet_version(graphene::utilities::git_revision_description); const size_t pos = wallet_version.find('/'); - if( pos != std::string::npos && wallet_version.size() > pos ) - wallet_version = wallet_version.substr( pos + 1 ); - std::cerr << "Version: " << wallet_version << "\n"; - std::cerr << "Git Revision: " << graphene::utilities::git_revision_sha << "\n"; - std::cerr << "Built: " << __DATE__ " at " __TIME__ << "\n"; + if (pos != std::string::npos && wallet_version.size() > pos) + wallet_version = wallet_version.substr(pos + 1); + std::cout << "Version: " << wallet_version << "\n"; + std::cout << "Git Revision: " << graphene::utilities::git_revision_sha << "\n"; + std::cout << "Built: " << __DATE__ " at " __TIME__ << "\n"; std::cout << "SSL: " << OPENSSL_VERSION_TEXT << "\n"; std::cout << "Boost: " << boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", ".") << "\n"; return 0; @@ -116,33 +125,33 @@ int main( int argc, char** argv ) fc::path log_dir = data_dir / "logs"; fc::file_appender::config ac; - ac.filename = log_dir / "rpc" / "rpc.log"; - ac.flush = true; - ac.rotate = true; - ac.rotation_interval = fc::hours( 1 ); - ac.rotation_limit = fc::days( 1 ); + ac.filename = log_dir / "rpc" / "rpc.log"; + ac.flush = true; + ac.rotate = true; + ac.rotation_interval = fc::hours(1); + ac.rotation_limit = fc::days(1); std::cout << "Logging RPC to file: " << (data_dir / ac.filename).preferred_string() << "\n"; - cfg.appenders.push_back(fc::appender_config( "default", "console", fc::variant(fc::console_appender::config(), 20))); - cfg.appenders.push_back(fc::appender_config( "rpc", "file", fc::variant(ac, 5))); + cfg.appenders.push_back(fc::appender_config("default", "console", fc::variant(fc::console_appender::config(), 20))); + cfg.appenders.push_back(fc::appender_config("rpc", "file", fc::variant(ac, 5))); - cfg.loggers = { fc::logger_config("default"), fc::logger_config( "rpc") }; + cfg.loggers = {fc::logger_config("default"), fc::logger_config("rpc")}; cfg.loggers.front().level = fc::log_level::warn; cfg.loggers.front().appenders = {"default"}; cfg.loggers.back().level = fc::log_level::info; cfg.loggers.back().appenders = {"rpc"}; - fc::configure_logging( cfg ); + fc::configure_logging(cfg); fc::ecc::private_key committee_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key"))); - idump( (key_to_wif( committee_private_key ) ) ); + idump((key_to_wif(committee_private_key))); fc::ecc::private_key nathan_private_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); public_key_type nathan_pub_key = nathan_private_key.get_public_key(); - idump( (nathan_pub_key) ); - idump( (key_to_wif( nathan_private_key ) ) ); + idump((nathan_pub_key)); + idump((key_to_wif(nathan_private_key))); // // TODO: We read wallet_data twice, once in main() to grab the @@ -152,153 +161,136 @@ int main( int argc, char** argv ) // wallet_data wdata; - fc::path wallet_file( options.count("wallet-file") ? options.at("wallet-file").as() : "wallet.json"); - if( fc::exists( wallet_file ) ) - { - wdata = fc::json::from_file( wallet_file ).as( GRAPHENE_MAX_NESTED_OBJECTS ); - if( options.count("chain-id") ) - { + fc::path wallet_file(options.count("wallet-file") ? options.at("wallet-file").as() : "wallet.json"); + if (fc::exists(wallet_file)) { + wdata = fc::json::from_file(wallet_file).as(GRAPHENE_MAX_NESTED_OBJECTS); + if (options.count("chain-id")) { // the --chain-id on the CLI must match the chain ID embedded in the wallet file - if( chain_id_type(options.at("chain-id").as()) != wdata.chain_id ) - { + if (chain_id_type(options.at("chain-id").as()) != wdata.chain_id) { std::cout << "Chain ID in wallet file does not match specified chain ID\n"; return 1; } } - } - else - { - if( options.count("chain-id") ) - { + } else { + if (options.count("chain-id")) { wdata.chain_id = chain_id_type(options.at("chain-id").as()); std::cout << "Starting a new wallet with chain ID " << wdata.chain_id.str() << " (from CLI)\n"; - } - else - { + } else { wdata.chain_id = graphene::egenesis::get_egenesis_chain_id(); std::cout << "Starting a new wallet with chain ID " << wdata.chain_id.str() << " (from egenesis)\n"; } } // but allow CLI to override - if( options.count("server-rpc-endpoint") ) + if (options.count("server-rpc-endpoint")) wdata.ws_server = options.at("server-rpc-endpoint").as(); - if( options.count("server-rpc-user") ) + if (options.count("server-rpc-user")) wdata.ws_user = options.at("server-rpc-user").as(); - if( options.count("server-rpc-password") ) + if (options.count("server-rpc-password")) wdata.ws_password = options.at("server-rpc-password").as(); fc::http::websocket_client client; idump((wdata.ws_server)); - auto con = client.connect( wdata.ws_server ); + auto con = client.connect(wdata.ws_server); auto apic = std::make_shared(con, GRAPHENE_MAX_NESTED_OBJECTS); - auto remote_api = apic->get_remote_api< login_api >(1); - edump((wdata.ws_user)(wdata.ws_password) ); - FC_ASSERT( remote_api->login( wdata.ws_user, wdata.ws_password ), "Failed to log in to API server" ); + auto remote_api = apic->get_remote_api(1); + edump((wdata.ws_user)(wdata.ws_password)); + FC_ASSERT(remote_api->login(wdata.ws_user, wdata.ws_password), "Failed to log in to API server"); - auto wapiptr = std::make_shared( wdata, remote_api ); - wapiptr->set_wallet_filename( wallet_file.generic_string() ); + auto wapiptr = std::make_shared(wdata, remote_api); + wapiptr->set_wallet_filename(wallet_file.generic_string()); wapiptr->load_wallet_file(); fc::api wapi(wapiptr); - auto wallet_cli = std::make_shared( GRAPHENE_MAX_NESTED_OBJECTS ); - for( auto& name_formatter : wapiptr->get_result_formatters() ) - wallet_cli->format_result( name_formatter.first, name_formatter.second ); + auto wallet_cli = std::make_shared(GRAPHENE_MAX_NESTED_OBJECTS); + for (auto &name_formatter : wapiptr->get_result_formatters()) + wallet_cli->format_result(name_formatter.first, name_formatter.second); - boost::signals2::scoped_connection closed_connection(con->closed.connect([wallet_cli]{ + boost::signals2::scoped_connection closed_connection(con->closed.connect([wallet_cli] { cerr << "Server has disconnected us.\n"; wallet_cli->stop(); })); (void)(closed_connection); - if( wapiptr->is_new() ) - { + if (wapiptr->is_new()) { std::cout << "Please use the set_password method to initialize a new wallet before continuing\n"; - wallet_cli->set_prompt( "new >>> " ); + wallet_cli->set_prompt("new >>> "); } else - wallet_cli->set_prompt( "locked >>> " ); + wallet_cli->set_prompt("locked >>> "); boost::signals2::scoped_connection locked_connection(wapiptr->lock_changed.connect([&](bool locked) { - wallet_cli->set_prompt( locked ? "locked >>> " : "unlocked >>> " ); + wallet_cli->set_prompt(locked ? "locked >>> " : "unlocked >>> "); })); std::shared_ptr _websocket_server; - if( options.count("rpc-endpoint") ) - { + if (options.count("rpc-endpoint")) { _websocket_server = std::make_shared(); - _websocket_server->on_connection([&wapi]( const fc::http::websocket_connection_ptr& c ){ + _websocket_server->on_connection([&wapi](const fc::http::websocket_connection_ptr &c) { std::cout << "here... \n"; - wlog("." ); + wlog("."); auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); - c->set_session_data( wsc ); + c->set_session_data(wsc); }); - ilog( "Listening for incoming RPC requests on ${p}", ("p", options.at("rpc-endpoint").as() )); - _websocket_server->listen( fc::ip::endpoint::from_string(options.at("rpc-endpoint").as()) ); + ilog("Listening for incoming RPC requests on ${p}", ("p", options.at("rpc-endpoint").as())); + _websocket_server->listen(fc::ip::endpoint::from_string(options.at("rpc-endpoint").as())); _websocket_server->start_accept(); } string cert_pem = "server.pem"; - if( options.count( "rpc-tls-certificate" ) ) + if (options.count("rpc-tls-certificate")) cert_pem = options.at("rpc-tls-certificate").as(); std::shared_ptr _websocket_tls_server; - if( options.count("rpc-tls-endpoint") ) - { + if (options.count("rpc-tls-endpoint")) { _websocket_tls_server = std::make_shared(cert_pem); - _websocket_tls_server->on_connection([&]( const fc::http::websocket_connection_ptr& c ){ + _websocket_tls_server->on_connection([&](const fc::http::websocket_connection_ptr &c) { auto wsc = std::make_shared(c, GRAPHENE_MAX_NESTED_OBJECTS); wsc->register_api(wapi); - c->set_session_data( wsc ); + c->set_session_data(wsc); }); - ilog( "Listening for incoming TLS RPC requests on ${p}", ("p", options.at("rpc-tls-endpoint").as() )); - _websocket_tls_server->listen( fc::ip::endpoint::from_string(options.at("rpc-tls-endpoint").as()) ); + ilog("Listening for incoming TLS RPC requests on ${p}", ("p", options.at("rpc-tls-endpoint").as())); + _websocket_tls_server->listen(fc::ip::endpoint::from_string(options.at("rpc-tls-endpoint").as())); _websocket_tls_server->start_accept(); } auto _http_server = std::make_shared(); - if( options.count("rpc-http-endpoint" ) ) - { - ilog( "Listening for incoming HTTP RPC requests on ${p}", ("p", options.at("rpc-http-endpoint").as() ) ); - _http_server->listen( fc::ip::endpoint::from_string( options.at( "rpc-http-endpoint" ).as() ) ); + if (options.count("rpc-http-endpoint")) { + ilog("Listening for incoming HTTP RPC requests on ${p}", ("p", options.at("rpc-http-endpoint").as())); + _http_server->listen(fc::ip::endpoint::from_string(options.at("rpc-http-endpoint").as())); // // due to implementation, on_request() must come AFTER listen() // _http_server->on_request( - [&wapi]( const fc::http::request& req, const fc::http::server::response& resp ) - { - std::shared_ptr< fc::rpc::http_api_connection > conn = - std::make_shared< fc::rpc::http_api_connection >( GRAPHENE_MAX_NESTED_OBJECTS ); - conn->register_api( wapi ); - conn->on_request( req, resp ); - } ); + [&wapi](const fc::http::request &req, const fc::http::server::response &resp) { + std::shared_ptr conn = + std::make_shared(GRAPHENE_MAX_NESTED_OBJECTS); + conn->register_api(wapi); + conn->on_request(req, resp); + }); } - if( !options.count( "daemon" ) ) - { - wallet_cli->register_api( wapi ); + if (!options.count("daemon")) { + wallet_cli->register_api(wapi); wallet_cli->start(); wallet_cli->wait(); - } - else - { - fc::promise::ptr exit_promise = new fc::promise("UNIX Signal Handler"); - fc::set_signal_handler([&exit_promise](int signal) { - exit_promise->set_value(signal); - }, SIGINT); - - ilog( "Entering Daemon Mode, ^C to exit" ); - exit_promise->wait(); + } else { + fc::promise::ptr exit_promise = new fc::promise("UNIX Signal Handler"); + fc::set_signal_handler([&exit_promise](int signal) { + exit_promise->set_value(signal); + }, + SIGINT); + + ilog("Entering Daemon Mode, ^C to exit"); + exit_promise->wait(); } wapi->save_wallet_file(wallet_file.generic_string()); locked_connection.disconnect(); closed_connection.disconnect(); - } - catch ( const fc::exception& e ) - { + } catch (const fc::exception &e) { std::cout << e.to_detail_string() << "\n"; return -1; } diff --git a/tests/benchmarks/genesis_allocation.cpp b/tests/benchmarks/genesis_allocation.cpp index 63e75db56..b212e2cce 100644 --- a/tests/benchmarks/genesis_allocation.cpp +++ b/tests/benchmarks/genesis_allocation.cpp @@ -27,7 +27,7 @@ #include -#include +#include using namespace graphene::chain; diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 1996c2676..491ec8f96 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -60,8 +60,14 @@ class son_test_helper create_tx = fixture_.con.wallet_api_ptr->create_account_with_brain_key( bki.brain_priv_key, account_name, "nathan", "nathan", true ); + + fixture_.generate_block(); + // save the private key for this new account in the wallet file BOOST_CHECK(fixture_.con.wallet_api_ptr->import_key(account_name, bki.wif_priv_key)); + + fixture_.generate_block(); + fixture_.con.wallet_api_ptr->save_wallet_file(fixture_.con.wallet_filename); // attempt to give son account some CORE tokens @@ -244,6 +250,28 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes > son2_start_votes); + //! Get nathan account + const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); + + //! Check son1account voters + auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account"); + BOOST_REQUIRE(voters_for_son1account.voters_for_son); + BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account.voters_for_son->voters[0].instance, nathan_account_object.id.instance()); + + //! Check son2account voters + auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account"); + BOOST_REQUIRE(voters_for_son2account.voters_for_son); + BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account.voters_for_son->voters[0].instance, nathan_account_object.id.instance()); + + //! Check votes of nathan + auto nathan_votes = con.wallet_api_ptr->get_votes("nathan"); + BOOST_REQUIRE(nathan_votes.votes_for_sons); + BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 2); + BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(1).id.instance(), son2_obj.id.instance()); + // Withdraw vote for a son1account BOOST_TEST_MESSAGE("Withdraw vote for a son1account"); vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", false, true); @@ -254,6 +282,17 @@ BOOST_AUTO_TEST_CASE( son_voting ) son1_end_votes = son1_obj.total_votes; BOOST_CHECK(son1_end_votes == son1_start_votes); + //! Check son1account voters + voters_for_son1account = con.wallet_api_ptr->get_voters("son1account"); + BOOST_REQUIRE(voters_for_son1account.voters_for_son); + BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 0); + + //! Check votes of nathan + nathan_votes = con.wallet_api_ptr->get_votes("nathan"); + BOOST_REQUIRE(nathan_votes.votes_for_sons); + BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 1); + BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son2_obj.id.instance()); + // Withdraw vote for a son2account BOOST_TEST_MESSAGE("Withdraw vote for a son2account"); vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", false, true); @@ -264,6 +303,15 @@ BOOST_AUTO_TEST_CASE( son_voting ) son2_end_votes = son2_obj.total_votes; BOOST_CHECK(son2_end_votes == son2_start_votes); + //! Check son2account voters + voters_for_son2account = con.wallet_api_ptr->get_voters("son2account"); + BOOST_REQUIRE(voters_for_son2account.voters_for_son); + BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 0); + + //! Check votes of nathan + nathan_votes = con.wallet_api_ptr->get_votes("nathan"); + BOOST_CHECK(!nathan_votes.votes_for_sons.valid()); + } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); edump((e.to_detail_string())); diff --git a/tests/elasticsearch/main.cpp b/tests/elasticsearch/main.cpp index c948616f2..7067e1e9b 100644 --- a/tests/elasticsearch/main.cpp +++ b/tests/elasticsearch/main.cpp @@ -38,6 +38,10 @@ using namespace graphene::chain; using namespace graphene::chain::test; using namespace graphene::app; +const std::string g_es_url = "http://localhost:9200/"; +const std::string g_es_index_prefix = "peerplays-"; +const std::string g_es_ppobjects_prefix = "ppobjects-"; + BOOST_FIXTURE_TEST_SUITE( elasticsearch_tests, database_fixture ) BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { @@ -48,8 +52,8 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { graphene::utilities::ES es; es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "peerplays-"; + es.elasticsearch_url = g_es_url; + es.index_prefix = g_es_index_prefix; //es.auth = "elastic:changeme"; // delete all first @@ -71,7 +75,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { //int account_create_op_id = operation::tag::value; string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - es.endpoint = es.index_prefix + "*/data/_count"; + es.endpoint = es.index_prefix + "*/_doc/_count"; es.query = query; auto res = graphene::utilities::simpleQuery(es); @@ -79,7 +83,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { auto total = j["count"].as_string(); BOOST_CHECK_EQUAL(total, "5"); - es.endpoint = es.index_prefix + "*/data/_search"; + es.endpoint = es.index_prefix + "*/_doc/_search"; res = graphene::utilities::simpleQuery(es); j = fc::json::from_string(res); auto first_id = j["hits"]["hits"][size_t(0)]["_id"].as_string(); @@ -91,7 +95,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { fc::usleep(fc::milliseconds(1000)); // index.refresh_interval - es.endpoint = es.index_prefix + "*/data/_count"; + es.endpoint = es.index_prefix + "*/_doc/_count"; res = graphene::utilities::simpleQuery(es); j = fc::json::from_string(res); @@ -114,9 +118,9 @@ BOOST_AUTO_TEST_CASE(elasticsearch_account_history) { // check the visitor data auto block_date = db.head_block_time(); - std::string index_name = graphene::utilities::generateIndexName(block_date, "peerplays-"); + std::string index_name = g_es_index_prefix + block_date.to_iso_string().substr( 0, 7 ); // yyyy-mm - es.endpoint = index_name + "/data/2.9.12"; // we know last op is a transfer of amount 300 + es.endpoint = index_name + "/_doc/2.9.12"; // we know last op is a transfer of amount 300 res = graphene::utilities::getEndPoint(es); j = fc::json::from_string(res); auto last_transfer_amount = j["_source"]["operation_history"]["op_object"]["amount_"]["amount"].as_string(); @@ -137,8 +141,8 @@ BOOST_AUTO_TEST_CASE(elasticsearch_objects) { graphene::utilities::ES es; es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "ppobjects-"; + es.elasticsearch_url = g_es_url; + es.index_prefix = g_es_ppobjects_prefix; //es.auth = "elastic:changeme"; // delete all first @@ -155,7 +159,7 @@ BOOST_AUTO_TEST_CASE(elasticsearch_objects) { fc::usleep(fc::milliseconds(1000)); string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - es.endpoint = es.index_prefix + "*/data/_count"; + es.endpoint = es.index_prefix + "*/_doc/_count"; es.query = query; auto res = graphene::utilities::simpleQuery(es); @@ -163,14 +167,14 @@ BOOST_AUTO_TEST_CASE(elasticsearch_objects) { auto total = j["count"].as_string(); BOOST_CHECK_EQUAL(total, "2"); - es.endpoint = es.index_prefix + "asset/data/_search"; + es.endpoint = es.index_prefix + "asset/_doc/_search"; res = graphene::utilities::simpleQuery(es); j = fc::json::from_string(res); auto first_id = j["hits"]["hits"][size_t(0)]["_source"]["symbol"].as_string(); BOOST_CHECK_EQUAL(first_id, "USD"); auto bitasset_data_id = j["hits"]["hits"][size_t(0)]["_source"]["bitasset_data_id"].as_string(); - es.endpoint = es.index_prefix + "bitasset/data/_search"; + es.endpoint = es.index_prefix + "bitasset/_doc/_search"; es.query = "{ \"query\" : { \"bool\": { \"must\" : [{ \"term\": { \"object_id\": \""+bitasset_data_id+"\"}}] } } }"; res = graphene::utilities::simpleQuery(es); j = fc::json::from_string(res); @@ -192,11 +196,11 @@ BOOST_AUTO_TEST_CASE(elasticsearch_suite) { graphene::utilities::ES es; es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "peerplays-"; + es.elasticsearch_url = g_es_url; + es.index_prefix = g_es_index_prefix; auto delete_account_history = graphene::utilities::deleteAll(es); fc::usleep(fc::milliseconds(1000)); - es.index_prefix = "ppobjects-"; + es.index_prefix = g_es_ppobjects_prefix; auto delete_objects = graphene::utilities::deleteAll(es); fc::usleep(fc::milliseconds(1000)); @@ -218,8 +222,8 @@ BOOST_AUTO_TEST_CASE(elasticsearch_history_api) { graphene::utilities::ES es; es.curl = curl; - es.elasticsearch_url = "http://localhost:9200/"; - es.index_prefix = "peerplays-"; + es.elasticsearch_url = g_es_url; + es.index_prefix = g_es_index_prefix; auto delete_account_history = graphene::utilities::deleteAll(es); diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 7571feb67..a802aac58 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -934,6 +934,125 @@ BOOST_FIXTURE_TEST_CASE( change_block_interval, database_fixture ) BOOST_CHECK_EQUAL(db.head_block_time().sec_since_epoch() - past_time, 2); } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE( prevent_missconfiguration_blockchain_param, database_fixture ) +{ try { + + db.modify(db.get_global_properties(), [](global_property_object& p) { + p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds(); + }); + + // remeber global properties before we try to change + const global_property_object gpo = db.get_global_properties(); + + BOOST_TEST_MESSAGE( "Creating a proposal to try with changing some global parameters" ); + { + proposal_create_operation cop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time()); + cop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + cop.expiration_time = db.head_block_time() + *cop.review_period_seconds + 10; + + committee_member_update_global_parameters_operation uop; + + // this is allowed values and the proposal change should be visible after approving + *uop.new_parameters.extensions.value.son_heartbeat_frequency = 100; + *uop.new_parameters.extensions.value.son_deregister_time = 120; + *uop.new_parameters.extensions.value.son_down_time = 140; + *uop.new_parameters.extensions.value.son_pay_time = 160; + + // this change should not be applied. Changing gpos_period_start is not allowed + *uop.new_parameters.extensions.value.gpos_period_start = 50; + + // the following changes should not be applied. Changing the assets is not allowed + *uop.new_parameters.extensions.value.btc_asset = db.get_global_properties().parameters.hbd_asset(); + *uop.new_parameters.extensions.value.hbd_asset = db.get_global_properties().parameters.hive_asset(); + *uop.new_parameters.extensions.value.hive_asset = db.get_global_properties().parameters.btc_asset(); + + // this change should not be applied. Changing the son_acccount is not allowed + *uop.new_parameters.extensions.value.son_account = GRAPHENE_TEMP_ACCOUNT; + + cop.proposed_ops.emplace_back(uop); + + trx.operations.push_back(cop); + db.push_transaction(trx); + } + + BOOST_TEST_MESSAGE( "Updating proposal by signing with the committee_member private key" ); + { + proposal_update_operation uop; + uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT; + uop.active_approvals_to_add = {get_account("init0").get_id(), get_account("init1").get_id(), + get_account("init2").get_id(), get_account("init3").get_id(), + get_account("init4").get_id(), get_account("init5").get_id(), + get_account("init6").get_id(), get_account("init7").get_id()}; + trx.operations.push_back(uop); + sign( trx, init_account_priv_key ); + + db.push_transaction(trx); + + BOOST_CHECK(proposal_id_type()(db).is_authorized_to_execute(db)); + } + + BOOST_TEST_MESSAGE( "Generating blocks until proposal expires" ); + generate_blocks(proposal_id_type()(db).expiration_time + 5); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way*/ + + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_heartbeat_frequency(), 100 ); + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_deregister_time(), 120 ); + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_down_time(), 140 ); + BOOST_CHECK_EQUAL( db.get_global_properties().parameters.son_pay_time(), 160 ); + + BOOST_CHECK_NE( db.get_global_properties().parameters.gpos_period_start(), 50 ); + + BOOST_CHECK( db.get_global_properties().parameters.son_account() != GRAPHENE_TEMP_ACCOUNT ); + + BOOST_CHECK( gpo.parameters.hbd_asset() == db.get_global_properties().parameters.hbd_asset() ); + BOOST_CHECK( gpo.parameters.hive_asset() == db.get_global_properties().parameters.hive_asset() ); + BOOST_CHECK( gpo.parameters.btc_asset() == db.get_global_properties().parameters.btc_asset() ); + +} FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture ) +{ try { + + db.modify(db.get_global_properties(), [](global_property_object& p) { + p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds(); + }); + + generate_block(); + // check that maximum_son_count are not yet updated on 7, it should + // be updated on HARDFORK_SON2_TIME + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS); + + generate_blocks(HARDFORK_SON2_TIME); + + // check that flags for assets are set after hardfork_son2_time + asset_object btc_asset = get_asset("BTC"); + uint16_t check_flags{0}; + check_flags |= asset_issuer_permission_flags::charge_market_fee | asset_issuer_permission_flags::override_authority; + uint16_t result = btc_asset.options.flags & check_flags; + BOOST_CHECK_EQUAL( result, check_flags); + + // move on next maintenance interval and check that maximum_son_count is updated to 7 + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); // get the maintenance skip slots out of the way*/ + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 7); + + generate_blocks(HARDFORK_SON3_TIME); + // after this hardfork maximum son account should not reset the value + // on 7 after maintenance interval anymore. So change the global parameters + // and check the value after maintenance interval + db.modify(db.get_global_properties(), [](global_property_object& p) { + p.parameters.extensions.value.maximum_son_count = 13; + }); + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + generate_block(); + + BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 13); + +} FC_LOG_AND_RETHROW() } + BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) { try diff --git a/tests/tests/voting_tests.cpp b/tests/tests/voting_tests.cpp index a6f416753..ad6c3ee7b 100644 --- a/tests/tests/voting_tests.cpp +++ b/tests/tests/voting_tests.cpp @@ -322,6 +322,18 @@ BOOST_AUTO_TEST_CASE(track_votes_witnesses_enabled) auto witness1_object = db_api1.get_witness_by_account(witness1_id(db).name); BOOST_CHECK_EQUAL(witness1_object->total_votes, 111); + //! Check witness1 voters + const auto voters_for_witness1 = db_api1.get_voters("witness1"); + BOOST_REQUIRE(voters_for_witness1.voters_for_witness); + BOOST_CHECK_EQUAL(voters_for_witness1.voters_for_witness->voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_witness1.voters_for_witness->voters[0].instance, 18); + + //! Check votes of account + const auto account_votes = db_api1.get_votes("1.2.18"); + BOOST_REQUIRE(account_votes.votes_for_witnesses); + BOOST_CHECK_EQUAL(account_votes.votes_for_witnesses->size(), 1); + BOOST_CHECK_EQUAL(account_votes.votes_for_witnesses->at(0).id.instance(), witness1_object->id.instance()); + } FC_LOG_AND_RETHROW() } @@ -501,6 +513,17 @@ BOOST_AUTO_TEST_CASE(track_votes_committee_enabled) auto committee1_object = db_api1.get_committee_member_by_account(committee1_id(db).name); BOOST_CHECK_EQUAL(committee1_object->total_votes, 111); + //! Check committee1 voters + const auto voters_for_committee1 = db_api1.get_voters("committee1"); + BOOST_REQUIRE(voters_for_committee1.voters_for_committee_member); + BOOST_CHECK_EQUAL(voters_for_committee1.voters_for_committee_member->voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_committee1.voters_for_committee_member->voters[0].instance, 18); + + //! Check votes of account + const auto account_votes = db_api1.get_votes("1.2.18"); + BOOST_REQUIRE(account_votes.votes_for_committee_members); + BOOST_CHECK_EQUAL(account_votes.votes_for_committee_members->back().id.instance(), committee1_object->id.instance()); + } FC_LOG_AND_RETHROW() }