From 19cc469ab7a1ff1bb62f57422842bfdf8dae1eb4 Mon Sep 17 00:00:00 2001 From: dimfred Date: Sun, 17 Mar 2019 23:57:36 +0100 Subject: [PATCH 01/18] added new signals to database, calling signals from maintenance --- libraries/chain/db_maint.cpp | 5 +++++ libraries/chain/include/graphene/chain/database.hpp | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 7618fda50b..25ae7bacd0 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -44,6 +44,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -1150,6 +1151,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g distribute_fba_balances(*this); create_buyback_orders(*this); + on_maintenance_begin( next_block.id() ); + struct vote_tally_helper { database& d; const global_property_object& props; @@ -1179,6 +1182,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0) + stats.core_in_balance.value; + d.on_voting_stake_calculated( stake_account, opinion_account, voting_stake ); + for( vote_id_type id : opinion_account.options.votes ) { uint32_t offset = id.instance(); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 395818389b..1be112cf9d 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -217,6 +217,16 @@ namespace graphene { namespace chain { */ fc::signal&, const vector&, const flat_set&)> removed_objects; + /** + * Emitted after the calculation of the voting_stake (occurs in the maintenance interval) + */ + fc::signal on_voting_stake_calculated; + + /** + * Emitted after the beginning of the maintenance interval + */ + fc::signal on_maintenance_begin; + //////////////////// db_witness_schedule.cpp //////////////////// /** From f1329e69882b9badac14c2e23e1f08aa887f3381 Mon Sep 17 00:00:00 2001 From: dimfred Date: Sat, 16 Mar 2019 17:31:16 +0100 Subject: [PATCH 02/18] added voting_statistics_object --- .../chain/voting_statistics_object.hpp | 105 ++++++++++++++++++ .../include/graphene/protocol/types.hpp | 3 +- 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 libraries/chain/include/graphene/chain/voting_statistics_object.hpp diff --git a/libraries/chain/include/graphene/chain/voting_statistics_object.hpp b/libraries/chain/include/graphene/chain/voting_statistics_object.hpp new file mode 100644 index 0000000000..a8da8df337 --- /dev/null +++ b/libraries/chain/include/graphene/chain/voting_statistics_object.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 Blockchain Projects B.V. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include + +#include + +#include +#include + +#include +#include + +namespace graphene { namespace chain { + + /** + * @brief tracks the history of the voting stake for an account + * @ingroup object + * @ingroup implementation + * + * The calculation of the voting stake, performed in the maintenance interval, results in the creation or, + * if present, in the update of a voting_statistics_object. + * + * @note By default these objects are not tracked, the voting_stat_plugin must be loaded for these objects to + * be maintained. + */ + class voting_statistics_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = voting_statistics_object_type; + + voting_statistics_object(){} + + /* the block number where the maintenance interval was performed */ + uint32_t block_number; + /* the owner of the stake */ + account_id_type account; + /* the stake which was generated by this account */ + uint64_t stake = 0; + /* the proxy for this account */ + account_id_type proxy = GRAPHENE_PROXY_TO_SELF_ACCOUNT; + /* the accounts which this account was a proxy for with the proxied stakes */ + flat_map< account_id_type, uint64_t > proxy_for; + /* the vote_id's this account was voting for */ + flat_set votes; + + /* returns the total voting stake this account can vote with */ + uint64_t get_total_voting_stake() const + { + uint64_t init = has_proxy() ? 0 : stake; + return boost::accumulate( proxy_for | boost::adaptors::map_values, init ); + } + + inline bool has_proxy() const + { + return GRAPHENE_PROXY_TO_SELF_ACCOUNT != proxy; + } + }; + + + struct by_block_number{}; + typedef multi_index_container< voting_statistics_object, + indexed_by< + ordered_unique< tag, + member< object, object_id_type, &object::id > + >, + ordered_unique< tag, + composite_key< voting_statistics_object, + member< voting_statistics_object, uint32_t, &voting_statistics_object::block_number >, + member< voting_statistics_object, account_id_type, &voting_statistics_object::account > + > + > + > + > voting_statistics_multi_index_type; + + typedef generic_index< voting_statistics_object, voting_statistics_multi_index_type > voting_statistics_index; + +}} // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::voting_statistics_object, (graphene::chain::object), + (block_number)(account)(stake)(proxy)(proxy_for)(votes) ) diff --git a/libraries/protocol/include/graphene/protocol/types.hpp b/libraries/protocol/include/graphene/protocol/types.hpp index b861efc78a..931bbc9908 100644 --- a/libraries/protocol/include/graphene/protocol/types.hpp +++ b/libraries/protocol/include/graphene/protocol/types.hpp @@ -239,7 +239,8 @@ GRAPHENE_DEFINE_IDS(protocol, protocol_ids, /*protocol objects are not prefixed* (vesting_balance) (worker) (balance) - (htlc)) + (htlc) + (voting_statistics)) FC_REFLECT(graphene::protocol::public_key_type, (key_data)) FC_REFLECT(graphene::protocol::public_key_type::binary_key, (data)(check)) From b690a3333f40addc17c9e967360957530c0afbd6 Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 30 Jul 2019 16:47:54 +0200 Subject: [PATCH 03/18] added voteable statistics object --- .../chain/voteable_statistics_object.hpp | 95 +++++++++++++++++++ .../include/graphene/protocol/types.hpp | 3 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 libraries/chain/include/graphene/chain/voteable_statistics_object.hpp diff --git a/libraries/chain/include/graphene/chain/voteable_statistics_object.hpp b/libraries/chain/include/graphene/chain/voteable_statistics_object.hpp new file mode 100644 index 0000000000..44abf4e638 --- /dev/null +++ b/libraries/chain/include/graphene/chain/voteable_statistics_object.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2019 Blockchain Projects B.V. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include + +#include + +#include +#include + +#include + + +namespace graphene { namespace chain { + + /** + * @brief tracks the history of the voting stake for an account + * @ingroup object + * @ingroup implementation + * + * The calculation of the voting stake, performed in the maintenance interval, results + * in the creation or, if present, in the update of a voting_statistics_object. + * + * @note By default these objects are not tracked, the voting_stat_plugin must + * be loaded for these objects to be maintained. + */ + class voteable_statistics_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = voteable_statistics_object_type; + + voteable_statistics_object(){} + + /* the block_num where the maintenance interval was performed */ + uint32_t block_number; + /* vote_id of the voteable_object */ + vote_id_type vote_id; + /* the accounts which this voteable is voted by with the number of votes */ + flat_map< account_id_type, uint64_t > voted_by; + + /* returns the total votes of this object */ + uint64_t get_votes() const + { + return boost::accumulate( voted_by | boost::adaptors::map_values, 0 ); + } + }; + + + //struct by_block_number{}; // declared in voting_statistics_object just let it like this? + typedef multi_index_container< voteable_statistics_object, + indexed_by< + ordered_unique< tag, + member< object, object_id_type, &object::id > + >, + ordered_unique< tag, + composite_key< voteable_statistics_object, + member< voteable_statistics_object, uint32_t, &voteable_statistics_object::block_number >, + member< voteable_statistics_object, vote_id_type, &voteable_statistics_object::vote_id > + > + > + > + > voteable_statistics_multi_index_type; + + typedef generic_index< voteable_statistics_object, + voteable_statistics_multi_index_type + > voteable_statistics_index; + +}} // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::voteable_statistics_object, (graphene::chain::object), + (block_number)(vote_id)(voted_by) ) diff --git a/libraries/protocol/include/graphene/protocol/types.hpp b/libraries/protocol/include/graphene/protocol/types.hpp index 931bbc9908..812c3e65f2 100644 --- a/libraries/protocol/include/graphene/protocol/types.hpp +++ b/libraries/protocol/include/graphene/protocol/types.hpp @@ -240,7 +240,8 @@ GRAPHENE_DEFINE_IDS(protocol, protocol_ids, /*protocol objects are not prefixed* (worker) (balance) (htlc) - (voting_statistics)) + (voting_statistics) + (voteable_statistics)) FC_REFLECT(graphene::protocol::public_key_type, (key_data)) FC_REFLECT(graphene::protocol::public_key_type::binary_key, (data)(check)) From 7c5c124ce84ed852d645d86794a0f728c1e81d8f Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 12 Mar 2019 16:02:17 +0100 Subject: [PATCH 04/18] added voting_stat_plugin --- libraries/chain/db_maint.cpp | 4 +- .../chain/include/graphene/chain/database.hpp | 9 +- libraries/plugins/CMakeLists.txt | 1 + libraries/plugins/voting_stat/CMakeLists.txt | 20 + .../voting_stat/voting_stat_plugin.hpp | 69 ++++ .../voting_stat/voting_stat_plugin.cpp | 369 ++++++++++++++++++ programs/witness_node/CMakeLists.txt | 2 +- programs/witness_node/main.cpp | 5 +- 8 files changed, 474 insertions(+), 5 deletions(-) create mode 100644 libraries/plugins/voting_stat/CMakeLists.txt create mode 100644 libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp create mode 100644 libraries/plugins/voting_stat/voting_stat_plugin.cpp diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 25ae7bacd0..8ed2073aed 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -1151,7 +1151,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g distribute_fba_balances(*this); create_buyback_orders(*this); - on_maintenance_begin( next_block.id() ); + on_maintenance_begin( next_block.block_num() ); struct vote_tally_helper { database& d; @@ -1332,6 +1332,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g // process_budget needs to run at the bottom because // it needs to know the next_maintenance_time process_budget(); + + on_maintenance_end(); } } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 1be112cf9d..761eca51d6 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -225,7 +225,12 @@ namespace graphene { namespace chain { /** * Emitted after the beginning of the maintenance interval */ - fc::signal on_maintenance_begin; + fc::signal on_maintenance_begin; + + /** + * Emitted after the end of the maintenance interval + */ + fc::signal on_maintenance_end; //////////////////// db_witness_schedule.cpp //////////////////// @@ -343,7 +348,7 @@ namespace graphene { namespace chain { * to newly created VBID and return it. * * Otherwise, credit amount to ovbid. - * + * * @return ID of newly created VBO, but only if VBO was created. */ optional< vesting_balance_id_type > deposit_lazy_vesting( diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index 412b185e08..29cfe496a1 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -8,3 +8,4 @@ add_subdirectory( debug_witness ) add_subdirectory( snapshot ) add_subdirectory( es_objects ) add_subdirectory( api_helper_indexes ) +add_subdirectory( voting_stat ) diff --git a/libraries/plugins/voting_stat/CMakeLists.txt b/libraries/plugins/voting_stat/CMakeLists.txt new file mode 100644 index 0000000000..b3dc852938 --- /dev/null +++ b/libraries/plugins/voting_stat/CMakeLists.txt @@ -0,0 +1,20 @@ +file( GLOB HEADER "include/graphene/account_history/*.hpp" ) + +add_library( graphene_voting_stat voting_stat_plugin.cpp ) + +target_link_libraries( graphene_voting_stat graphene_chain graphene_app ) +target_include_directories( graphene_voting_stat + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if(MSVC) + set_source_files_properties( voting_stat_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) +endif(MSVC) + +install( TARGETS + graphene_voting_stat + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib ) + +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/voting_stat" ) diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp new file mode 100644 index 0000000000..f4fae9df8b --- /dev/null +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Blockchain Projects BV. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include + +namespace graphene { namespace voting_stat { + using namespace chain; +// +// Plugins should #define their SPACE_ID's so plugins with +// conflicting SPACE_ID assignments can be compiled into the +// same binary (by simply re-assigning some of the conflicting #defined +// SPACE_ID's in a build script). +// +// Assignment of SPACE_ID's cannot be done at run-time because +// various template automagic depends on them being known at compile +// time. +// +#ifndef VOTING_STAT_SPACE_ID +#define VOTING_STAT_SPACE_ID 4 +#endif + +namespace detail +{ + class voting_stat_plugin_impl; +} + +class voting_stat_plugin : public graphene::app::plugin +{ + public: + voting_stat_plugin(); + virtual ~voting_stat_plugin(); + + std::string plugin_name()const override; + virtual void plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg) override; + virtual void plugin_initialize(const boost::program_options::variables_map& options) override; + virtual void plugin_startup() override; + + friend class detail::voting_stat_plugin_impl; + std::unique_ptr my; +}; + +}} // graphene::voting_stat + + diff --git a/libraries/plugins/voting_stat/voting_stat_plugin.cpp b/libraries/plugins/voting_stat/voting_stat_plugin.cpp new file mode 100644 index 0000000000..5801701595 --- /dev/null +++ b/libraries/plugins/voting_stat/voting_stat_plugin.cpp @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2019 Blockchain Projects BV. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace graphene { namespace voting_stat { + +namespace detail { + +class voting_stat_plugin_impl +{ +public: + voting_stat_plugin_impl(voting_stat_plugin& _plugin) : _self( _plugin ){} + virtual ~voting_stat_plugin_impl(){} + + boost::signals2::connection _on_voting_stake_calc_conn; + std::unique_ptr _on_voting_stake_calc_block; + + uint16_t _maint_counter = 0; + + /** + * plugin parameters + */ + bool _keep_objects_in_db = true; + uint16_t _track_every_x_maint = 12; + bool _track_worker_votes = true; + bool _track_witness_votes = true; + bool _track_committee_votes = true; + + /** + * @brief callback for database::on_stake_calculated + * + * This function is triggered when the calculation of a stake for a given account is done inside the maintenance + * interval. It creates/updates the voting_statistics_object for a stake account. Optionally if a proxy is set + * in the stake account, also the voting_statistics_object for the proxy account is created/updated. + */ + void on_stake_calculated(const account_object& stake_account, const account_object& proxy_account, uint64_t stake); + + /** + * @brief callback for database::on_maintenance_begin + * + * Updates the block number to the one where the maintenance interval occurs and unblocks the + * database::on_stake_calculated signal, so that statistics objects can be created. + */ + void on_maintenance_begin(uint32_t block_num); + + /** + * @brief callback for database::on_maintenance_end + * + * Disconnects the on_stake_calculated callback and deletes all statistics objects if + * _voting_stat_delete_objects_after_interval=true + */ + void on_maintenance_end(); + + void create_voteable_statistics_objects(); + void delete_all_statistics_objects(); + + graphene::chain::database& database() + { + return _self.database(); + } + +private: + voting_stat_plugin& _self; + + uint32_t _maint_block; + bool _create_voteable = false; +}; + +void voting_stat_plugin_impl::on_maintenance_begin(uint32_t block_num) +{ + if( !_keep_objects_in_db ) + delete_all_statistics_objects(); + + if( _maint_counter == _track_every_x_maint ) + { + _on_voting_stake_calc_block->unblock(); + _maint_counter = 0; + _maint_block = block_num; + _create_voteable = true; + } + ++_maint_counter; +} + +void voting_stat_plugin_impl::on_maintenance_end() +{ + _on_voting_stake_calc_block->block(); + if( _create_voteable ) { + _create_voteable = false; + create_voteable_statistics_objects(); + } +} + +void voting_stat_plugin_impl::delete_all_statistics_objects() +{ + auto& db = database(); + + // TODO find better way to delete + const auto& voting_idx = db.get_index_type().indices().get(); + for( const auto& voting_obj : voting_idx ) { + db.remove( voting_obj ); + } + + const auto& voteable_idx = db.get_index_type().indices().get(); + for( const auto& voteable_obj : voteable_idx ) { + db.remove( voteable_obj ); + } +} + +void voting_stat_plugin_impl::create_voteable_statistics_objects() +{ + auto& db = database(); + + // TODO secondary index for workers where current_time < worker_end_time + // will reduce the iteration time + if( _track_worker_votes ) + { + const auto& worker_idx = db.get_index_type().indices().get(); + + auto now = db.head_block_time(); + for( const auto& worker : worker_idx ) + { + if( now > worker.work_end_date ) + continue; + + db.create( [this, &worker]( voteable_statistics_object& o ){ + o.block_number = _maint_block; + o.vote_id = worker.vote_for; + }); + } + } + + if( _track_witness_votes ) + { + const auto& witness_idx = db.get_index_type().indices().get(); + for( const auto& witness : witness_idx ) + { + db.create( [this, &witness]( voteable_statistics_object& o ){ + o.block_number = _maint_block; + o.vote_id = witness.vote_id; + }); + } + } + + if( _track_committee_votes ) + { + const auto& committee_idx = db.get_index_type().indices().get(); + for( const auto& committee_member : committee_idx ) + { + db.create( [this, &committee_member](voteable_statistics_object& o){ + o.block_number = _maint_block; + o.vote_id = committee_member.vote_id; + }); + } + } + + + const auto& voting_stat_idx = db.get_index_type().indices().get(); + auto voting_stat_range = voting_stat_idx.equal_range( _maint_block ); + + const auto& voteable_idx = db.get_index_type().indices().get(); + + for( auto voting_stat_it = voting_stat_range.first; voting_stat_it != voting_stat_range.second; ++voting_stat_it ) + { + uint64_t total_stake = voting_stat_it->get_total_voting_stake(); + if( !total_stake ) + continue; // don't bother inserting a 0 stake + + const flat_set& votes = voting_stat_it->votes; + for( const auto& vote_id : votes ) + { + auto voteable_obj_range = voteable_idx.equal_range( boost::make_tuple( _maint_block, vote_id ) ); + if( voteable_obj_range.first == voteable_obj_range.second ) + continue; // when the obj isn't found it isn't needed hence skip it + + db.modify( *voteable_obj_range.first, + [voting_stat_it, total_stake]( voteable_statistics_object& o ){ + o.voted_by.emplace( voting_stat_it->account, total_stake ); + } + ); + } + } +} + +void voting_stat_plugin_impl::on_stake_calculated( + const account_object& stake_account, + const account_object& proxy_account, + uint64_t stake ) +{ + auto& db = database(); + + account_id_type stake_id = stake_account.id; + account_id_type proxy_id = proxy_account.id; + proxy_id = ( stake_id == proxy_id ? GRAPHENE_PROXY_TO_SELF_ACCOUNT : proxy_id ); + + const auto& voting_stat_idx = db.get_index_type().indices().get(); + + auto stake_stat_range = voting_stat_idx.equal_range( boost::make_tuple( _maint_block, stake_id ) ); + if( stake_stat_range.first == stake_stat_range.second ) + { + db.create( [this, &stake_account, &proxy_id, stake]( voting_statistics_object& o ){ + o.block_number = _maint_block; + o.account = stake_account.id; + o.stake = stake; + o.proxy = proxy_id; + o.votes = stake_account.options.votes; + }); + } + else + { + db.modify( *stake_stat_range.first, + [this, &stake_account, &proxy_id, stake]( voting_statistics_object& o ){ + o.stake = stake; + o.proxy = proxy_id; + o.votes = stake_account.options.votes; + } + ); + } + + if( proxy_id == GRAPHENE_PROXY_TO_SELF_ACCOUNT ) + return; + + auto proxy_stat_range = voting_stat_idx.equal_range( boost::make_tuple( _maint_block, proxy_id ) ); + if( proxy_stat_range.first == proxy_stat_range.second ) + { + db.create( [this, &stake_id, &proxy_id, stake]( voting_statistics_object& o ){ + o.block_number = _maint_block; + o.account = proxy_id; + o.proxy_for.emplace( stake_id, stake ); + }); + } + else + { + db.modify( *proxy_stat_range.first, + [&stake_id, stake]( voting_statistics_object& o ){ + o.proxy_for.emplace( stake_id, stake ); + } + ); + } +} + +} // namespace::detail + + + +voting_stat_plugin::voting_stat_plugin() : + my( new detail::voting_stat_plugin_impl(*this) ) +{ +} + +voting_stat_plugin::~voting_stat_plugin() +{ +} + +std::string voting_stat_plugin::plugin_name()const +{ + return "voting_stat"; +} + +void voting_stat_plugin::plugin_set_program_options( + boost::program_options::options_description& cli, + boost::program_options::options_description& cfg ) +{ + cli.add_options() + ( + "voting-stat-track-every-x-maint", boost::program_options::value(), + "Every x maintenance interval statistic objects will be created (12=2per day)" + ) + ( + "voting-stat-keep-objects-in-db", boost::program_options::value(), + "Every created object will be deleted after the maintenance interval (true)" + ) + ( + "voting-stat-track-worker-votes", boost::program_options::value(), + "Worker votes will be tracked (true)" + ) + ( + "voting-stat-track-witness-votes", boost::program_options::value(), + "Witness votes will be tracked (true)" + ) + ( + "voting-stat-track-committee-votes", boost::program_options::value(), + "Committee votes will be tracked (true)" + ); + cfg.add(cli); +} + +void voting_stat_plugin::plugin_initialize(const boost::program_options::variables_map& options) +{ + auto& db = database(); + db.add_index< primary_index >(); + db.add_index< primary_index >(); + + if( options.count("voting-stat-track-every-x-maint") ){ + my->_track_every_x_maint = options["voting-stat-track-every-x-maint"].as(); + if( my->_track_every_x_maint == 0 ) + my->_track_every_x_maint = 1; + my->_maint_counter = my->_track_every_x_maint; + } + if( options.count("voting-stat-keep-objects-in-db") ){ + my->_keep_objects_in_db = options["voting-stat-keep-objects-in-db"].as(); + } + if( options.count("voting-stat-track-worker-votes") ){ + my->_track_worker_votes = options["voting-stat-track-worker-votes"].as(); + } + if( options.count("voting-stat-track-witness-votes") ){ + my->_track_witness_votes = options["voting-stat-track-witness-votes"].as(); + } + if( options.count("voting-stat-track-committee-votes") ){ + my->_track_committee_votes = options["voting-stat-track-committee-votes"].as(); + } + + my->_on_voting_stake_calc_conn = db.on_voting_stake_calculated.connect( + [&]( const account_object& stake_account, const account_object& proxy_account, const uint64_t stake ){ + my->on_stake_calculated( stake_account, proxy_account, stake ); + } + ); + + my->_on_voting_stake_calc_block = std::unique_ptr( + new boost::signals2::shared_connection_block(my->_on_voting_stake_calc_conn) + ); + + db.on_maintenance_begin.connect( [this](uint32_t block_num){ + my->on_maintenance_begin( block_num ); + }); + + db.on_maintenance_end.connect( [this](){ + my->on_maintenance_end(); + }); +} + +void voting_stat_plugin::plugin_startup() +{ +} + +} } diff --git a/programs/witness_node/CMakeLists.txt b/programs/witness_node/CMakeLists.txt index d671a93c17..2b2b6bef2e 100644 --- a/programs/witness_node/CMakeLists.txt +++ b/programs/witness_node/CMakeLists.txt @@ -13,7 +13,7 @@ endif() target_link_libraries( witness_node PRIVATE graphene_app graphene_delayed_node graphene_account_history graphene_elasticsearch graphene_market_history graphene_grouped_orders graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full graphene_snapshot graphene_es_objects - graphene_api_helper_indexes + graphene_api_helper_indexes graphene_voting_stat fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) install( TARGETS diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index 400d09f0ff..137913c003 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -50,7 +51,7 @@ #include #ifdef WIN32 -# include +# include #else # include #endif @@ -96,8 +97,10 @@ int main(int argc, char** argv) { auto es_objects_plug = node->register_plugin(); auto grouped_orders_plug = node->register_plugin(); auto api_helper_indexes_plug = node->register_plugin(); + auto voting_stat_plug = node->register_plugin(); // add plugin options to config + try { bpo::options_description cli, cfg; From 4336e0067d65300eccf7af990efe091bb26134f5 Mon Sep 17 00:00:00 2001 From: dimfred Date: Sat, 7 Sep 2019 11:54:50 +0200 Subject: [PATCH 05/18] added vot*_objs to es_objects added also switch cases in later commis replaced case label with static constexpr method from abstract object --- libraries/plugins/es_objects/es_objects.cpp | 188 ++++++++++++++------ 1 file changed, 136 insertions(+), 52 deletions(-) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index 79b9f1cdba..a6cfadd142 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include @@ -61,6 +63,10 @@ class es_objects_plugin_impl bool _es_objects_balances = true; bool _es_objects_limit_orders = true; bool _es_objects_asset_bitasset = true; + bool _es_objects_voting_statistics = true; + bool _es_objects_voteable_statistics = true; + bool _es_objects_statistics_delete_allowed = true; + std::string _es_objects_index_prefix = "objects-"; uint32_t _es_objects_start_es_after_block = 0; CURL *curl; // curl handler @@ -141,61 +147,126 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s limit_documents = _es_objects_bulk_replay; - for (auto const &value: ids) { - if (value.is() && _es_objects_proposals) { - auto obj = db.find_object(value); - auto p = static_cast(obj); - if (p != nullptr) { - if (action == "delete") - remove_from_database(p->id, "proposal"); - else - prepareTemplate(*p, "proposal"); + for (auto const &value: ids) + { + switch( value.space_type() ) + { + case( proposal_object::space_id << 8 | proposal_object::type_id ): + { + if( _es_objects_proposals ) { + auto obj = db.find_object(value); + auto p = static_cast(obj); + if (p != nullptr) { + if (action == "delete") + remove_from_database(p->id, "proposal"); + else + prepareTemplate(*p, "proposal"); + } + } + break; + } + case( account_object::space_id << 8 | account_object::type_id ): + { + if( _es_objects_accounts ) { + auto obj = db.find_object(value); + auto a = static_cast(obj); + if (a != nullptr) { + if (action == "delete") + remove_from_database(a->id, "account"); + else + prepareTemplate(*a, "account"); + } + } + break; + } + case( asset_object::space_id << 8 | asset_object::type_id ): + { + if( _es_objects_assets ) { + auto obj = db.find_object(value); + auto a = static_cast(obj); + if (a != nullptr) { + if (action == "delete") + remove_from_database(a->id, "asset"); + else + prepareTemplate(*a, "asset"); + } + } + break; } - } else if (value.is() && _es_objects_accounts) { - auto obj = db.find_object(value); - auto a = static_cast(obj); - if (a != nullptr) { - if (action == "delete") - remove_from_database(a->id, "account"); - else - prepareTemplate(*a, "account"); + case( account_balance_object::space_id << 8 | account_balance_object::type_id ): + { + if( _es_objects_balances ) { + auto obj = db.find_object(value); + auto b = static_cast(obj); + if (b != nullptr) { + if (action == "delete") + remove_from_database(b->id, "balance"); + else + prepareTemplate(*b, "balance"); + } + } + break; } - } else if (value.is() && _es_objects_assets) { - auto obj = db.find_object(value); - auto a = static_cast(obj); - if (a != nullptr) { - if (action == "delete") - remove_from_database(a->id, "asset"); - else - prepareTemplate(*a, "asset"); + case( limit_order_object::space_id << 8 | limit_order_object::type_id ): + { + if( _es_objects_limit_orders ) { + auto obj = db.find_object(value); + auto l = static_cast(obj); + if (l != nullptr) { + if (action == "delete") + remove_from_database(l->id, "limitorder"); + else + prepareTemplate(*l, "limitorder"); + } + } + break; } - } else if (value.is() && _es_objects_balances) { - auto obj = db.find_object(value); - auto b = static_cast(obj); - if (b != nullptr) { - if (action == "delete") - remove_from_database(b->id, "balance"); - else - prepareTemplate(*b, "balance"); + case( asset_bitasset_data_object::space_id << 8 | asset_bitasset_data_object::type_id ): + { + if( _es_objects_asset_bitasset ) { + auto obj = db.find_object(value); + auto ba = static_cast(obj); + if (ba != nullptr) { + if (action == "delete") + remove_from_database(ba->id, "bitasset"); + else + prepareTemplate(*ba, "bitasset"); + } + } + break; } - } else if (value.is() && _es_objects_limit_orders) { - auto obj = db.find_object(value); - auto l = static_cast(obj); - if (l != nullptr) { - if (action == "delete") - remove_from_database(l->id, "limitorder"); - else - prepareTemplate(*l, "limitorder"); + case( voting_statistics_object::space_id << 8 | voting_statistics_object::type_id ): + { + if( _es_objects_voting_statistics ) { + auto obj = db.find_object(value); + auto vs = static_cast(obj); + + if (vs != nullptr) { + if (action == "delete" && _es_objects_statistics_delete_allowed ) // TODO will lead to error when false + remove_from_database(vs->id, "voting-statistics"); + else + prepareTemplate(*vs, "voting-statistics"); + } + } + break; } - } else if (value.is() && _es_objects_asset_bitasset) { - auto obj = db.find_object(value); - auto ba = static_cast(obj); - if (ba != nullptr) { - if (action == "delete") - remove_from_database(ba->id, "bitasset"); - else - prepareTemplate(*ba, "bitasset"); + case( voteable_statistics_object::space_id << 8 | voteable_statistics_object::type_id ): + { + if( _es_objects_voteable_statistics ) { + auto obj = db.find_object(value); + auto vs = static_cast(obj); + + if (vs != nullptr) { + if (action == "delete" && _es_objects_statistics_delete_allowed ) // TODO will lead to error when false + remove_from_database(vs->id, "voteable-statistics"); + else + prepareTemplate(*vs, "voteable-statistics"); + } + } + break; } + default: + break; } } @@ -308,12 +379,16 @@ void es_objects_plugin::plugin_set_program_options( ("es-objects-balances", boost::program_options::value(), "Store balances objects(true)") ("es-objects-limit-orders", boost::program_options::value(), "Store limit order objects(true)") ("es-objects-asset-bitasset", boost::program_options::value(), "Store feed data(true)") + ("es-objects-voting-statistics", boost::program_options::value(), "Store voting statistcs(true)") + ("es-objects-voteable-statistics", boost::program_options::value(), "Store voteable statistcs(true)") + ("es-objects-statistics-delete-allowed", boost::program_options::value(), + "Allows the deletion of statistics objects from es(true)") ("es-objects-index-prefix", boost::program_options::value(), - "Add a prefix to the index(objects-)") + "Add a prefix to the index(objects-)") ("es-objects-keep-only-current", boost::program_options::value(), - "Keep only current state of the objects(true)") + "Keep only current state of the objects(true)") ("es-objects-start-es-after-block", boost::program_options::value(), - "Start doing ES job after block(0)") + "Start doing ES job after block(0)") ; cfg.add(cli); } @@ -350,6 +425,15 @@ void es_objects_plugin::plugin_initialize(const boost::program_options::variable if (options.count("es-objects-asset-bitasset")) { my->_es_objects_asset_bitasset = options["es-objects-asset-bitasset"].as(); } + if (options.count("es-objects-voting-statistics")) { + my->_es_objects_voting_statistics = options["es-objects-voting-statistics"].as(); + } + if (options.count("es-objects-voteable-statistics")) { + my->_es_objects_voteable_statistics = options["es-objects-voteable-statistics"].as(); + } + if (options.count("es-objects-statistics-delete-allowed")) { + my->_es_objects_statistics_delete_allowed = options["es-objects-statistics-delete-allowed"].as(); + } if (options.count("es-objects-index-prefix")) { my->_es_objects_index_prefix = options["es-objects-index-prefix"].as(); } From 07d0a8196c94336d6820c3bb77a0872f3e70a983 Mon Sep 17 00:00:00 2001 From: dimfred Date: Mon, 18 Mar 2019 00:00:34 +0100 Subject: [PATCH 06/18] added test --- tests/CMakeLists.txt | 4 + tests/voting_stat/main.cpp | 757 +++++++++++++++++++++++++++++++++++++ 2 files changed, 761 insertions(+) create mode 100644 tests/voting_stat/main.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d05bf13183..36a1e7abb8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,4 +52,8 @@ target_link_libraries( es_test graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes fc ${PLATFORM_SPECIFIC_LIBS} ) +file( GLOB VOTING_STAT_SOURCES "voting_stat/*.cpp" ) +add_executable( voting_stat_test ${COMMON_SOURCES} ${VOTING_STAT_SOURCES} ) +target_link_libraries( voting_stat_test graphene_chain graphene_app graphene_voting_stat graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) + add_subdirectory( generate_empty_blocks ) diff --git a/tests/voting_stat/main.cpp b/tests/voting_stat/main.cpp new file mode 100644 index 0000000000..fbd4672ef0 --- /dev/null +++ b/tests/voting_stat/main.cpp @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2019 Blockchain Projects BV. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include "../common/database_fixture.hpp" + +#define BOOST_TEST_MODULE Voting Statistics Plugin Test +#include + +using namespace fc; +using namespace graphene::app; +using namespace graphene::chain; +using namespace graphene::chain::test; +using namespace graphene::voting_stat; +using namespace graphene::es_objects; +using namespace graphene::utilities; +namespace bpo = boost::program_options; + +struct voting_stat_fixture : public database_fixture +{ + vote_id_type default_vote_id; + CURL *_curl; + ES _es; + + voting_stat_fixture() + { + try + { + _curl = curl_easy_init(); + _es.curl = _curl; + _es.elasticsearch_url = "http://localhost:9200/"; + _es.index_prefix = "objects-"; + + app.register_plugin( true ); + app.register_plugin( true ); + } + catch(fc::exception &e) + { + edump((e.to_detail_string() )); + } + + }; + + void make_next_maintenance_interval() + { + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + generate_block(); + } + + void set_account_options( account_id_type id, optional proxy = optional() ) + { + witness_id_type wit_id = *db.get_global_properties().active_witnesses.begin(); + default_vote_id = wit_id(db).vote_id; + + account_options opt; + opt.votes = { default_vote_id }; + opt.num_witness = opt.votes.size(); + if( proxy ) + opt.voting_account = *proxy; + + account_update_operation op; + op.account = id; + op.new_options = opt; + + signed_transaction trx; + trx.operations.push_back( op ); + set_expiration( db, trx ); + PUSH_TX( db, trx, ~0 ); + } + + const voting_statistics_object& get_voting_statistics_object( account_id_type id ) + { + const auto& idx = db.get_index_type().indices().get(); + auto last_block = idx.rbegin()->block_number; + auto range = idx.equal_range( boost::make_tuple( last_block, id ) ); + if( range.first == range.second ) + BOOST_FAIL( "object could not be found, which should never happen" ); + return *range.first; + } +}; + +BOOST_FIXTURE_TEST_SUITE( voting_stat_tests, voting_stat_fixture ) + +BOOST_AUTO_TEST_CASE( test_voting_statistics_object_tracking_without_proxy ) +{ try { + + bpo::options_description cli; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "1", + "--voting-stat-keep-objects-in-db", "true" + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + + ACTOR( alice ); + set_account_options( alice_id ); + + + const auto& alice_acc = alice_id(db); + BOOST_CHECK( *alice_acc.options.votes.begin() == default_vote_id ); + BOOST_CHECK( alice_acc.options.voting_account == GRAPHENE_PROXY_TO_SELF_ACCOUNT ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + const auto& alice_stat1 = get_voting_statistics_object( alice_id ); + + BOOST_CHECK( alice_stat1.proxy == GRAPHENE_PROXY_TO_SELF_ACCOUNT ); + BOOST_CHECK( !alice_stat1.has_proxy() ); + BOOST_CHECK( alice_stat1.proxy_for.empty() ); + BOOST_CHECK( alice_stat1.stake == 1 ); + BOOST_CHECK( *alice_stat1.votes.begin() == default_vote_id ); + BOOST_CHECK( alice_stat1.get_total_voting_stake() == 1 ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + const auto& alice_stat2 = get_voting_statistics_object( alice_id ); + + BOOST_CHECK( alice_stat2.proxy == GRAPHENE_PROXY_TO_SELF_ACCOUNT ); + BOOST_CHECK( !alice_stat2.has_proxy() ); + BOOST_CHECK( alice_stat2.proxy_for.empty() ); + BOOST_CHECK( alice_stat2.stake == 2 ); + BOOST_CHECK( *alice_stat2.votes.begin() == default_vote_id ); + BOOST_CHECK( alice_stat2.get_total_voting_stake() == 2 ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_delete_after_interval ) +{ try { + + bpo::options_description cli_vs; + bpo::options_description cli_es; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli_vs, cfg ); + + auto es_objects = app.get_plugin("es_objects"); + es_objects->plugin_set_program_options( cli_es, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "1", + "--voting-stat-keep-objects-in-db", "false", + "--voting-stat-track-witness-votes", "false", + "--voting-stat-track-committee-votes", "false", + "--voting-stat-track-worker-votes", "false", + + "--es-objects-bulk-replay", "1", + "--es-objects-proposals", "false", + "--es-objects-accounts", "false", + "--es-objects-assets", "false", + "--es-objects-balances", "false", + "--es-objects-limit-orders", "false", + "--es-objects-asset-bitasset", "false", + "--es-objects-keep-only-current", "true", + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + auto objects_deleted = graphene::utilities::deleteAll(_es); + if( !objects_deleted ) + BOOST_FAIL( "elastic search DB could not be deleted" ); + + + ACTORS( (alice)(bob)(charlie) ); + transfer( committee_account, alice_id, asset(1) ); + transfer( committee_account, bob_id, asset(2) ); + transfer( committee_account, charlie_id, asset(3) ); + + // proxy chain: alice => bob => charlie + set_account_options( alice_id, bob_id ); + set_account_options( bob_id, charlie_id ); + set_account_options( charlie_id ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( alice_stat.has_proxy() ); + BOOST_CHECK( alice_stat.proxy == bob_id ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == 0 ); + + BOOST_CHECK( bob_stat.has_proxy() ); + BOOST_CHECK( bob_stat.proxy == charlie_id ); + auto alice_proxied = *bob_stat.proxy_for.begin(); + BOOST_CHECK( alice_proxied.first == alice_id && alice_proxied.second == 1 ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 1 ); + + BOOST_CHECK( !charlie_stat.has_proxy() ); + auto bob_proxied = *charlie_stat.proxy_for.begin(); + BOOST_CHECK( bob_proxied.first == bob_id && bob_proxied.second == 2 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == (2 + 3) ); + } + + // proxy: alice => alice; bob => charlie; + set_account_options( alice_id, GRAPHENE_PROXY_TO_SELF_ACCOUNT ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( !alice_stat.has_proxy() ); + BOOST_CHECK( alice_stat.proxy_for.empty() ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == 1 ); + + BOOST_CHECK( bob_stat.has_proxy() ); + BOOST_CHECK( bob_stat.proxy_for.empty() ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 0 ); + + BOOST_CHECK( !charlie_stat.has_proxy() ); + auto bob_proxied = *charlie_stat.proxy_for.begin(); + BOOST_CHECK( bob_proxied.first == bob_id && bob_proxied.second == 2 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == (2 + 3) ); + } + + // proxy: alice => alice; bob => charlie; charlie => alice; stake increase + set_account_options( charlie_id, alice_id ); + transfer( committee_account, alice_id, asset(10) ); + transfer( committee_account, bob_id, asset(20) ); + transfer( committee_account, charlie_id, asset(30) ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( !alice_stat.has_proxy() ); + auto charlie_proxied = *alice_stat.proxy_for.begin(); + BOOST_CHECK( charlie_proxied.first == charlie_id && charlie_proxied.second == 33 ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == (11 + 33) ); + + BOOST_CHECK( bob_stat.has_proxy() ); + BOOST_CHECK( bob_stat.proxy_for.empty() ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 0 ); + BOOST_CHECK( bob_stat.stake == 22 ); + + BOOST_CHECK( charlie_stat.has_proxy() ); + auto bob_proxied = *charlie_stat.proxy_for.begin(); + BOOST_CHECK( bob_proxied.first == bob_id && bob_proxied.second == 22 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == 22 ); + } + + // only stake increase + transfer( committee_account, alice_id, asset(100) ); + transfer( committee_account, bob_id, asset(200) ); + transfer( committee_account, charlie_id, asset(300) ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( alice_stat.stake == 111 ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == (111 + 333) ); + BOOST_CHECK( bob_stat.stake == 222 ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 0 ); + BOOST_CHECK( charlie_stat.stake == 333 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == 222 ); + } + + fc::usleep(fc::milliseconds(2000)); + string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + _es.endpoint = _es.index_prefix + "*/data/_count"; + _es.query = query; + auto res = graphene::utilities::simpleQuery(_es); + variant j = fc::json::from_string(res); + BOOST_CHECK( j["count"].as_int64() == 12 ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_keep_after_interval ) +{ try { + + // this test is the same as above just that objects are not deleted with each interval + + bpo::options_description cli_vs; + bpo::options_description cli_es; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli_vs, cfg ); + + auto es_objects = app.get_plugin("es_objects"); + es_objects->plugin_set_program_options( cli_es, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "1", + "--voting-stat-keep-objects-in-db", "true", + "--voting-stat-track-witness-votes", "false", + "--voting-stat-track-committee-votes", "false", + "--voting-stat-track-worker-votes", "false", + + "--es-objects-bulk-replay", "1", + "--es-objects-proposals", "false", + "--es-objects-accounts", "false", + "--es-objects-assets", "false", + "--es-objects-balances", "false", + "--es-objects-limit-orders", "false", + "--es-objects-asset-bitasset", "false", + "--es-objects-keep-only-current", "true", + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + auto objects_deleted = graphene::utilities::deleteAll(_es); + if( !objects_deleted ) + BOOST_FAIL( "elastic search DB could not be deleted" ); + + + ACTORS( (alice)(bob)(charlie) ); + transfer( committee_account, alice_id, asset(1) ); + transfer( committee_account, bob_id, asset(2) ); + transfer( committee_account, charlie_id, asset(3) ); + + // proxy chain: alice => bob => charlie + set_account_options( alice_id, bob_id ); + set_account_options( bob_id, charlie_id ); + set_account_options( charlie_id ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( alice_stat.has_proxy() ); + BOOST_CHECK( alice_stat.proxy == bob_id ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == 0 ); + + BOOST_CHECK( bob_stat.has_proxy() ); + BOOST_CHECK( bob_stat.proxy == charlie_id ); + auto alice_proxied = *bob_stat.proxy_for.begin(); + BOOST_CHECK( alice_proxied.first == alice_id && alice_proxied.second == 1 ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 1 ); + + BOOST_CHECK( !charlie_stat.has_proxy() ); + auto bob_proxied = *charlie_stat.proxy_for.begin(); + BOOST_CHECK( bob_proxied.first == bob_id && bob_proxied.second == 2 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == (2 + 3) ); + } + + // proxy: alice => alice; bob => charlie; + set_account_options( alice_id, GRAPHENE_PROXY_TO_SELF_ACCOUNT ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( !alice_stat.has_proxy() ); + BOOST_CHECK( alice_stat.proxy_for.empty() ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == 1 ); + + BOOST_CHECK( bob_stat.has_proxy() ); + BOOST_CHECK( bob_stat.proxy_for.empty() ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 0 ); + + BOOST_CHECK( !charlie_stat.has_proxy() ); + auto bob_proxied = *charlie_stat.proxy_for.begin(); + BOOST_CHECK( bob_proxied.first == bob_id && bob_proxied.second == 2 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == (2 + 3) ); + } + + // proxy: alice => alice; bob => charlie; charlie => alice; stake increase + set_account_options( charlie_id, alice_id ); + transfer( committee_account, alice_id, asset(10) ); + transfer( committee_account, bob_id, asset(20) ); + transfer( committee_account, charlie_id, asset(30) ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( !alice_stat.has_proxy() ); + auto charlie_proxied = *alice_stat.proxy_for.begin(); + BOOST_CHECK( charlie_proxied.first == charlie_id && charlie_proxied.second == 33 ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == (11 + 33) ); + + BOOST_CHECK( bob_stat.has_proxy() ); + BOOST_CHECK( bob_stat.proxy_for.empty() ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 0 ); + BOOST_CHECK( bob_stat.stake == 22 ); + + BOOST_CHECK( charlie_stat.has_proxy() ); + auto bob_proxied = *charlie_stat.proxy_for.begin(); + BOOST_CHECK( bob_proxied.first == bob_id && bob_proxied.second == 22 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == 22 ); + } + + // only stake increase + transfer( committee_account, alice_id, asset(100) ); + transfer( committee_account, bob_id, asset(200) ); + transfer( committee_account, charlie_id, asset(300) ); + + make_next_maintenance_interval(); + { + const auto& alice_stat = get_voting_statistics_object( alice_id ); + const auto& bob_stat = get_voting_statistics_object( bob_id ); + const auto& charlie_stat = get_voting_statistics_object( charlie_id ); + + BOOST_CHECK( alice_stat.stake == 111 ); + BOOST_CHECK( alice_stat.get_total_voting_stake() == (111 + 333) ); + BOOST_CHECK( bob_stat.stake == 222 ); + BOOST_CHECK( bob_stat.get_total_voting_stake() == 0 ); + BOOST_CHECK( charlie_stat.stake == 333 ); + BOOST_CHECK( charlie_stat.get_total_voting_stake() == 222 ); + } + + fc::usleep(fc::milliseconds(2000)); + string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + _es.endpoint = _es.index_prefix + "*/data/_count"; + _es.query = query; + auto res = graphene::utilities::simpleQuery(_es); + variant j = fc::json::from_string(res); + BOOST_CHECK( j["count"].as_int64() == 12 ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( test_voteable_objects_tracking_with_es ) +{ try { + + bpo::options_description cli_vs; + bpo::options_description cli_es; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli_vs, cfg ); + + auto es_objects = app.get_plugin("es_objects"); + es_objects->plugin_set_program_options( cli_es, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "1", + "--voting-stat-keep-objects-in-db", "false", + "--voting-stat-track-worker-votes", "true", + "--voting-stat-track-witness-votes", "true", + "--voting-stat-track-committee-votes", "true", + + "--es-objects-voting-statistics", "true", + "--es-objects-voteable-statistics", "true", + "--es-objects-statistics-delete-allowed", "false", + + "--es-objects-bulk-replay", "1", + "--es-objects-proposals", "false", + "--es-objects-accounts", "false", + "--es-objects-assets", "false", + "--es-objects-balances", "false", + "--es-objects-limit-orders", "false", + "--es-objects-asset-bitasset", "false", + "--es-objects-keep-only-current", "true", + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + auto objects_deleted = graphene::utilities::deleteAll(_es); + if( !objects_deleted ) + BOOST_FAIL( "elastic search DB could not be deleted" ); + + + ACTOR( alice ); + uint64_t alice_stake = 100; + upgrade_to_lifetime_member( alice_id ); + transfer( committee_account, alice_id, asset(alice_stake) ); + set_account_options( alice_id ); + + create_worker( alice_id ); + create_worker( alice_id ); + + + const auto& voteable_idx = db.get_index_type().indices().get(); + const auto& witness_idx = db.get_index_type().indices().get(); + const auto& committee_idx = db.get_index_type().indices().get(); + const auto& worker_idx = db.get_index_type().indices().get(); + + uint64_t num_witnesses = witness_idx.size(); + uint64_t num_committee_members = committee_idx.size(); + uint64_t num_workers = worker_idx.size(); + + uint64_t expected_voteables = num_witnesses + num_committee_members + num_workers; + + make_next_maintenance_interval(); + BOOST_CHECK( voteable_idx.size() == expected_voteables ); + auto last_block = voteable_idx.rbegin()->block_number; + auto default_voteable_votes = voteable_idx.equal_range( boost::make_tuple( last_block, default_vote_id) ) + .first->get_votes(); + BOOST_CHECK( default_voteable_votes == alice_stake ); + + make_next_maintenance_interval(); + BOOST_CHECK( voteable_idx.size() == expected_voteables ); + + + auto expected_objs_in_es = 2*(expected_voteables + 1); + + fc::usleep(fc::milliseconds(2000)); + string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + _es.endpoint = _es.index_prefix + "*/data/_count"; + _es.query = query; + auto res = graphene::utilities::simpleQuery(_es); + variant j = fc::json::from_string(res); + uint64_t obj_count = j["count"].as_uint64(); + BOOST_CHECK( obj_count == expected_objs_in_es ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( test_voting_stat_plugin_track_every_x_interval ) +{ try { + + bpo::options_description cli; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "2", + "--voting-stat-keep-objects-in-db", "true" + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + + ACTOR( alice ); + set_account_options( alice_id ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + const auto& alice_stat1 = get_voting_statistics_object( alice_id ); + BOOST_CHECK( alice_stat1.stake == 1 ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + const auto& alice_stat2 = get_voting_statistics_object( alice_id ); + // since this interval is even it should not be tracked + BOOST_CHECK( alice_stat2.stake == 1 ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + const auto& alice_stat3 = get_voting_statistics_object( alice_id ); + // this should result in the correct object since all odd intervals are tracked + BOOST_CHECK( alice_stat3.stake == 3 ); + +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( test_delete_after_interval_and_pushed_to_es ) +{ try { + + bpo::options_description cli_vs; + bpo::options_description cli_es; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli_vs, cfg ); + + auto es_objects = app.get_plugin("es_objects"); + es_objects->plugin_set_program_options( cli_es, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "1", + "--voting-stat-keep-objects-in-db", "false", + + "--es-objects-voting-statistics", "true", + "--es-objects-voteable-statistics", "false", + "--es-objects-statistics-delete-allowed", "false", + + "--es-objects-bulk-replay", "1", + "--es-objects-proposals", "false", + "--es-objects-accounts", "false", + "--es-objects-assets", "false", + "--es-objects-balances", "false", + "--es-objects-limit-orders", "false", + "--es-objects-asset-bitasset", "false", + "--es-objects-keep-only-current", "true", + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + auto objects_deleted = graphene::utilities::deleteAll(_es); + if( !objects_deleted ) + BOOST_FAIL( "elastic search DB could not be deleted" ); + + + const auto& idx = db.get_index_type().indices().get(); + ACTOR( alice ); + set_account_options( alice_id ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + BOOST_CHECK( idx.size() == 1 ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + BOOST_CHECK( idx.size() == 1 ); + + + transfer( committee_account, alice_id, asset(1) ); + make_next_maintenance_interval(); + BOOST_CHECK( idx.size() == 1 ); + + + fc::usleep(fc::milliseconds(2000)); + string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + _es.endpoint = _es.index_prefix + "*/data/_count"; + _es.query = query; + auto res = graphene::utilities::simpleQuery(_es); + variant j = fc::json::from_string(res); + int64_t obj_count = j["count"].as_int64(); + BOOST_CHECK( obj_count == 3 ); + +} FC_LOG_AND_RETHROW() } + +/* +// TODO REMOVE +BOOST_AUTO_TEST_CASE( test_indices ) +{ try { + + bpo::options_description cli_vs; + bpo::options_description cli_es; + bpo::options_description cfg; + + auto voting_stat = app.get_plugin("voting_stat"); + voting_stat->plugin_set_program_options( cli_vs, cfg ); + + const char* const plugin_argv[]{ "voting_stat", + "--voting-stat-track-every-x-maint", "1", + "--voting-stat-keep-objects-in-db", "true", + }; + int plugin_argc = sizeof(plugin_argv)/sizeof(char*); + + bpo::variables_map var_map; + bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); + app.initialize_plugins( var_map ); + + + ACTORS((alice)(bob)); + + transfer( committee_account, alice_id, asset(1) ); + transfer( committee_account, bob_id, asset(1) ); + make_next_maintenance_interval(); + + transfer( committee_account, alice_id, asset(1) ); + transfer( committee_account, bob_id, asset(1) ); + make_next_maintenance_interval(); + + transfer( committee_account, alice_id, asset(1) ); + transfer( committee_account, bob_id, asset(1) ); + make_next_maintenance_interval(); + + const auto& block_idx = db.get_index_type().indices().get(); + edump(("BLOCK")); + for( const auto& o : block_idx ) + edump((o.block_number)(o.account)); + + auto beg = block_idx.lower_bound( block_idx.begin()->block_number ); + auto end = block_idx.upper_bound( block_idx.begin()->block_number ); + + edump(("BLOCK BOUND")); + for(; beg != end; ++beg ) + edump((beg->block_number)(beg->account)); + + + edump(("RANGEPLAY")); + auto last_block = block_idx.begin()->block_number; + auto block_range = block_idx.equal_range( last_block ); + for( auto o = block_range.first; o != block_range.second; ++o ) { + edump((o->block_number)(o->account)); + } + + for( const auto& o : block_idx ) + db.remove(o); + + auto range = block_idx.equal_range( boost::make_tuple( last_block, alice_id ) ); + edump((range.first==range.second)); + + uint64_t delt = std::distance( range.first, range.second ); + edump((delt)); + + //edump((range.first->block_number)(range.first->account)); + +} FC_LOG_AND_RETHROW() } +*/ + +BOOST_AUTO_TEST_SUITE_END() From 43ef52176168d350bf2f3c31254fdfb1918a104c Mon Sep 17 00:00:00 2001 From: dimfred Date: Sat, 7 Sep 2019 11:47:27 +0200 Subject: [PATCH 07/18] add here? --- libraries/chain/db_notify.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index d17bec6821..a4eb36bc67 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -275,10 +275,10 @@ struct get_impacted_account_visitor } void operator()( const htlc_extend_operation& op ) { - _impacted.insert( op.fee_payer() ); + _impacted.insert( op.fee_payer() ); } - void operator()( const htlc_refund_operation& op ) - { + void operator()( const htlc_refund_operation& op ) + { _impacted.insert( op.fee_payer() ); } }; @@ -375,6 +375,9 @@ void get_relevant_accounts( const object* obj, flat_set& accoun accounts.insert( htlc_obj->transfer.to ); break; } + /* + add voting and voteable here? + */ } } else if( obj->id.space() == implementation_ids ) @@ -455,7 +458,7 @@ void database::notify_on_pending_transaction( const signed_transaction& tx ) void database::notify_changed_objects() { try { - if( _undo_db.enabled() ) + if( _undo_db.enabled() ) { const auto& head_undo = _undo_db.head(); From 47861c97790f284e9c8b1ce069e8d5f53049ee3f Mon Sep 17 00:00:00 2001 From: dimfred Date: Sat, 7 Sep 2019 11:49:12 +0200 Subject: [PATCH 08/18] static space_type for abstract_object (optional) --- libraries/db/include/graphene/db/object.hpp | 5 +++++ libraries/plugins/es_objects/es_objects.cpp | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/libraries/db/include/graphene/db/object.hpp b/libraries/db/include/graphene/db/object.hpp index b5a6724a2b..4c2d5580fb 100644 --- a/libraries/db/include/graphene/db/object.hpp +++ b/libraries/db/include/graphene/db/object.hpp @@ -101,6 +101,11 @@ namespace graphene { namespace db { } virtual variant to_variant()const { return variant( static_cast(*this), MAX_NESTING ); } virtual vector pack()const { return fc::raw::pack( static_cast(*this) ); } + + static constexpr uint16_t space_type() + { + return (uint16_t)DerivedClass::space_id << 8 | DerivedClass::type_id; + } }; typedef flat_map annotation_map; diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index a6cfadd142..14f9b1b17b 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -151,7 +151,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s { switch( value.space_type() ) { - case( proposal_object::space_id << 8 | proposal_object::type_id ): + case( proposal_object::space_type() ): { if( _es_objects_proposals ) { auto obj = db.find_object(value); @@ -165,7 +165,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( account_object::space_id << 8 | account_object::type_id ): + case( account_object::space_type() ): { if( _es_objects_accounts ) { auto obj = db.find_object(value); @@ -179,7 +179,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( asset_object::space_id << 8 | asset_object::type_id ): + case( asset_object::space_type() ): { if( _es_objects_assets ) { auto obj = db.find_object(value); @@ -193,7 +193,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( account_balance_object::space_id << 8 | account_balance_object::type_id ): + case( account_balance_object::space_type() ): { if( _es_objects_balances ) { auto obj = db.find_object(value); @@ -207,7 +207,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( limit_order_object::space_id << 8 | limit_order_object::type_id ): + case( limit_order_object::space_type() ): { if( _es_objects_limit_orders ) { auto obj = db.find_object(value); @@ -221,7 +221,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( asset_bitasset_data_object::space_id << 8 | asset_bitasset_data_object::type_id ): + case( asset_bitasset_data_object::space_type() ): { if( _es_objects_asset_bitasset ) { auto obj = db.find_object(value); @@ -235,7 +235,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( voting_statistics_object::space_id << 8 | voting_statistics_object::type_id ): + case( voting_statistics_object::space_type() ): { if( _es_objects_voting_statistics ) { auto obj = db.find_object(value); @@ -250,7 +250,7 @@ bool es_objects_plugin_impl::index_database(const vector& ids, s } break; } - case( voteable_statistics_object::space_id << 8 | voteable_statistics_object::type_id ): + case( voteable_statistics_object::space_type() ): { if( _es_objects_voteable_statistics ) { auto obj = db.find_object(value); From 2fe34b1b0752edffba8c1229c599bca1fe0b9678 Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 13:55:13 +0200 Subject: [PATCH 09/18] added voting stat to plugin README --- libraries/plugins/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/plugins/README.md b/libraries/plugins/README.md index 411bab9b13..90bd872552 100644 --- a/libraries/plugins/README.md +++ b/libraries/plugins/README.md @@ -8,15 +8,16 @@ Plugins are optional to run by node operator according to their needs. However, # Available Plugins -Folder | Name | Description | Category | Status | SpaceID +Folder | Name | Description | Category | Status | SpaceID -----------------------------------|--------------------------|-----------------------------------------------------------------------------|----------------|---------------|--------------| [account_history](account_history) | Account History | Save account history data | History | Stable | 4 -[api_helper_indexes](api_helper_indexes) | API Helper Indexes | Provides some helper indexes used by various API calls | Database API | Stable | +[api_helper_indexes](api_helper_indexes) | API Helper Indexes | Provides some helper indexes used by various API calls | Database API | Stable | [debug_witness](debug_witness) | Debug Witness | Run "what-if" tests | Debug | Stable | [delayed_node](delayed_node) | Delayed Node | Avoid forks by running a several times confirmed and delayed blockchain | Business | Stable | [elasticsearch](elasticsearch) | ElasticSearch Operations | Save account history data into elasticsearch database | History | Experimental | 6 [es_objects](es_objects) | ElasticSearch Objects | Save selected objects into elasticsearch database | History | Experimental | [grouped_orders](grouped_orders) | Grouped Orders | Expose api to create a grouped order book of bitshares markets | Market data | Experimental | [market_history](market_history) | Market History | Save market history data | Market data | Stable | 5 -[snapshot](snapshot) | Snapshot | Get a json of all objects in blockchain at a specificed time or block | Debug | Stable | -[witness](witness) | Witness | Generate and sign blocks | Block producer | Stable | +[snapshot](snapshot) | Snapshot | Get a json of all objects in blockchain at a specificed time or block | Debug | Stable | +[witness](witness) | Witness | Generate and sign blocks | Block producer | Stable | +[voting_stat](voting_stat) | Voting Statistics | Generate objects from voting related data (works together with es objects) | History | Experimental | 7 \ No newline at end of file From 3fa558191859a529ce9ca9875e1b9735f474e92f Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 13:52:52 +0200 Subject: [PATCH 10/18] moved voting stat obj to plugin dir, changed ids to match plugin space --- .../voting_stat}/voting_statistics_object.hpp | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) rename libraries/{chain/include/graphene/chain => plugins/voting_stat/include/graphene/voting_stat}/voting_statistics_object.hpp (81%) diff --git a/libraries/chain/include/graphene/chain/voting_statistics_object.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp similarity index 81% rename from libraries/chain/include/graphene/chain/voting_statistics_object.hpp rename to libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp index a8da8df337..32491ebba1 100644 --- a/libraries/chain/include/graphene/chain/voting_statistics_object.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Blockchain Projects B.V. + * Copyright (c) 2019 Blockchain Projects BV. * * The MIT License * @@ -22,20 +22,35 @@ * THE SOFTWARE. */ #pragma once - #include #include +#include +#include +#include #include +#include + #include #include #include -#include -namespace graphene { namespace chain { +using graphene::chain::object_id_type; +using graphene::chain::account_id_type; +using graphene::chain::vote_id_type; +using graphene::db::object; +using graphene::db::abstract_object; +using graphene::db::generic_index; +using graphene::db::by_id; + +using namespace boost::multi_index; +using boost::container::flat_map; +using boost::container::flat_set; + +namespace graphene { namespace voting_stat { /** * @brief tracks the history of the voting stake for an account * @ingroup object @@ -50,8 +65,8 @@ namespace graphene { namespace chain { class voting_statistics_object : public abstract_object { public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = voting_statistics_object_type; + static const uint8_t space_id = VOTING_STAT_SPACE_ID; + static const uint8_t type_id = voting_stat_object_type_ids::voting_statistics_object_type_id; voting_statistics_object(){} @@ -65,6 +80,8 @@ namespace graphene { namespace chain { account_id_type proxy = GRAPHENE_PROXY_TO_SELF_ACCOUNT; /* the accounts which this account was a proxy for with the proxied stakes */ flat_map< account_id_type, uint64_t > proxy_for; + + /* the vote_id's this account was voting for */ flat_set votes; @@ -75,7 +92,7 @@ namespace graphene { namespace chain { return boost::accumulate( proxy_for | boost::adaptors::map_values, init ); } - inline bool has_proxy() const + bool has_proxy() const { return GRAPHENE_PROXY_TO_SELF_ACCOUNT != proxy; } @@ -101,5 +118,5 @@ namespace graphene { namespace chain { }} // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::voting_statistics_object, (graphene::chain::object), +FC_REFLECT_DERIVED( graphene::voting_stat::voting_statistics_object, (graphene::chain::object), (block_number)(account)(stake)(proxy)(proxy_for)(votes) ) From 8ff6922327a4ecdfd40d66372c0f0fe40e0466af Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 13:54:37 +0200 Subject: [PATCH 11/18] moved voteable obj to plugin dir and changed ids to match plugin space --- .../voteable_statistics_object.hpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) rename libraries/{chain/include/graphene/chain => plugins/voting_stat/include/graphene/voting_stat}/voteable_statistics_object.hpp (80%) diff --git a/libraries/chain/include/graphene/chain/voteable_statistics_object.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp similarity index 80% rename from libraries/chain/include/graphene/chain/voteable_statistics_object.hpp rename to libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp index 44abf4e638..a7a5f7e4ea 100644 --- a/libraries/chain/include/graphene/chain/voteable_statistics_object.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Blockchain Projects B.V. + * Copyright (c) 2019 Blockchain Projects BV. * * The MIT License * @@ -22,22 +22,26 @@ * THE SOFTWARE. */ #pragma once - #include #include #include +#include + #include #include #include +using graphene::chain::account_id_type; +using graphene::chain::vote_id_type; +using graphene::db::generic_index; -namespace graphene { namespace chain { +namespace graphene { namespace voting_stat { /** - * @brief tracks the history of the voting stake for an account + * @brief tracks the history voting related data per account * @ingroup object * @ingroup implementation * @@ -47,11 +51,11 @@ namespace graphene { namespace chain { * @note By default these objects are not tracked, the voting_stat_plugin must * be loaded for these objects to be maintained. */ - class voteable_statistics_object : public abstract_object + class voteable_statistics_object : public db::abstract_object { public: - static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = voteable_statistics_object_type; + static const uint8_t space_id = VOTING_STAT_SPACE_ID; + static const uint8_t type_id = voting_stat_object_type_ids::voteable_statistics_object_type_id; voteable_statistics_object(){} @@ -70,7 +74,7 @@ namespace graphene { namespace chain { }; - //struct by_block_number{}; // declared in voting_statistics_object just let it like this? + //struct by_block_number{}; // declared in voting_statistics_object typedef multi_index_container< voteable_statistics_object, indexed_by< ordered_unique< tag, @@ -85,11 +89,11 @@ namespace graphene { namespace chain { > > voteable_statistics_multi_index_type; - typedef generic_index< voteable_statistics_object, + typedef db::generic_index< voteable_statistics_object, voteable_statistics_multi_index_type > voteable_statistics_index; }} // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::voteable_statistics_object, (graphene::chain::object), +FC_REFLECT_DERIVED( graphene::voting_stat::voteable_statistics_object, (graphene::chain::object), (block_number)(vote_id)(voted_by) ) From 3736c9050a983c198149aae0049591a48cd26e8c Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 14:01:49 +0200 Subject: [PATCH 12/18] chnaged the plugin space removed objs from types --- libraries/plugins/voting_stat/CMakeLists.txt | 8 ++++---- .../include/graphene/voting_stat/voting_stat_plugin.hpp | 9 +++++++-- libraries/plugins/voting_stat/voting_stat_plugin.cpp | 7 +++++-- libraries/protocol/include/graphene/protocol/types.hpp | 4 +--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/plugins/voting_stat/CMakeLists.txt b/libraries/plugins/voting_stat/CMakeLists.txt index b3dc852938..d59937295f 100644 --- a/libraries/plugins/voting_stat/CMakeLists.txt +++ b/libraries/plugins/voting_stat/CMakeLists.txt @@ -1,18 +1,18 @@ -file( GLOB HEADER "include/graphene/account_history/*.hpp" ) +file( GLOB HEADER "include/graphene/voting_stat/*.hpp" ) add_library( graphene_voting_stat voting_stat_plugin.cpp ) -target_link_libraries( graphene_voting_stat graphene_chain graphene_app ) +target_link_libraries( graphene_voting_stat graphene_chain graphene_app fc ) target_include_directories( graphene_voting_stat PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) - + if(MSVC) set_source_files_properties( voting_stat_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) install( TARGETS graphene_voting_stat - + RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp index f4fae9df8b..55faed4a80 100644 --- a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp @@ -27,7 +27,6 @@ #include namespace graphene { namespace voting_stat { - using namespace chain; // // Plugins should #define their SPACE_ID's so plugins with // conflicting SPACE_ID assignments can be compiled into the @@ -39,9 +38,15 @@ namespace graphene { namespace voting_stat { // time. // #ifndef VOTING_STAT_SPACE_ID -#define VOTING_STAT_SPACE_ID 4 +#define VOTING_STAT_SPACE_ID 7 #endif +enum voting_stat_object_type_ids +{ + voting_statistics_object_type_id, + voteable_statistics_object_type_id +}; + namespace detail { class voting_stat_plugin_impl; diff --git a/libraries/plugins/voting_stat/voting_stat_plugin.cpp b/libraries/plugins/voting_stat/voting_stat_plugin.cpp index 5801701595..447e61876b 100644 --- a/libraries/plugins/voting_stat/voting_stat_plugin.cpp +++ b/libraries/plugins/voting_stat/voting_stat_plugin.cpp @@ -23,8 +23,8 @@ */ #include -#include -#include +#include +#include #include #include #include @@ -38,6 +38,9 @@ namespace graphene { namespace voting_stat { +using namespace graphene::chain; +using graphene::db::primary_index; + namespace detail { class voting_stat_plugin_impl diff --git a/libraries/protocol/include/graphene/protocol/types.hpp b/libraries/protocol/include/graphene/protocol/types.hpp index 812c3e65f2..b861efc78a 100644 --- a/libraries/protocol/include/graphene/protocol/types.hpp +++ b/libraries/protocol/include/graphene/protocol/types.hpp @@ -239,9 +239,7 @@ GRAPHENE_DEFINE_IDS(protocol, protocol_ids, /*protocol objects are not prefixed* (vesting_balance) (worker) (balance) - (htlc) - (voting_statistics) - (voteable_statistics)) + (htlc)) FC_REFLECT(graphene::protocol::public_key_type, (key_data)) FC_REFLECT(graphene::protocol::public_key_type::binary_key, (data)(check)) From c53bf9a444349c9a2b870825a074e5a4cb23938e Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 13:56:20 +0200 Subject: [PATCH 13/18] es objs --- libraries/plugins/es_objects/CMakeLists.txt | 2 +- libraries/plugins/es_objects/es_objects.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/plugins/es_objects/CMakeLists.txt b/libraries/plugins/es_objects/CMakeLists.txt index 2cae2ffde4..ba4e2cd379 100644 --- a/libraries/plugins/es_objects/CMakeLists.txt +++ b/libraries/plugins/es_objects/CMakeLists.txt @@ -15,7 +15,7 @@ if(MSVC) set_source_files_properties(es_objects.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) endif(MSVC) -target_link_libraries( graphene_es_objects graphene_chain graphene_app ${CURL_LIBRARIES} ) +target_link_libraries( graphene_es_objects graphene_chain graphene_app graphene_voting_stat ${CURL_LIBRARIES} ) target_include_directories( graphene_es_objects PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/plugins/es_objects/es_objects.cpp b/libraries/plugins/es_objects/es_objects.cpp index 14f9b1b17b..605c332779 100644 --- a/libraries/plugins/es_objects/es_objects.cpp +++ b/libraries/plugins/es_objects/es_objects.cpp @@ -30,13 +30,16 @@ #include #include #include -#include -#include +#include +#include #include namespace graphene { namespace es_objects { +using voting_stat::voting_statistics_object; +using voting_stat::voteable_statistics_object; + namespace detail { From e00daaea6ee4427dccb29fb8d3f528b2aa586cf8 Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 13:53:24 +0200 Subject: [PATCH 14/18] db maint removed unnecessary inlude --- libraries/chain/db_maint.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 8ed2073aed..eb2bc9b19d 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -44,7 +44,6 @@ #include #include #include -#include namespace graphene { namespace chain { @@ -206,7 +205,7 @@ void database::update_active_witnesses() /// accounts that vote for 0 or 1 witness do not get to express an opinion on /// the number of witnesses to have (they abstain and are non-voting accounts) - share_type stake_tally = 0; + share_type stake_tally = 0; size_t witness_count = 0; if( stake_target > 0 ) @@ -960,8 +959,8 @@ void process_hf_1465( database& db ) graphene::chain::share_type max_supply = current_asset.options.max_supply; if (current_supply > max_supply && max_supply != GRAPHENE_MAX_SHARE_SUPPLY) { - wlog( "Adjusting max_supply of ${asset} because current_supply (${current_supply}) is greater than ${old}.", - ("asset", current_asset.symbol) + wlog( "Adjusting max_supply of ${asset} because current_supply (${current_supply}) is greater than ${old}.", + ("asset", current_asset.symbol) ("current_supply", current_supply.value) ("old", max_supply)); db.modify( current_asset, [current_supply](asset_object& obj) { @@ -1238,7 +1237,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g update_worker_votes(); const auto& dgpo = get_dynamic_global_properties(); - + modify(gpo, [&dgpo](global_property_object& p) { // Remove scaling of account registration fee p.parameters.get_mutable_fees().get().basic_fee >>= p.parameters.account_fee_scale_bitshifts * From c0f3def0e0c36aae9f0e67a2f7fdd45fcc4d441d Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 13:53:49 +0200 Subject: [PATCH 15/18] db notify removed question --- libraries/chain/db_notify.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index a4eb36bc67..ffd61f44d8 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -375,9 +375,6 @@ void get_relevant_accounts( const object* obj, flat_set& accoun accounts.insert( htlc_obj->transfer.to ); break; } - /* - add voting and voteable here? - */ } } else if( obj->id.space() == implementation_ids ) From b53b2545fab580ea3614af57d017ea5f45c10006 Mon Sep 17 00:00:00 2001 From: dimfred Date: Tue, 10 Sep 2019 14:01:02 +0200 Subject: [PATCH 16/18] (temp) moved test to standard bench and removed es tests --- tests/CMakeLists.txt | 6 +- .../main.cpp => tests/voting_stat_tests.cpp} | 210 +++++++++--------- 2 files changed, 105 insertions(+), 111 deletions(-) rename tests/{voting_stat/main.cpp => tests/voting_stat_tests.cpp} (80%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 36a1e7abb8..b1447ed3e0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ file(GLOB UNIT_TESTS "tests/*.cpp") add_executable( chain_test ${COMMON_SOURCES} ${UNIT_TESTS} ) target_link_libraries( chain_test graphene_chain graphene_app graphene_witness graphene_account_history graphene_elasticsearch - graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes + graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes graphene_voting_stat fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) if(MSVC) set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) @@ -52,8 +52,4 @@ target_link_libraries( es_test graphene_es_objects graphene_egenesis_none graphene_api_helper_indexes fc ${PLATFORM_SPECIFIC_LIBS} ) -file( GLOB VOTING_STAT_SOURCES "voting_stat/*.cpp" ) -add_executable( voting_stat_test ${COMMON_SOURCES} ${VOTING_STAT_SOURCES} ) -target_link_libraries( voting_stat_test graphene_chain graphene_app graphene_voting_stat graphene_elasticsearch graphene_es_objects graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) - add_subdirectory( generate_empty_blocks ) diff --git a/tests/voting_stat/main.cpp b/tests/tests/voting_stat_tests.cpp similarity index 80% rename from tests/voting_stat/main.cpp rename to tests/tests/voting_stat_tests.cpp index fbd4672ef0..04e9753d0a 100644 --- a/tests/voting_stat/main.cpp +++ b/tests/tests/voting_stat_tests.cpp @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include #include #include @@ -28,47 +29,44 @@ #include #include -#include -#include +//#include +//#include #include #include -#include -#include +#include +#include #include "../common/database_fixture.hpp" -#define BOOST_TEST_MODULE Voting Statistics Plugin Test -#include - using namespace fc; using namespace graphene::app; using namespace graphene::chain; using namespace graphene::chain::test; using namespace graphene::voting_stat; -using namespace graphene::es_objects; +//using namespace graphene::es_objects; using namespace graphene::utilities; namespace bpo = boost::program_options; struct voting_stat_fixture : public database_fixture { vote_id_type default_vote_id; - CURL *_curl; - ES _es; + // CURL *_curl; + // ES _es; voting_stat_fixture() { try { - _curl = curl_easy_init(); - _es.curl = _curl; - _es.elasticsearch_url = "http://localhost:9200/"; - _es.index_prefix = "objects-"; + // _curl = curl_easy_init(); + // _es.curl = _curl; + // _es.elasticsearch_url = "http://localhost:9200/"; + // _es.index_prefix = "objects-"; app.register_plugin( true ); - app.register_plugin( true ); + //app.register_plugin( true ); } catch(fc::exception &e) { @@ -181,8 +179,8 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_delete_after_interval ) auto voting_stat = app.get_plugin("voting_stat"); voting_stat->plugin_set_program_options( cli_vs, cfg ); - auto es_objects = app.get_plugin("es_objects"); - es_objects->plugin_set_program_options( cli_es, cfg ); + //auto es_objects = app.get_plugin("es_objects"); + //es_objects->plugin_set_program_options( cli_es, cfg ); const char* const plugin_argv[]{ "voting_stat", "--voting-stat-track-every-x-maint", "1", @@ -191,14 +189,14 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_delete_after_interval ) "--voting-stat-track-committee-votes", "false", "--voting-stat-track-worker-votes", "false", - "--es-objects-bulk-replay", "1", - "--es-objects-proposals", "false", - "--es-objects-accounts", "false", - "--es-objects-assets", "false", - "--es-objects-balances", "false", - "--es-objects-limit-orders", "false", - "--es-objects-asset-bitasset", "false", - "--es-objects-keep-only-current", "true", + // "--es-objects-bulk-replay", "1", + // "--es-objects-proposals", "false", + // "--es-objects-accounts", "false", + // "--es-objects-assets", "false", + // "--es-objects-balances", "false", + // "--es-objects-limit-orders", "false", + // "--es-objects-asset-bitasset", "false", + // "--es-objects-keep-only-current", "true", }; int plugin_argc = sizeof(plugin_argv)/sizeof(char*); @@ -206,9 +204,9 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_delete_after_interval ) bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); app.initialize_plugins( var_map ); - auto objects_deleted = graphene::utilities::deleteAll(_es); - if( !objects_deleted ) - BOOST_FAIL( "elastic search DB could not be deleted" ); + // auto objects_deleted = graphene::utilities::deleteAll(_es); + // if( !objects_deleted ) + // BOOST_FAIL( "elastic search DB could not be deleted" ); ACTORS( (alice)(bob)(charlie) ); @@ -313,13 +311,13 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_delete_after_interval ) BOOST_CHECK( charlie_stat.get_total_voting_stake() == 222 ); } - fc::usleep(fc::milliseconds(2000)); - string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - _es.endpoint = _es.index_prefix + "*/data/_count"; - _es.query = query; - auto res = graphene::utilities::simpleQuery(_es); - variant j = fc::json::from_string(res); - BOOST_CHECK( j["count"].as_int64() == 12 ); + // fc::usleep(fc::milliseconds(2000)); + // string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + // _es.endpoint = _es.index_prefix + "*/data/_count"; + // _es.query = query; + // auto res = graphene::utilities::simpleQuery(_es); + // variant j = fc::json::from_string(res); + // BOOST_CHECK( j["count"].as_int64() == 12 ); } FC_LOG_AND_RETHROW() } @@ -335,8 +333,8 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_keep_after_interval ) auto voting_stat = app.get_plugin("voting_stat"); voting_stat->plugin_set_program_options( cli_vs, cfg ); - auto es_objects = app.get_plugin("es_objects"); - es_objects->plugin_set_program_options( cli_es, cfg ); + //auto es_objects = app.get_plugin("es_objects"); + //es_objects->plugin_set_program_options( cli_es, cfg ); const char* const plugin_argv[]{ "voting_stat", "--voting-stat-track-every-x-maint", "1", @@ -345,14 +343,14 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_keep_after_interval ) "--voting-stat-track-committee-votes", "false", "--voting-stat-track-worker-votes", "false", - "--es-objects-bulk-replay", "1", - "--es-objects-proposals", "false", - "--es-objects-accounts", "false", - "--es-objects-assets", "false", - "--es-objects-balances", "false", - "--es-objects-limit-orders", "false", - "--es-objects-asset-bitasset", "false", - "--es-objects-keep-only-current", "true", + // "--es-objects-bulk-replay", "1", + // "--es-objects-proposals", "false", + // "--es-objects-accounts", "false", + // "--es-objects-assets", "false", + // "--es-objects-balances", "false", + // "--es-objects-limit-orders", "false", + // "--es-objects-asset-bitasset", "false", + // "--es-objects-keep-only-current", "true", }; int plugin_argc = sizeof(plugin_argv)/sizeof(char*); @@ -360,9 +358,9 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_keep_after_interval ) bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); app.initialize_plugins( var_map ); - auto objects_deleted = graphene::utilities::deleteAll(_es); - if( !objects_deleted ) - BOOST_FAIL( "elastic search DB could not be deleted" ); + // auto objects_deleted = graphene::utilities::deleteAll(_es); + // if( !objects_deleted ) + // BOOST_FAIL( "elastic search DB could not be deleted" ); ACTORS( (alice)(bob)(charlie) ); @@ -467,13 +465,13 @@ BOOST_AUTO_TEST_CASE( test_voting_statistics_with_proxy_keep_after_interval ) BOOST_CHECK( charlie_stat.get_total_voting_stake() == 222 ); } - fc::usleep(fc::milliseconds(2000)); - string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - _es.endpoint = _es.index_prefix + "*/data/_count"; - _es.query = query; - auto res = graphene::utilities::simpleQuery(_es); - variant j = fc::json::from_string(res); - BOOST_CHECK( j["count"].as_int64() == 12 ); + // fc::usleep(fc::milliseconds(2000)); + // string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + // _es.endpoint = _es.index_prefix + "*/data/_count"; + // _es.query = query; + // auto res = graphene::utilities::simpleQuery(_es); + // variant j = fc::json::from_string(res); + // BOOST_CHECK( j["count"].as_int64() == 12 ); } FC_LOG_AND_RETHROW() } @@ -487,8 +485,8 @@ BOOST_AUTO_TEST_CASE( test_voteable_objects_tracking_with_es ) auto voting_stat = app.get_plugin("voting_stat"); voting_stat->plugin_set_program_options( cli_vs, cfg ); - auto es_objects = app.get_plugin("es_objects"); - es_objects->plugin_set_program_options( cli_es, cfg ); + // auto es_objects = app.get_plugin("es_objects"); + // es_objects->plugin_set_program_options( cli_es, cfg ); const char* const plugin_argv[]{ "voting_stat", "--voting-stat-track-every-x-maint", "1", @@ -497,18 +495,18 @@ BOOST_AUTO_TEST_CASE( test_voteable_objects_tracking_with_es ) "--voting-stat-track-witness-votes", "true", "--voting-stat-track-committee-votes", "true", - "--es-objects-voting-statistics", "true", - "--es-objects-voteable-statistics", "true", - "--es-objects-statistics-delete-allowed", "false", - - "--es-objects-bulk-replay", "1", - "--es-objects-proposals", "false", - "--es-objects-accounts", "false", - "--es-objects-assets", "false", - "--es-objects-balances", "false", - "--es-objects-limit-orders", "false", - "--es-objects-asset-bitasset", "false", - "--es-objects-keep-only-current", "true", + // "--es-objects-voting-statistics", "true", + // "--es-objects-voteable-statistics", "true", + // "--es-objects-statistics-delete-allowed", "false", + + // "--es-objects-bulk-replay", "1", + // "--es-objects-proposals", "false", + // "--es-objects-accounts", "false", + // "--es-objects-assets", "false", + // "--es-objects-balances", "false", + // "--es-objects-limit-orders", "false", + // "--es-objects-asset-bitasset", "false", + // "--es-objects-keep-only-current", "true", }; int plugin_argc = sizeof(plugin_argv)/sizeof(char*); @@ -516,9 +514,9 @@ BOOST_AUTO_TEST_CASE( test_voteable_objects_tracking_with_es ) bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); app.initialize_plugins( var_map ); - auto objects_deleted = graphene::utilities::deleteAll(_es); - if( !objects_deleted ) - BOOST_FAIL( "elastic search DB could not be deleted" ); + // auto objects_deleted = graphene::utilities::deleteAll(_es); + // if( !objects_deleted ) + // BOOST_FAIL( "elastic search DB could not be deleted" ); ACTOR( alice ); @@ -553,16 +551,16 @@ BOOST_AUTO_TEST_CASE( test_voteable_objects_tracking_with_es ) BOOST_CHECK( voteable_idx.size() == expected_voteables ); - auto expected_objs_in_es = 2*(expected_voteables + 1); + // auto expected_objs_in_es = 2*(expected_voteables + 1); - fc::usleep(fc::milliseconds(2000)); - string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - _es.endpoint = _es.index_prefix + "*/data/_count"; - _es.query = query; - auto res = graphene::utilities::simpleQuery(_es); - variant j = fc::json::from_string(res); - uint64_t obj_count = j["count"].as_uint64(); - BOOST_CHECK( obj_count == expected_objs_in_es ); + // fc::usleep(fc::milliseconds(2000)); + // string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + // _es.endpoint = _es.index_prefix + "*/data/_count"; + // _es.query = query; + // auto res = graphene::utilities::simpleQuery(_es); + // variant j = fc::json::from_string(res); + // uint64_t obj_count = j["count"].as_uint64(); + // BOOST_CHECK( obj_count == expected_objs_in_es ); } FC_LOG_AND_RETHROW() } @@ -621,25 +619,25 @@ BOOST_AUTO_TEST_CASE( test_delete_after_interval_and_pushed_to_es ) auto voting_stat = app.get_plugin("voting_stat"); voting_stat->plugin_set_program_options( cli_vs, cfg ); - auto es_objects = app.get_plugin("es_objects"); - es_objects->plugin_set_program_options( cli_es, cfg ); + //auto es_objects = app.get_plugin("es_objects"); + //es_objects->plugin_set_program_options( cli_es, cfg ); const char* const plugin_argv[]{ "voting_stat", "--voting-stat-track-every-x-maint", "1", "--voting-stat-keep-objects-in-db", "false", - "--es-objects-voting-statistics", "true", - "--es-objects-voteable-statistics", "false", - "--es-objects-statistics-delete-allowed", "false", - - "--es-objects-bulk-replay", "1", - "--es-objects-proposals", "false", - "--es-objects-accounts", "false", - "--es-objects-assets", "false", - "--es-objects-balances", "false", - "--es-objects-limit-orders", "false", - "--es-objects-asset-bitasset", "false", - "--es-objects-keep-only-current", "true", + // "--es-objects-voting-statistics", "true", + // "--es-objects-voteable-statistics", "false", + // "--es-objects-statistics-delete-allowed", "false", + + // "--es-objects-bulk-replay", "1", + // "--es-objects-proposals", "false", + // "--es-objects-accounts", "false", + // "--es-objects-assets", "false", + // "--es-objects-balances", "false", + // "--es-objects-limit-orders", "false", + // "--es-objects-asset-bitasset", "false", + // "--es-objects-keep-only-current", "true", }; int plugin_argc = sizeof(plugin_argv)/sizeof(char*); @@ -647,9 +645,9 @@ BOOST_AUTO_TEST_CASE( test_delete_after_interval_and_pushed_to_es ) bpo::store( bpo::parse_command_line( plugin_argc, plugin_argv, cfg ), var_map ); app.initialize_plugins( var_map ); - auto objects_deleted = graphene::utilities::deleteAll(_es); - if( !objects_deleted ) - BOOST_FAIL( "elastic search DB could not be deleted" ); + // auto objects_deleted = graphene::utilities::deleteAll(_es); + // if( !objects_deleted ) + // BOOST_FAIL( "elastic search DB could not be deleted" ); const auto& idx = db.get_index_type().indices().get(); @@ -672,14 +670,14 @@ BOOST_AUTO_TEST_CASE( test_delete_after_interval_and_pushed_to_es ) BOOST_CHECK( idx.size() == 1 ); - fc::usleep(fc::milliseconds(2000)); - string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; - _es.endpoint = _es.index_prefix + "*/data/_count"; - _es.query = query; - auto res = graphene::utilities::simpleQuery(_es); - variant j = fc::json::from_string(res); - int64_t obj_count = j["count"].as_int64(); - BOOST_CHECK( obj_count == 3 ); + // fc::usleep(fc::milliseconds(2000)); + // string query = "{ \"query\" : { \"bool\" : { \"must\" : [{\"match_all\": {}}] } } }"; + // _es.endpoint = _es.index_prefix + "*/data/_count"; + // _es.query = query; + // auto res = graphene::utilities::simpleQuery(_es); + // variant j = fc::json::from_string(res); + // int64_t obj_count = j["count"].as_int64(); + // BOOST_CHECK( obj_count == 3 ); } FC_LOG_AND_RETHROW() } From 72587d3017b38b05a11d86de8b22b5b3adc19265 Mon Sep 17 00:00:00 2001 From: dimfred Date: Wed, 11 Sep 2019 11:57:36 +0200 Subject: [PATCH 17/18] changed the plugin space --- libraries/plugins/README.md | 2 +- .../include/graphene/voting_stat/voting_stat_plugin.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/plugins/README.md b/libraries/plugins/README.md index 90bd872552..446c11b0cf 100644 --- a/libraries/plugins/README.md +++ b/libraries/plugins/README.md @@ -20,4 +20,4 @@ Folder | Name | Description [market_history](market_history) | Market History | Save market history data | Market data | Stable | 5 [snapshot](snapshot) | Snapshot | Get a json of all objects in blockchain at a specificed time or block | Debug | Stable | [witness](witness) | Witness | Generate and sign blocks | Block producer | Stable | -[voting_stat](voting_stat) | Voting Statistics | Generate objects from voting related data (works together with es objects) | History | Experimental | 7 \ No newline at end of file +[voting_stat](voting_stat) | Voting Statistics | Generate objects from voting related data (works together with es objects) | History | Experimental | 8 \ No newline at end of file diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp index 55faed4a80..e5fb54b257 100644 --- a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp @@ -38,7 +38,7 @@ namespace graphene { namespace voting_stat { // time. // #ifndef VOTING_STAT_SPACE_ID -#define VOTING_STAT_SPACE_ID 7 +#define VOTING_STAT_SPACE_ID 8 #endif enum voting_stat_object_type_ids From f47e5bae5b6436368b5bf454400eb3852088f1eb Mon Sep 17 00:00:00 2001 From: dimfred Date: Mon, 21 Oct 2019 14:04:41 +0200 Subject: [PATCH 18/18] maint counter object added, changed plugin accordingly --- .../maintenance_counter_object.hpp | 109 ++++++++++++++++++ .../voteable_statistics_object.hpp | 2 +- .../voting_stat/voting_stat_plugin.hpp | 3 +- .../voting_stat/voting_statistics_object.hpp | 2 +- .../voting_stat/voting_stat_plugin.cpp | 45 +++++--- 5 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 libraries/plugins/voting_stat/include/graphene/voting_stat/maintenance_counter_object.hpp diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/maintenance_counter_object.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/maintenance_counter_object.hpp new file mode 100644 index 0000000000..78047223e5 --- /dev/null +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/maintenance_counter_object.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2019 Blockchain Projects BV. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +using graphene::chain::object_id_type; +using graphene::chain::account_id_type; +using graphene::chain::vote_id_type; + +using graphene::db::object; +using graphene::db::abstract_object; +using graphene::db::generic_index; +using graphene::db::by_id; + +using namespace boost::multi_index; +using boost::container::flat_map; +using boost::container::flat_set; + +namespace graphene { namespace voting_stat { + /** + * @brief tracks the number maintenance interval occurences + * @ingroup object + * @ingroup voting_stat_plugin + * + * The number of maintenance intervals to be tracked is set in this object. Since a fork can occur during a + * maintenance interval, it is not sufficient to track the number of intervals through a plugin internal + * variable. In the case of a fork this object will be reverted together with the internal maintenance counter. + * Through the lifetime of the plugin there will be only one of this objects. + * + * @note By default this object are not tracked, the voting_stat_plugin must be loaded for this object to + * be maintained. + */ + class maintenance_counter_object : public abstract_object + { + public: + static const uint8_t space_id = VOTING_STAT_SPACE_ID; + static const uint8_t type_id = voting_stat_object_type_ids::maintenance_counter_object_type_id; + + maintenance_counter_object(){} + + bool counter_reached( chain::database& db ) const + { + if( counter == max_counter ) + { + db.modify( *this, [](maintenance_counter_object& o) { + o.counter = 0; + }); + return true; + } + db.modify( *this, [](maintenance_counter_object& o) { + o.counter += 1; + }); + return false; + } + + uint16_t max_counter = 12; // every 12th maintenance interval vote*_objects will be created + uint16_t counter = 12; + }; + + typedef multi_index_container< maintenance_counter_object, + indexed_by< + ordered_unique< tag, + member< object, object_id_type, &object::id > + > + > + > maintenance_counter_multi_index_type; + + typedef generic_index< + maintenance_counter_object, maintenance_counter_multi_index_type > maintenance_counter_index; + +}} // graphene::chain + +FC_REFLECT_DERIVED( graphene::voting_stat::maintenance_counter_object, (graphene::chain::object), + (max_counter)(counter) ) diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp index a7a5f7e4ea..73a3a715a7 100644 --- a/libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voteable_statistics_object.hpp @@ -43,7 +43,7 @@ namespace graphene { namespace voting_stat { /** * @brief tracks the history voting related data per account * @ingroup object - * @ingroup implementation + * @ingroup voting_stat_plugin * * The calculation of the voting stake, performed in the maintenance interval, results * in the creation or, if present, in the update of a voting_statistics_object. diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp index e5fb54b257..fbd6b14771 100644 --- a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_stat_plugin.hpp @@ -44,7 +44,8 @@ namespace graphene { namespace voting_stat { enum voting_stat_object_type_ids { voting_statistics_object_type_id, - voteable_statistics_object_type_id + voteable_statistics_object_type_id, + maintenance_counter_object_type_id }; namespace detail diff --git a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp index 32491ebba1..b5e13243fb 100644 --- a/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp +++ b/libraries/plugins/voting_stat/include/graphene/voting_stat/voting_statistics_object.hpp @@ -54,7 +54,7 @@ namespace graphene { namespace voting_stat { /** * @brief tracks the history of the voting stake for an account * @ingroup object - * @ingroup implementation + * @ingroup voting_stat_plugin * * The calculation of the voting stake, performed in the maintenance interval, results in the creation or, * if present, in the update of a voting_statistics_object. diff --git a/libraries/plugins/voting_stat/voting_stat_plugin.cpp b/libraries/plugins/voting_stat/voting_stat_plugin.cpp index 447e61876b..0c02931866 100644 --- a/libraries/plugins/voting_stat/voting_stat_plugin.cpp +++ b/libraries/plugins/voting_stat/voting_stat_plugin.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include #include @@ -52,13 +54,10 @@ class voting_stat_plugin_impl boost::signals2::connection _on_voting_stake_calc_conn; std::unique_ptr _on_voting_stake_calc_block; - uint16_t _maint_counter = 0; - /** * plugin parameters */ bool _keep_objects_in_db = true; - uint16_t _track_every_x_maint = 12; bool _track_worker_votes = true; bool _track_witness_votes = true; bool _track_committee_votes = true; @@ -108,14 +107,14 @@ void voting_stat_plugin_impl::on_maintenance_begin(uint32_t block_num) if( !_keep_objects_in_db ) delete_all_statistics_objects(); - if( _maint_counter == _track_every_x_maint ) + auto& db = database(); + const auto& maint_counter_obj = *db.get_index_type().indices().get().begin(); + if( maint_counter_obj.counter_reached( db ) ) { _on_voting_stake_calc_block->unblock(); - _maint_counter = 0; _maint_block = block_num; _create_voteable = true; } - ++_maint_counter; } void voting_stat_plugin_impl::on_maintenance_end() @@ -147,8 +146,6 @@ void voting_stat_plugin_impl::create_voteable_statistics_objects() { auto& db = database(); - // TODO secondary index for workers where current_time < worker_end_time - // will reduce the iteration time if( _track_worker_votes ) { const auto& worker_idx = db.get_index_type().indices().get(); @@ -326,13 +323,8 @@ void voting_stat_plugin::plugin_initialize(const boost::program_options::variabl auto& db = database(); db.add_index< primary_index >(); db.add_index< primary_index >(); + db.add_index< primary_index >(); - if( options.count("voting-stat-track-every-x-maint") ){ - my->_track_every_x_maint = options["voting-stat-track-every-x-maint"].as(); - if( my->_track_every_x_maint == 0 ) - my->_track_every_x_maint = 1; - my->_maint_counter = my->_track_every_x_maint; - } if( options.count("voting-stat-keep-objects-in-db") ){ my->_keep_objects_in_db = options["voting-stat-keep-objects-in-db"].as(); } @@ -345,6 +337,31 @@ void voting_stat_plugin::plugin_initialize(const boost::program_options::variabl if( options.count("voting-stat-track-committee-votes") ){ my->_track_committee_votes = options["voting-stat-track-committee-votes"].as(); } + uint16_t track_every_x_maint = 12; + if( options.count("voting-stat-track-every-x-maint") ) + { + track_every_x_maint = options["voting-stat-track-every-x-maint"].as(); + if( track_every_x_maint == 0 ) + track_every_x_maint = 1; + } + const auto& maint_counter_idx = db.get_index_type().indices().get(); + if( maint_counter_idx.empty() ) + { + db.create( [track_every_x_maint]( maintenance_counter_object& o ) { + o.max_counter = track_every_x_maint; + o.counter = track_every_x_maint; + }); + } + else + { + db.modify( *maint_counter_idx.begin(), + [track_every_x_maint]( maintenance_counter_object& o ) + { + o.max_counter = track_every_x_maint; + o.counter = track_every_x_maint; + } + ); + } my->_on_voting_stake_calc_conn = db.on_voting_stake_calculated.connect( [&]( const account_object& stake_account, const account_object& proxy_account, const uint64_t stake ){