From 22fc780a91c53faa433546c5fb622a42b50b4f96 Mon Sep 17 00:00:00 2001 From: serkixenos Date: Tue, 26 Jul 2022 23:17:42 +0000 Subject: [PATCH] SON for Hive voting --- .gitlab-ci.yml | 4 - Dockerfile | 2 +- Dockerfile.18.04 | 6 +- README.md | 6 +- libraries/app/database_api.cpp | 69 +- libraries/chain/account_evaluator.cpp | 10 +- libraries/chain/db_block.cpp | 17 +- libraries/chain/db_getter.cpp | 64 +- libraries/chain/db_init.cpp | 31 +- libraries/chain/db_maint.cpp | 662 ++++++++++-------- libraries/chain/db_witness_schedule.cpp | 151 ++-- .../chain/include/graphene/chain/database.hpp | 41 +- .../graphene/chain/global_property_object.hpp | 16 +- .../graphene/chain/protocol/account.hpp | 20 +- .../graphene/chain/protocol/son_wallet.hpp | 2 +- .../include/graphene/chain/protocol/vote.hpp | 5 +- .../chain/sidechain_address_object.hpp | 2 +- .../include/graphene/chain/sidechain_defs.hpp | 15 +- .../chain/include/graphene/chain/son_info.hpp | 19 +- .../include/graphene/chain/son_object.hpp | 42 +- .../graphene/chain/son_wallet_object.hpp | 2 +- .../include/graphene/chain/voters_info.hpp | 12 +- .../include/graphene/chain/votes_info.hpp | 12 +- .../chain/witness_schedule_object.hpp | 2 +- .../graphene/chain/witness_scheduler.hpp | 10 +- libraries/chain/protocol/account.cpp | 15 +- .../chain/sidechain_address_evaluator.cpp | 2 +- libraries/chain/son_evaluator.cpp | 192 +++-- .../chain/son_wallet_deposit_evaluator.cpp | 10 +- libraries/chain/son_wallet_evaluator.cpp | 12 +- .../chain/son_wallet_withdraw_evaluator.cpp | 12 +- .../peerplays_sidechain/CMakeLists.txt | 2 +- .../peerplays_sidechain_plugin.hpp | 16 +- .../sidechain_net_handler_factory.hpp | 23 + .../sidechain_net_manager.hpp | 38 - .../peerplays_sidechain_plugin.cpp | 318 +++++---- .../sidechain_net_handler.cpp | 50 +- .../sidechain_net_handler_bitcoin.cpp | 76 +- .../sidechain_net_handler_factory.cpp | 31 + .../sidechain_net_handler_hive.cpp | 34 +- .../sidechain_net_handler_peerplays.cpp | 14 +- .../sidechain_net_manager.cpp | 112 --- .../wallet/include/graphene/wallet/wallet.hpp | 5 + libraries/wallet/wallet.cpp | 186 +++-- tests/cli/son.cpp | 305 +++++--- tests/tests/block_tests.cpp | 7 +- tests/tests/sidechain_addresses_test.cpp | 142 +++- tests/tests/son_operations_tests.cpp | 85 ++- tests/tests/son_wallet_tests.cpp | 9 +- 49 files changed, 1783 insertions(+), 1135 deletions(-) create mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp delete mode 100644 libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp delete mode 100644 libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cda41654..61afce6e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,8 +44,6 @@ test-mainnet: dockerize-mainnet: stage: dockerize - dependencies: - - test-mainnet variables: IMAGE: $CI_REGISTRY_IMAGE/mainnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA before_script: @@ -105,8 +103,6 @@ test-testnet: dockerize-testnet: stage: dockerize - dependencies: - - test-testnet variables: IMAGE: $CI_REGISTRY_IMAGE/testnet/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA before_script: diff --git a/Dockerfile b/Dockerfile index 7a76c136..50c51c3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ RUN \ expect \ git \ graphviz \ - libboost1.67-all-dev \ + libboost-all-dev \ libbz2-dev \ libcurl4-openssl-dev \ libncurses-dev \ diff --git a/Dockerfile.18.04 b/Dockerfile.18.04 index d3fa1d75..49a06fa1 100644 --- a/Dockerfile.18.04 +++ b/Dockerfile.18.04 @@ -58,9 +58,9 @@ EXPOSE 22 WORKDIR /home/peerplays/ RUN \ - wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 && \ - tar xjf boost_1_67_0.tar.bz2 && \ - cd boost_1_67_0/ && \ + wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 && \ + tar xjf boost_1_71_0.tar.bz2 && \ + cd boost_1_71_0/ && \ ./bootstrap.sh && \ ./b2 install diff --git a/README.md b/README.md index a8d98021..f3d8c5b9 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ sudo apt-get install \ Install Boost libraries from source ``` -wget -c 'http://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download' -O boost_1_67_0.tar.bz2 -tar xjf boost_1_67_0.tar.bz2 -cd boost_1_67_0/ +wget -c 'https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.bz2' -O boost_1_71_0.tar.bz2 +tar xjf boost_1_71_0.tar.bz2 +cd boost_1_71_0/ ./bootstrap.sh sudo ./b2 install ``` diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 6ffee911..b10d6b99 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -2083,7 +2083,8 @@ vector database_api_impl::lookup_vote_ids(const vector &v const auto &committee_idx = _db.get_index_type().indices().get(); const auto &for_worker_idx = _db.get_index_type().indices().get(); const auto &against_worker_idx = _db.get_index_type().indices().get(); - const auto &son_idx = _db.get_index_type().indices().get(); + const auto &son_bictoin_idx = _db.get_index_type().indices().get(); + const auto &son_hive_idx = _db.get_index_type().indices().get(); vector result; result.reserve(votes.size()); @@ -2119,15 +2120,22 @@ vector database_api_impl::lookup_vote_ids(const vector &v } break; } - case vote_id_type::son: { - auto itr = son_idx.find(id); - if (itr != son_idx.end()) + case vote_id_type::son_bitcoin: { + auto itr = son_bictoin_idx.find(id); + if (itr != son_bictoin_idx.end()) + result.emplace_back(variant(*itr, 5)); + else + result.emplace_back(variant()); + break; + } + case vote_id_type::son_hive: { + auto itr = son_hive_idx.find(id); + if (itr != son_hive_idx.end()) result.emplace_back(variant(*itr, 5)); else result.emplace_back(variant()); break; } - case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings default: @@ -2152,12 +2160,21 @@ vector database_api_impl::get_votes_ids(const string &account_name votes_info database_api_impl::get_votes(const string &account_name_or_id) const { votes_info result; - const auto &votes_ids = get_votes_ids(account_name_or_id); - const auto &committee_ids = get_votes_objects(votes_ids); - const auto &witness_ids = get_votes_objects(votes_ids); - const auto &for_worker_ids = get_votes_objects(votes_ids); - const auto &against_worker_ids = get_votes_objects(votes_ids); - const auto &son_ids = get_votes_objects(votes_ids, 5); + const auto votes_ids = get_votes_ids(account_name_or_id); + const auto committee_ids = get_votes_objects(votes_ids); + const auto witness_ids = get_votes_objects(votes_ids); + const auto for_worker_ids = get_votes_objects(votes_ids); + const auto against_worker_ids = get_votes_objects(votes_ids); + const auto son_ids = [this, &votes_ids]() { + flat_map> son_ids; + const auto son_bitcoin_ids = get_votes_objects(votes_ids, 5); + if (!son_bitcoin_ids.empty()) + son_ids[sidechain_type::bitcoin] = std::move(son_bitcoin_ids); + const auto son_hive_ids = get_votes_objects(votes_ids, 5); + if (!son_hive_ids.empty()) + son_ids[sidechain_type::hive] = std::move(son_hive_ids); + return son_ids; + }(); //! Fill votes info if (!committee_ids.empty()) { @@ -2201,11 +2218,15 @@ votes_info database_api_impl::get_votes(const string &account_name_or_id) const } if (!son_ids.empty()) { - vector votes_for_sons; - votes_for_sons.reserve(son_ids.size()); - for (const auto &son : son_ids) { - const auto &son_obj = son.as(6); - votes_for_sons.emplace_back(votes_info_object{son_obj.vote_id, son_obj.id}); + flat_map> votes_for_sons; + for (const auto &son_sidechain_ids : son_ids) { + const auto &sidechain = son_sidechain_ids.first; + const auto &sidechain_ids = son_sidechain_ids.second; + votes_for_sons[sidechain].reserve(sidechain_ids.size()); + for (const auto &son : sidechain_ids) { + const auto &son_obj = son.as(6); + votes_for_sons[sidechain].emplace_back(votes_info_object{son_obj.get_sidechain_vote_id(sidechain), son_obj.id}); + } } result.votes_for_sons = std::move(votes_for_sons); } @@ -2379,12 +2400,16 @@ voters_info database_api_impl::get_voters(const string &account_name_or_id) cons //! Info for son voters if (son_object) { - const auto &son_voters = get_voters_by_id(son_object->vote_id); - voters_info_object voters_for_son; - voters_for_son.vote_id = son_object->vote_id; - voters_for_son.voters.reserve(son_voters.size()); - for (const auto &voter : son_voters) { - voters_for_son.voters.emplace_back(voter.get_id()); + flat_map voters_for_son; + for (const auto &vote_id : son_object->sidechain_vote_ids) { + const auto &son_voters = get_voters_by_id(vote_id.second); + voters_info_object voters_for_sidechain_son; + voters_for_sidechain_son.vote_id = vote_id.second; + voters_for_sidechain_son.voters.reserve(son_voters.size()); + for (const auto &voter : son_voters) { + voters_for_sidechain_son.voters.emplace_back(voter.get_id()); + } + voters_for_son[vote_id.first] = std::move(voters_for_sidechain_son); } result.voters_for_son = std::move(voters_for_son); } diff --git a/libraries/chain/account_evaluator.cpp b/libraries/chain/account_evaluator.cpp index 3026c2f0..5f1a4416 100644 --- a/libraries/chain/account_evaluator.cpp +++ b/libraries/chain/account_evaluator.cpp @@ -68,8 +68,14 @@ void verify_account_votes( const database& db, const account_options& options ) "Voted for more witnesses than currently allowed (${c})", ("c", chain_params.maximum_witness_count) ); FC_ASSERT( options.num_committee <= chain_params.maximum_committee_count, "Voted for more committee members than currently allowed (${c})", ("c", chain_params.maximum_committee_count) ); - FC_ASSERT( options.num_son() <= chain_params.maximum_son_count(), - "Voted for more sons than currently allowed (${c})", ("c", chain_params.maximum_son_count()) ); + FC_ASSERT( chain_params.extensions.value.maximum_son_count.valid() , "Invalid maximum son count" ); + FC_ASSERT( options.extensions.value.num_son.valid() , "Invalid son number" ); + for(const auto& num_sons : *options.extensions.value.num_son) + { + FC_ASSERT( num_sons.second <= *chain_params.extensions.value.maximum_son_count, + "Voted for more sons than currently allowed (${c})", ("c", *chain_params.extensions.value.maximum_son_count) ); + } + FC_ASSERT( db.find_object(options.voting_account), "Invalid proxy account specified." ); uint32_t max_vote_id = gpo.next_available_vote_id; bool has_worker_votes = false; diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index c475813b..9606c59e 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -705,7 +705,12 @@ void database::_apply_block( const signed_block& next_block ) if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) { update_witness_schedule(next_block); - if(global_props.active_sons.size() > 0) { + bool need_to_update_son_schedule = false; + for(const auto& active_sons : global_props.active_sons){ + if(!active_sons.second.empty()) + need_to_update_son_schedule = true; + } + if(need_to_update_son_schedule) { update_son_schedule(next_block); } } @@ -739,10 +744,18 @@ void database::_apply_block( const signed_block& next_block ) // TODO: figure out if we could collapse this function into // update_global_dynamic_data() as perhaps these methods only need // to be called for header validation? + update_maintenance_flag( maint_needed ); if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { update_witness_schedule(); - if(global_props.active_sons.size() > 0) { + + bool need_update_son_schedule = false; + for(const auto& active_sidechain_type : active_sidechain_types) { + if(global_props.active_sons.at(active_sidechain_type).size() > 0) { + need_update_son_schedule = true; + } + } + if(need_update_son_schedule) { update_son_schedule(); } } diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 78740c9b..0bb9b10b 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -222,17 +222,30 @@ std::set database::get_sons_to_be_deregistered() for( auto& son : son_idx ) { - if(son.status == son_status::in_maintenance) + bool need_to_be_deregistered = true; + for(const auto& status : son.statuses) { - auto stats = son.statistics(*this); - // TODO : We need to add a function that returns if we can deregister SON - // i.e. with introduction of PW code, we have to make a decision if the SON - // is needed for release of funds from the PW - if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())) + const auto& sidechain = status.first; + if(status.second != son_status::in_maintenance) + need_to_be_deregistered = false; + + if(need_to_be_deregistered) { - ret.insert(son.id); + auto stats = son.statistics(*this); + + // TODO : We need to add a function that returns if we can deregister SON + // i.e. with introduction of PW code, we have to make a decision if the SON + // is needed for release of funds from the PW + if (head_block_time() - stats.last_down_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) { + need_to_be_deregistered = false; + } } } + + if(need_to_be_deregistered) + { + ret.insert(son.id); + } } return ret; } @@ -289,28 +302,51 @@ bool database::is_son_dereg_valid( son_id_type son_id ) return false; } - return (son->status == son_status::in_maintenance && - (head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))); + bool status_son_dereg_valid = true; + for(const auto& status : son->statuses) + { + const auto& sidechain = status.first; + if(status.second != son_status::in_maintenance) + status_son_dereg_valid = false; + + if(status_son_dereg_valid) + { + if(head_block_time() - son->statistics(*this).last_down_timestamp.at(sidechain) < fc::seconds(get_global_properties().parameters.son_deregister_time())) + { + status_son_dereg_valid = false; + } + } + } + + return status_son_dereg_valid; } -bool database::is_son_active( son_id_type son_id ) +bool database::is_son_active( sidechain_type type, son_id_type son_id ) { const auto& son_idx = get_index_type().indices().get< by_id >(); auto son = son_idx.find( son_id ); - if(son == son_idx.end()) - { + if(son == son_idx.end()) { return false; } const global_property_object& gpo = get_global_properties(); + if(!gpo.active_sons.contains(type)) { + return false; + } + + const auto& gpo_as = gpo.active_sons.at(type); vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + active_son_ids.reserve(gpo_as.size()); + std::transform(gpo_as.cbegin(), gpo_as.cend(), std::inserter(active_son_ids, active_son_ids.end()), [](const son_info& swi) { return swi.son_id; }); + if(active_son_ids.empty()) { + return false; + } + auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id); return (it_son != active_son_ids.end()); } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index c4aabfa8..cc862618 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -1100,8 +1100,9 @@ void database::init_genesis(const genesis_state_type& genesis_state) FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() ); // Initialize witness schedule -#ifndef NDEBUG - const son_schedule_object& sso = + + #ifndef NDEBUG + const son_schedule_object& ssohive = #endif create([&](son_schedule_object& _sso) { @@ -1120,13 +1121,29 @@ void database::init_genesis(const genesis_state_type& genesis_state) _sso.recent_slots_filled = fc::uint128::max_value(); }); - assert( sso.id == son_schedule_id_type() ); + assert( ssohive.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::hive)) ); - // Enable fees - modify(get_global_properties(), [&genesis_state](global_property_object& p) { - p.parameters.current_fees = genesis_state.initial_parameters.current_fees; +#ifndef NDEBUG + const son_schedule_object& ssobitcoin = +#endif + create([&](son_schedule_object& _sso) + { + // for scheduled + memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size()); + + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + + auto init_witnesses = get_global_properties().active_witnesses; + + _sso.scheduler = son_scheduler(); + _sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1); + + + _sso.last_scheduling_block = 0; + + _sso.recent_slots_filled = fc::uint128::max_value(); }); - + assert( ssobitcoin.id == son_schedule_id_type(get_son_schedule_id(sidechain_type::bitcoin)) ); // Create FBA counters create([&]( fba_accumulator_object& acc ) diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index c3f826d1..d18e0f26 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -78,26 +78,30 @@ vector> database::sort } template<> -vector> database::sort_votable_objects(size_t count) const +vector> database::sort_votable_objects(sidechain_type sidechain, size_t count) const { const auto& all_sons = get_index_type().indices().get< by_id >(); std::vector> refs; for( auto& son : all_sons ) { - if(son.has_valid_config(head_block_time()) && son.status != son_status::deregistered) + if(son.has_valid_config(head_block_time()) && son.statuses.at(sidechain) != son_status::deregistered) { refs.push_back(std::cref(son)); } } count = std::min(count, refs.size()); std::partial_sort(refs.begin(), refs.begin() + count, refs.end(), - [this](const son_object& a, const son_object& b)->bool { - share_type oa_vote = _vote_tally_buffer[a.vote_id]; - share_type ob_vote = _vote_tally_buffer[b.vote_id]; + [this, sidechain](const son_object& a, const son_object& b)->bool { + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + + const share_type oa_vote = _vote_tally_buffer[a.get_sidechain_vote_id(sidechain)]; + const share_type ob_vote = _vote_tally_buffer[b.get_sidechain_vote_id(sidechain)]; + if( oa_vote != ob_vote ) return oa_vote > ob_vote; - return a.vote_id < b.vote_id; - }); + + return a.get_sidechain_vote_id(sidechain) < b.get_sidechain_vote_id(sidechain); +}); refs.resize(count, refs.front()); return refs; @@ -178,222 +182,233 @@ void database::update_worker_votes() void database::pay_sons() { - time_point_sec now = head_block_time(); + const time_point_sec now = head_block_time(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); // Current requirement is that we have to pay every 24 hours, so the following check - if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) { - assert( _son_count_histogram_buffer.size() > 0 ); - const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; - /// accounts that vote for 0 or 1 son do not get to express an opinion on - /// the number of sons to have (they abstain and are non-voting accounts) - share_type stake_tally = 0; - size_t son_count = 0; - if( stake_target > 0 ) + if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) + { + for(const auto& active_sidechain_type : active_sidechain_types) { - while( (son_count < _son_count_histogram_buffer.size() - 1) - && (stake_tally <= stake_target) ) + assert( _son_count_histogram_buffer.at(active_sidechain_type).size() > 0 ); + const share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer.at(active_sidechain_type)[0]) / 2; + /// accounts that vote for 0 or 1 son do not get to express an opinion on + /// the number of sons to have (they abstain and are non-voting accounts) + share_type stake_tally = 0; + size_t son_count = 0; + if( stake_target > 0 ) { - stake_tally += _son_count_histogram_buffer[++son_count]; - } - } - const vector> sons = [this, &son_count]{ - if(head_block_time() >= HARDFORK_SON3_TIME) - return sort_votable_objects(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)); - else - return sort_votable_objects(get_global_properties().parameters.maximum_son_count()); - }(); - // After SON2 HF - uint64_t total_votes = 0; - for( const son_object& son : sons ) - { - total_votes += _vote_tally_buffer[son.vote_id]; - } - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); - auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { - uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); - return weight; - }; - // Before SON2 HF - auto get_weight_before_son2_hf = []( uint64_t son_votes ) { - int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); - uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); - return weight; - }; - uint64_t weighted_total_txs_signed = 0; - share_type son_budget = dpo.son_budget; - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf](const object& o) { - const son_statistics_object& s = static_cast(o); - const auto& idx = get_index_type().indices().get(); - auto son_obj = idx.find( s.owner ); - auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); - if( now < HARDFORK_SON2_TIME ) { - son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]); - } - uint64_t txs_signed = 0; - for (const auto &ts : s.txs_signed) { - txs_signed = txs_signed + ts.second; - } - weighted_total_txs_signed += (txs_signed * son_weight); - }); - - // Now pay off each SON proportional to the number of transactions signed. - get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now](const object& o) { - const son_statistics_object& s = static_cast(o); - uint64_t txs_signed = 0; - for (const auto &ts : s.txs_signed) { - txs_signed = txs_signed + ts.second; + while( (son_count < _son_count_histogram_buffer.at(active_sidechain_type).size() - 1) + && (stake_tally <= stake_target) ) + { + stake_tally += _son_count_histogram_buffer.at(active_sidechain_type)[++son_count]; + } } - if(txs_signed > 0){ + const auto sons = sort_votable_objects(active_sidechain_type, + (std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)) + ); + + // After SON2 HF + uint64_t total_votes = 0; + for( const son_object& son : sons ) + { + total_votes += _vote_tally_buffer[son.sidechain_vote_ids.at(active_sidechain_type)]; + } + const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0); + auto get_weight = [&bits_to_drop]( uint64_t son_votes ) { + const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + // Before SON2 HF + auto get_weight_before_son2_hf = []( uint64_t son_votes ) { + const int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(son_votes)) - 15, 0); + const uint16_t weight = std::max((son_votes >> bits_to_drop), uint64_t(1) ); + return weight; + }; + uint64_t weighted_total_txs_signed = 0; + const share_type son_budget = dpo.son_budget; + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight, &now, &get_weight_before_son2_hf, &active_sidechain_type](const object& o) { + const son_statistics_object& s = static_cast(o); const auto& idx = get_index_type().indices().get(); - auto son_obj = idx.find( s.owner ); - auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]); - if( now < HARDFORK_SON2_TIME ) { - son_weight = get_weight_before_son2_hf(_vote_tally_buffer[son_obj->vote_id]); + const auto son_obj = idx.find( s.owner ); + uint16_t son_weight = 0; + if( now >= HARDFORK_SON2_TIME ) { + son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); } - share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; - modify( *son_obj, [&]( son_object& _son_obj) - { - _son_obj.pay_son_fee(pay, *this); - }); - //Remove the amount paid out to SON from global SON Budget - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.son_budget -= pay; - } ); - //Reset the tx counter in each son statistics object - modify( s, [&]( son_statistics_object& _s) - { - for (const auto &ts : s.txs_signed) { - _s.txs_signed.at(ts.first) = 0; + else { + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + } + const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; + weighted_total_txs_signed += (txs_signed * son_weight); + }); + + // Now pay off each SON proportional to the number of transactions signed. + get_index_type().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight, &get_weight_before_son2_hf, &now, &active_sidechain_type](const object& o) { + const son_statistics_object& s = static_cast(o); + const uint64_t txs_signed = s.txs_signed.contains(active_sidechain_type) ? s.txs_signed.at(active_sidechain_type) : 0; + + if(txs_signed > 0){ + const auto& idx = get_index_type().indices().get(); + auto son_obj = idx.find( s.owner ); + uint16_t son_weight = 0; + if( now >= HARDFORK_SON2_TIME ) { + son_weight += get_weight(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); } - }); - } - }); - //Note the last son pay out time - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.last_son_payout_time = now; - }); + else { + son_weight += get_weight_before_son2_hf(_vote_tally_buffer[son_obj->sidechain_vote_ids.at(active_sidechain_type)]); + } + const share_type pay = (txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed; + modify( *son_obj, [&]( son_object& _son_obj) + { + _son_obj.pay_son_fee(pay, *this); + }); + //Remove the amount paid out to SON from global SON Budget + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.son_budget -= pay; + } ); + //Reset the tx counter in each son statistics object + modify( s, [&]( son_statistics_object& _s) + { + if(_s.txs_signed.contains(active_sidechain_type)) + _s.txs_signed.at(active_sidechain_type) = 0; + }); + } + }); + //Note the last son pay out time + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.last_son_payout_time = now; + }); + } } } -void database::update_son_metrics(const vector& curr_active_sons) +void database::update_son_metrics(const flat_map >& curr_active_sons) { - vector current_sons; + for(const auto& curr_active_sidechain_sons : curr_active_sons) { + const auto& sidechain = curr_active_sidechain_sons.first; + const auto& _curr_active_sidechain_sons = curr_active_sidechain_sons.second; - current_sons.reserve(curr_active_sons.size()); - std::transform(curr_active_sons.begin(), curr_active_sons.end(), - std::inserter(current_sons, current_sons.end()), - [](const son_info &swi) { - return swi.son_id; - }); + vector current_sons; - const auto& son_idx = get_index_type().indices().get< by_id >(); - for( auto& son : son_idx ) - { - auto& stats = son.statistics(*this); - bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); - modify( stats, [&]( son_statistics_object& _stats ) - { - if(is_active_son) { - _stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval; - } - _stats.total_downtime += _stats.current_interval_downtime; - _stats.current_interval_downtime = 0; - for (const auto &str : _stats.sidechain_txs_reported) { - _stats.sidechain_txs_reported.at(str.first) = 0; - } - }); + current_sons.reserve(_curr_active_sidechain_sons.size()); + std::transform(_curr_active_sidechain_sons.cbegin(), _curr_active_sidechain_sons.cend(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); + + const auto &son_idx = get_index_type().indices().get(); + for (auto &son : son_idx) { + auto &stats = son.statistics(*this); + bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end()); + modify(stats, [&](son_statistics_object &_stats) { + if (is_active_son) { + _stats.total_voted_time[sidechain] = _stats.total_voted_time[sidechain] + get_global_properties().parameters.maintenance_interval; + } + _stats.total_downtime[sidechain] += _stats.current_interval_downtime[sidechain]; + _stats.current_interval_downtime[sidechain] = 0; + _stats.sidechain_txs_reported[sidechain] = 0; + }); + } } } -void database::update_son_statuses(const vector& curr_active_sons, const vector& new_active_sons) +void database::update_son_statuses( const flat_map >& curr_active_sons, + const flat_map >& new_active_sons ) { - vector current_sons, new_sons; - vector sons_to_remove, sons_to_add; - const auto& idx = get_index_type().indices().get(); + for(const auto& new_active_sidechain_sons : new_active_sons) { + const auto& sidechain = new_active_sidechain_sons.first; - current_sons.reserve(curr_active_sons.size()); - std::transform(curr_active_sons.begin(), curr_active_sons.end(), - std::inserter(current_sons, current_sons.end()), - [](const son_info &swi) { - return swi.son_id; - }); + vector current_sons, new_sons; + vector sons_to_remove, sons_to_add; + const auto &idx = get_index_type().indices().get(); - new_sons.reserve(new_active_sons.size()); - std::transform(new_active_sons.begin(), new_active_sons.end(), - std::inserter(new_sons, new_sons.end()), - [](const son_info &swi) { - return swi.son_id; - }); - - // find all cur_active_sons members that is not in new_active_sons - for_each(current_sons.begin(), current_sons.end(), - [&sons_to_remove, &new_sons](const son_id_type& si) - { - if(std::find(new_sons.begin(), new_sons.end(), si) == - new_sons.end()) - { - sons_to_remove.push_back(si); - } - } - ); - - for( const auto& sid : sons_to_remove ) - { - auto son = idx.find( sid ); - if(son == idx.end()) // SON is deleted already - continue; - // keep maintenance status for nodes becoming inactive - if(son->status == son_status::active) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::inactive; - }); + if(curr_active_sons.contains(sidechain)) { + current_sons.reserve(curr_active_sons.at(sidechain).size()); + std::transform(curr_active_sons.at(sidechain).cbegin(), curr_active_sons.at(sidechain).cend(), + std::inserter(current_sons, current_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); } - } - // find all new_active_sons members that is not in cur_active_sons - for_each(new_sons.begin(), new_sons.end(), - [&sons_to_add, ¤t_sons](const son_id_type& si) - { - if(std::find(current_sons.begin(), current_sons.end(), si) == - current_sons.end()) - { - sons_to_add.push_back(si); - } - } - ); + new_sons.reserve(new_active_sons.at(sidechain).size()); + std::transform(new_active_sons.at(sidechain).cbegin(), new_active_sons.at(sidechain).cend(), + std::inserter(new_sons, new_sons.end()), + [](const son_info &swi) { + return swi.son_id; + }); - for( const auto& sid : sons_to_add ) - { - auto son = idx.find( sid ); - FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); - // keep maintenance status for new nodes - if(son->status == son_status::inactive) - { - modify( *son, [&]( son_object& obj ){ - obj.status = son_status::active; - }); - } - } + // find all cur_active_sons members that is not in new_active_sons + for_each(current_sons.begin(), current_sons.end(), + [&sons_to_remove, &new_sons](const son_id_type &si) { + if (std::find(new_sons.begin(), new_sons.end(), si) == + new_sons.end()) { + sons_to_remove.push_back(si); + } + }); - ilog("New SONS"); - for(size_t i = 0; i < new_sons.size(); i++) { - auto son = idx.find( new_sons[i] ); - if(son == idx.end()) // SON is deleted already + for (const auto &sid : sons_to_remove) { + auto son = idx.find(sid); + if (son == idx.end()) // SON is deleted already continue; - ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) ); + // keep maintenance status for nodes becoming inactive + if (son->statuses.at(sidechain) == son_status::active) { + modify(*son, [&](son_object &obj) { + obj.statuses.at(sidechain) = son_status::inactive; + }); + } + } + + // find all new_active_sons members that is not in cur_active_sons + for_each(new_sons.begin(), new_sons.end(), + [&sons_to_add, ¤t_sons](const son_id_type &si) { + if (std::find(current_sons.begin(), current_sons.end(), si) == + current_sons.end()) { + sons_to_add.push_back(si); + } + }); + + for (const auto &sid : sons_to_add) { + auto son = idx.find(sid); + FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid)); + // keep maintenance status for new nodes + if (son->statuses.at(sidechain) == son_status::inactive) { + modify(*son, [&](son_object &obj) { + obj.statuses.at(sidechain) = son_status::active; + }); + } + } + + ilog("New SONS for sidechain = ${sidechain}", ("sidechain", sidechain)); + for (size_t i = 0; i < new_sons.size(); i++) { + auto son = idx.find(new_sons[i]); + if (son == idx.end()) // SON is deleted already + continue; + ilog("${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->statuses.at(sidechain))("sv", son->total_votes)); + } } - if( sons_to_remove.size() > 0 ) - { + //! Remove inactive sons (when all sidechain inactive) + vector sons_to_remove; + const auto &idx = get_index_type().indices().get(); + for(const auto& son : idx) { + bool inactive_son = true; + for(const auto& status : son.statuses) { + if (status.second != son_status::inactive) + inactive_son = false; + } + if (inactive_son) + sons_to_remove.emplace_back(son.id); + } + if (sons_to_remove.size() > 0) { remove_inactive_son_proposals(sons_to_remove); } } -void database::update_son_wallet(const vector& new_active_sons) +void database::update_son_wallet(const flat_map >& new_active_sons) { bool should_recreate_pw = true; @@ -406,8 +421,16 @@ void database::update_son_wallet(const vector& new_active_sons) bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size()); if (wallet_son_sets_equal) { - for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { - wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i); + for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) { + const auto& sidechain = cur_wallet_sidechain_sons.first; + const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second; + + wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_active_sons.at(sidechain).size()); + if (wallet_son_sets_equal) { + for (size_t i = 0; i < _cur_wallet_sidechain_sons.size(); i++) { + wallet_son_sets_equal = wallet_son_sets_equal && (_cur_wallet_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i)); + } + } } } @@ -420,14 +443,24 @@ void database::update_son_wallet(const vector& new_active_sons) } } - should_recreate_pw = should_recreate_pw && (new_active_sons.size() >= get_chain_properties().immutable_parameters.min_son_count); + bool should_recreate_pw_sidechain = false; + for(const auto& new_active_sidechain_sons : new_active_sons) { + if(new_active_sidechain_sons.second.size() >= get_chain_properties().immutable_parameters.min_son_count) + should_recreate_pw_sidechain = true; + } + should_recreate_pw = should_recreate_pw && should_recreate_pw_sidechain; if (should_recreate_pw) { // Create new son_wallet_object, to initiate wallet recreation create( [&]( son_wallet_object& obj ) { obj.valid_from = head_block_time(); obj.expires = time_point_sec::maximum(); - obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end()); + for(const auto& new_active_sidechain_sons : new_active_sons){ + const auto& sidechain = new_active_sidechain_sons.first; + const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second; + + obj.sons[sidechain].insert(obj.sons[sidechain].end(), _new_active_sidechain_sons.cbegin(), _new_active_sidechain_sons.cend()); + } }); } } @@ -684,48 +717,87 @@ void database::update_active_sons() } assert( _son_count_histogram_buffer.size() > 0 ); - share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2; + for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ + assert( son_count_histogram_buffer.second.size() > 0 ); + } + + const flat_map stake_target = [this]{ + flat_map stake_target; + for( const auto& son_count_histogram_buffer : _son_count_histogram_buffer ){ + const auto sidechain = son_count_histogram_buffer.first; + stake_target[sidechain] = (_total_voting_stake-son_count_histogram_buffer.second[0]) / 2; + } + return stake_target; + }(); /// accounts that vote for 0 or 1 son do not get to express an opinion on /// the number of sons to have (they abstain and are non-voting accounts) - - share_type stake_tally = 0; - - size_t son_count = 0; - if( stake_target > 0 ) - { - while( (son_count < _son_count_histogram_buffer.size() - 1) - && (stake_tally <= stake_target) ) + flat_map stake_tally = []{ + flat_map stake_tally; + for(const auto& active_sidechain_type : active_sidechain_types){ + stake_tally[active_sidechain_type] = 0; + } + return stake_tally; + }(); + flat_map son_count = []{ + flat_map son_count; + for(const auto& active_sidechain_type : active_sidechain_types){ + son_count[active_sidechain_type] = 0; + } + return son_count; + }(); + for( const auto& stake_target_sidechain : stake_target ){ + const auto sidechain = stake_target_sidechain.first; + if( stake_target_sidechain.second > 0 ) { - stake_tally += _son_count_histogram_buffer[++son_count]; + while( (son_count[sidechain] < _son_count_histogram_buffer.at(sidechain).size() - 1) + && (stake_tally[sidechain] <= stake_target_sidechain.second) ) + { + stake_tally[sidechain] += _son_count_histogram_buffer.at(sidechain)[ ++son_count[sidechain] ]; + } } } const global_property_object& gpo = get_global_properties(); - const vector> sons = [this, &son_count]{ - if(head_block_time() >= HARDFORK_SON3_TIME) - return sort_votable_objects(std::max(son_count*2+1, (size_t)get_chain_properties().immutable_parameters.min_son_count)); - else - return sort_votable_objects(get_global_properties().parameters.maximum_son_count()); - }(); - + const chain_property_object& cpo = get_chain_properties(); const auto& all_sons = get_index_type().indices(); + flat_map > > sons; + for(const auto& active_sidechain_type : active_sidechain_types) + { + if(head_block_time() >= HARDFORK_SON3_TIME) { + sons[active_sidechain_type] = sort_votable_objects(active_sidechain_type, + (std::max(son_count.at(active_sidechain_type) * 2 + 1, (size_t)cpo.immutable_parameters.min_son_count))); + } + else { + sons[active_sidechain_type] = sort_votable_objects(active_sidechain_type, get_global_properties().parameters.maximum_son_count()); + } + } auto& local_vote_buffer_ref = _vote_tally_buffer; for( const son_object& son : all_sons ) { - if(son.status == son_status::request_maintenance) + for(const auto& status: son.statuses) { - auto& stats = son.statistics(*this); - modify( stats, [&]( son_statistics_object& _s){ - _s.last_down_timestamp = head_block_time(); + const auto& sidechain = status.first; + if(status.second == son_status::in_maintenance) + { + auto &stats = son.statistics(*this); + modify(stats, [&](son_statistics_object &_s) { + _s.last_down_timestamp[sidechain] = head_block_time(); }); + } } + modify( son, [local_vote_buffer_ref]( son_object& obj ){ - obj.total_votes = local_vote_buffer_ref[obj.vote_id]; - if(obj.status == son_status::request_maintenance) - obj.status = son_status::in_maintenance; - }); + for(const auto& sidechain_vote_id : obj.sidechain_vote_ids ){ + obj.total_votes[sidechain_vote_id.first] = local_vote_buffer_ref[sidechain_vote_id.second]; + } + for(auto& status: obj.statuses) + { + if (status.second == son_status::request_maintenance) + status.second = son_status::in_maintenance; + } + }); } // Update SON authority @@ -733,21 +805,24 @@ void database::update_active_sons() { modify( get(gpo.parameters.son_account()), [&]( account_object& a ) { + set account_ids; + for(const auto& sidechain_sons : sons) + { + for( const son_object& son : sidechain_sons.second ) + { + account_ids.emplace(son.son_account); + } + } + if( head_block_time() < HARDFORK_533_TIME ) { - map weights; a.active.weight_threshold = 0; a.active.account_auths.clear(); - for( const son_object& son : sons ) - { - weights.emplace(son.son_account, uint64_t(1)); - } - - for( const auto& weight : weights ) + for( const auto& account_id : account_ids ) { // Ensure that everyone has at least one vote. Zero weights aren't allowed. - a.active.account_auths[weight.first] += 1; + a.active.account_auths[account_id] += 1; a.active.weight_threshold += 1; } @@ -758,8 +833,10 @@ void database::update_active_sons() else { vote_counter vc; - for( const son_object& son : sons ) - vc.add( son.son_account, UINT64_C(1) ); + for( const auto& account_id : account_ids ) + { + vc.add(account_id, UINT64_C(1)); + } vc.finish_2_3( a.active ); } } ); @@ -767,22 +844,36 @@ void database::update_active_sons() // Compare current and to-be lists of active sons - auto cur_active_sons = gpo.active_sons; - vector new_active_sons; + const auto cur_active_sons = gpo.active_sons; + flat_map > new_active_sons; const auto &acc = get(gpo.parameters.son_account()); - for( const son_object& son : sons ) { - son_info swi; - swi.son_id = son.id; - swi.weight = acc.active.account_auths.at(son.son_account); - swi.signing_key = son.signing_key; - swi.sidechain_public_keys = son.sidechain_public_keys; - new_active_sons.push_back(swi); + for( const auto& sidechain_sons : sons ){ + const auto& sidechain = sidechain_sons.first; + const auto& sons_array = sidechain_sons.second; + + new_active_sons[sidechain].reserve(sons_array.size()); + for( const son_object& son : sons_array ) { + son_info swi; + swi.son_id = son.id; + swi.weight = acc.active.account_auths.at(son.son_account); + swi.signing_key = son.signing_key; + swi.public_key = son.sidechain_public_keys.at(sidechain); + new_active_sons[sidechain].push_back(swi); + } } bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size()); if (son_sets_equal) { - for( size_t i = 0; i < cur_active_sons.size(); i++ ) { - son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i); + for( const auto& cur_active_sidechain_sons : cur_active_sons ){ + const auto& sidechain = cur_active_sidechain_sons.first; + const auto& _cur_active_sidechain_sons = cur_active_sidechain_sons.second; + + son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.size() == new_active_sons.at(sidechain).size()); + if (son_sets_equal) { + for (size_t i = 0; i < _cur_active_sidechain_sons.size(); i++) { + son_sets_equal = son_sets_equal && (_cur_active_sidechain_sons.at(i) == new_active_sons.at(sidechain).at(i)); + } + } } } @@ -797,28 +888,37 @@ void database::update_active_sons() modify(gpo, [&]( global_property_object& gp ){ gp.active_sons.clear(); gp.active_sons.reserve(new_active_sons.size()); - gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end()); - }); + for( const auto& new_active_sidechain_sons : new_active_sons ) { + const auto& sidechain = new_active_sidechain_sons.first; + const auto& _new_active_sidechain_sons = new_active_sidechain_sons.second; - const son_schedule_object& sso = son_schedule_id_type()(*this); - modify(sso, [&](son_schedule_object& _sso) - { - flat_set active_sons; - active_sons.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(active_sons, active_sons.end()), - [](const son_info& swi) { - return swi.son_id; - }); - _sso.scheduler.update(active_sons); - // similar to witness, produce schedule for sons - if(cur_active_sons.size() == 0 && new_active_sons.size() > 0) - { - witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); - for( size_t i=0; i active_sons; + active_sons.reserve(gpo.active_sons.at(active_sidechain_type).size()); + std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), + std::inserter(active_sons, active_sons.end()), + [](const son_info& swi) { + return swi.son_id; + }); + _sso.scheduler.update(active_sons); + // similar to witness, produce schedule for sons + if(cur_active_sons.at(active_sidechain_type).size() == 0 && new_active_sons.at(active_sidechain_type).size() > 0) + { + witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV); + for( size_t i=0; i& target; }; + struct clear_canary_map { + clear_canary_map(flat_map >& target): target(target){} + ~clear_canary_map() { + for(auto& sidechain_target : target){ + sidechain_target.second.clear(); + } + } + private: + flat_map >& target; + }; clear_canary a(_witness_count_histogram_buffer), b(_committee_count_histogram_buffer), - d(_son_count_histogram_buffer), c(_vote_tally_buffer); + clear_canary_map d{_son_count_histogram_buffer}; perform_son_tasks(); update_top_n_authorities(*this); diff --git a/libraries/chain/db_witness_schedule.cpp b/libraries/chain/db_witness_schedule.cpp index 084c8e1d..f4df5ac2 100644 --- a/libraries/chain/db_witness_schedule.cpp +++ b/libraries/chain/db_witness_schedule.cpp @@ -74,21 +74,31 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const return wid; } -son_id_type database::get_scheduled_son( uint32_t slot_num )const +unsigned_int database::get_son_schedule_id( sidechain_type type )const +{ + static const map schedule_map = { + { sidechain_type::hive, 0 }, + { sidechain_type::bitcoin, 1 } + }; + + return schedule_map.at(type); +} + +son_id_type database::get_scheduled_son( sidechain_type type, uint32_t slot_num )const { son_id_type sid; const global_property_object& gpo = get_global_properties(); if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - const son_schedule_object& sso = son_schedule_id_type()(*this); + const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this); uint64_t current_aslot = dpo.current_aslot + slot_num; return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ]; } if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM && slot_num != 0 ) { - const son_schedule_object& sso = son_schedule_id_type()(*this); + const son_schedule_object& sso = son_schedule_id_type(get_son_schedule_id(type))(*this); // ask the near scheduler who goes in the given slot bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid); if(! slot_is_near) @@ -191,36 +201,41 @@ void database::update_witness_schedule() void database::update_son_schedule() { - const son_schedule_object& sso = son_schedule_id_type()(*this); const global_property_object& gpo = get_global_properties(); - if( head_block_num() % gpo.active_sons.size() == 0 ) + for(const auto& active_sidechain_type : active_sidechain_types) { - modify( sso, [&]( son_schedule_object& _sso ) + const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); + if( head_block_num() % gpo.active_sons.at(active_sidechain_type).size() == 0) { - _sso.current_shuffled_sons.clear(); - _sso.current_shuffled_sons.reserve( gpo.active_sons.size() ); - - for( const son_info& w : gpo.active_sons ) - _sso.current_shuffled_sons.push_back( w.son_id ); - - auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; - for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i ) + modify( sidechain_sso, [&]( son_schedule_object& _sso ) { - /// High performance random generator - /// http://xorshift.di.unimi.it/ - uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL; - k ^= (k >> 12); - k ^= (k << 25); - k ^= (k >> 27); - k *= 2685821657736338717ULL; + _sso.current_shuffled_sons.clear(); + _sso.current_shuffled_sons.reserve( gpo.active_sons.at(active_sidechain_type).size() ); - uint32_t jmax = _sso.current_shuffled_sons.size() - i; - uint32_t j = i + k%jmax; - std::swap( _sso.current_shuffled_sons[i], - _sso.current_shuffled_sons[j] ); - } - }); + for ( const son_info &w : gpo.active_sons.at(active_sidechain_type) ) { + _sso.current_shuffled_sons.push_back(w.son_id); + } + + auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32; + + for (uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i) + { + /// High performance random generator + /// http://xorshift.di.unimi.it/ + uint64_t k = now_hi + uint64_t(i) * 2685821657736338717ULL; + k ^= (k >> 12); + k ^= (k << 25); + k ^= (k >> 27); + k *= 2685821657736338717ULL; + + uint32_t jmax = _sso.current_shuffled_sons.size() - i; + uint32_t j = i + k % jmax; + std::swap(_sso.current_shuffled_sons[i], + _sso.current_shuffled_sons[j]); + } + }); + } } } @@ -309,7 +324,15 @@ void database::update_son_schedule(const signed_block& next_block) auto start = fc::time_point::now(); const global_property_object& gpo = get_global_properties(); const son_schedule_object& sso = get(son_schedule_id_type()); - uint32_t schedule_needs_filled = gpo.active_sons.size(); + const flat_map schedule_needs_filled = [&gpo]() + { + flat_map schedule_needs_filled; + for(const auto& sidechain_active_sons : gpo.active_sons) + { + schedule_needs_filled[sidechain_active_sons.first] = sidechain_active_sons.second.size(); + } + return schedule_needs_filled; + }(); uint32_t schedule_slot = get_slot_at_time(next_block.timestamp); // We shouldn't be able to generate _pending_block with timestamp @@ -319,48 +342,52 @@ void database::update_son_schedule(const signed_block& next_block) assert( schedule_slot > 0 ); - son_id_type first_son; - bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son ); - - son_id_type son; - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); assert( dpo.random.data_size() == witness_scheduler_rng::seed_length ); assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() ); - modify(sso, [&](son_schedule_object& _sso) + for(const auto& active_sidechain_type : active_sidechain_types) { - _sso.slots_since_genesis += schedule_slot; - witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis); + const son_schedule_object& sidechain_sso = get(son_schedule_id_type(get_son_schedule_id(active_sidechain_type))); + son_id_type first_son; + bool slot_is_near = sidechain_sso.scheduler.get_slot( schedule_slot-1, first_son ); + son_id_type son_id; - _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1); + modify(sidechain_sso, [&](son_schedule_object& _sso) + { + _sso.slots_since_genesis += schedule_slot; + witness_scheduler_rng rng(_sso.rng_seed.data, _sso.slots_since_genesis); + + _sso.scheduler._min_token_count = std::max(int(gpo.active_sons.at(active_sidechain_type).size()) / 2, 1); + + if( slot_is_near ) + { + uint32_t drain = schedule_slot; + while( drain > 0 ) + { + if( _sso.scheduler.size() == 0 ) + break; + _sso.scheduler.consume_schedule(); + --drain; + } + } + else + { + _sso.scheduler.reset_schedule( first_son ); + } + while( !_sso.scheduler.get_slot(schedule_needs_filled.at(active_sidechain_type), son_id) ) + { + if( _sso.scheduler.produce_schedule(rng) & emit_turn ) + memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); + } + _sso.last_scheduling_block = next_block.block_num(); + _sso.recent_slots_filled = ( + (_sso.recent_slots_filled << 1) + + 1) << (schedule_slot - 1); + }); + } - if( slot_is_near ) - { - uint32_t drain = schedule_slot; - while( drain > 0 ) - { - if( _sso.scheduler.size() == 0 ) - break; - _sso.scheduler.consume_schedule(); - --drain; - } - } - else - { - _sso.scheduler.reset_schedule( first_son ); - } - while( !_sso.scheduler.get_slot(schedule_needs_filled, son) ) - { - if( _sso.scheduler.produce_schedule(rng) & emit_turn ) - memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size()); - } - _sso.last_scheduling_block = next_block.block_num(); - _sso.recent_slots_filled = ( - (_sso.recent_slots_filled << 1) - + 1) << (schedule_slot - 1); - }); auto end = fc::time_point::now(); static uint64_t total_time = 0; static uint64_t calls = 0; diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index f62df938..eeb25167 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -245,7 +245,16 @@ namespace graphene { namespace chain { witness_id_type get_scheduled_witness(uint32_t slot_num)const; /** - * @brief Get the son scheduled for block production in a slot. + * @brief Get son schedule id for the given sidechain_type. + * + * type sidechain_type we getting schedule. + * + * returns Id of the schedule object. + */ + unsigned_int get_son_schedule_id(sidechain_type type)const; + + /** + * @brief Get the bitcoin or hive son scheduled for block production in a slot. * * slot_num always corresponds to a time in the future. * @@ -258,7 +267,7 @@ namespace graphene { namespace chain { * * Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS */ - son_id_type get_scheduled_son(uint32_t slot_num)const; + son_id_type get_scheduled_son(sidechain_type type, uint32_t slot_num)const; /** * Get the time at which the given slot occurs. @@ -313,7 +322,7 @@ namespace graphene { namespace chain { fc::optional create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son ); signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op ); bool is_son_dereg_valid( son_id_type son_id ); - bool is_son_active( son_id_type son_id ); + bool is_son_active( sidechain_type type, son_id_type son_id ); bool is_asset_creation_allowed(const string& symbol); time_point_sec head_block_time()const; @@ -517,6 +526,9 @@ namespace graphene { namespace chain { template vector> sort_votable_objects(size_t count)const; + template + vector> sort_votable_objects(sidechain_type sidechain, size_t count)const; + //////////////////// db_block.cpp //////////////////// public: @@ -571,13 +583,14 @@ namespace graphene { namespace chain { void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void update_active_witnesses(); void update_active_committee_members(); - void update_son_metrics( const vector& curr_active_sons ); + void update_son_metrics( const flat_map >& curr_active_sons ); void update_active_sons(); void remove_son_proposal( const proposal_object& proposal ); void remove_inactive_son_down_proposals( const vector& son_ids_to_remove ); void remove_inactive_son_proposals( const vector& son_ids_to_remove ); - void update_son_statuses( const vector& cur_active_sons, const vector& new_active_sons ); - void update_son_wallet( const vector& new_active_sons ); + void update_son_statuses( const flat_map >& curr_active_sons, + const flat_map >& new_active_sons ); + void update_son_wallet( const flat_map >& new_active_sons ); void update_worker_votes(); public: @@ -616,11 +629,17 @@ namespace graphene { namespace chain { uint16_t _current_op_in_trx = 0; uint32_t _current_virtual_op = 0; - vector _vote_tally_buffer; - vector _witness_count_histogram_buffer; - vector _committee_count_histogram_buffer; - vector _son_count_histogram_buffer; - uint64_t _total_voting_stake; + vector _vote_tally_buffer; + vector _witness_count_histogram_buffer; + vector _committee_count_histogram_buffer; + flat_map > _son_count_histogram_buffer = []{ + flat_map > son_count_histogram_buffer; + for(const auto& active_sidechain_type : active_sidechain_types){ + son_count_histogram_buffer[active_sidechain_type] = vector{}; + } + return son_count_histogram_buffer; + }(); + uint64_t _total_voting_stake; flat_map _checkpoints; diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 53bdec08..71aba1dd 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -49,10 +49,18 @@ namespace graphene { namespace chain { chain_parameters parameters; optional pending_parameters; - uint32_t next_available_vote_id = 0; - vector active_committee_members; // updated once per maintenance interval - flat_set active_witnesses; // updated once per maintenance interval - vector active_sons; // updated once per maintenance interval + uint32_t next_available_vote_id = 0; + vector active_committee_members; // updated once per maintenance interval + flat_set active_witnesses; // updated once per maintenance interval + flat_map > active_sons = []() // updated once per maintenance interval + { + flat_map > active_sons; + for(const auto& active_sidechain_type : active_sidechain_types) + { + active_sons[active_sidechain_type] = vector(); + } + return active_sons; + }(); // n.b. witness scheduling is done by witness_schedule object }; diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index c6de2047..9c4c16d5 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -39,7 +40,15 @@ namespace graphene { namespace chain { { struct ext { - optional< uint16_t > num_son = 0; + /// The number of active son members this account votes the blockchain should appoint + /// Must not exceed the actual number of son members voted for in @ref votes + optional< flat_map > num_son = []{ + flat_map num_son; + for(const auto& active_sidechain_type : active_sidechain_types){ + num_son[active_sidechain_type] = 0; + } + return num_son; + }(); }; /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- @@ -57,14 +66,11 @@ namespace graphene { namespace chain { /// The number of active committee members this account votes the blockchain should appoint /// Must not exceed the actual number of committee members voted for in @ref votes uint16_t num_committee = 0; - /// The number of active son members this account votes the blockchain should appoint - /// Must not exceed the actual number of son members voted for in @ref votes - uint16_t num_son() const { return extensions.value.num_son.valid() ? *extensions.value.num_son : 0; } /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// account's balance of core asset. flat_set votes; extension< ext > extensions; - + /// Whether this account is voting inline bool is_voting() const { @@ -249,7 +255,7 @@ namespace graphene { namespace chain { */ struct account_upgrade_operation : public base_operation { - struct fee_parameters_type { + struct fee_parameters_type { uint64_t membership_annual_fee = 2000 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t membership_lifetime_fee = 10000 * GRAPHENE_BLOCKCHAIN_PRECISION; ///< the cost to upgrade to a lifetime member }; @@ -294,7 +300,7 @@ namespace graphene { namespace chain { } } // graphene::chain -FC_REFLECT(graphene::chain::account_options::ext, (num_son) ) +FC_REFLECT(graphene::chain::account_options::ext, (num_son)) FC_REFLECT(graphene::chain::account_options, (memo_key)(voting_account)(num_witness)(num_committee)(votes)(extensions)) // FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing) FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing, diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp index 5194bed2..75c3db85 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp @@ -11,7 +11,7 @@ namespace graphene { namespace chain { asset fee; account_id_type payer; - vector sons; + flat_map > sons; account_id_type fee_payer()const { return payer; } share_type calculate_fee(const fee_parameters_type& k)const { return 0; } diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 8a46954d..023bb511 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -59,7 +59,8 @@ struct vote_id_type committee, witness, worker, - son, + son_bitcoin, + son_hive, VOTE_TYPE_COUNT }; @@ -144,7 +145,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui FC_REFLECT_TYPENAME( fc::flat_set ) -FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) +FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son_bitcoin)(son_hive)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type ) diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp index b8aa07c6..73be44d5 100644 --- a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -31,7 +31,7 @@ namespace graphene { namespace chain { time_point_sec expires; sidechain_address_object() : - sidechain(sidechain_type::bitcoin), + sidechain(sidechain_type::bitcoin), //! FIXME - bitcoin ??? deposit_public_key(""), deposit_address(""), withdraw_public_key(""), diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp index 7f986f96..1097323b 100644 --- a/libraries/chain/include/graphene/chain/sidechain_defs.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include namespace graphene { namespace chain { @@ -13,12 +14,14 @@ enum class sidechain_type { hive }; +static const std::set active_sidechain_types = {sidechain_type::bitcoin, sidechain_type::hive}; + } } FC_REFLECT_ENUM(graphene::chain::sidechain_type, - (unknown) - (bitcoin) - (ethereum) - (eos) - (hive) - (peerplays) ) + (unknown) + (bitcoin) + (ethereum) + (eos) + (hive) + (peerplays) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp index 2bfecac4..75e58319 100644 --- a/libraries/chain/include/graphene/chain/son_info.hpp +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -14,26 +14,15 @@ namespace graphene { namespace chain { son_id_type son_id; weight_type weight = 0; public_key_type signing_key; - flat_map sidechain_public_keys; + string public_key; - bool operator==(const son_info& rhs) { + bool operator==(const son_info& rhs) const { bool son_sets_equal = (son_id == rhs.son_id) && (weight == rhs.weight) && (signing_key == rhs.signing_key) && - (sidechain_public_keys.size() == rhs.sidechain_public_keys.size()); + (public_key == rhs.public_key); - if (son_sets_equal) { - bool sidechain_public_keys_equal = true; - for (size_t i = 0; i < sidechain_public_keys.size(); i++) { - const auto lhs_scpk = sidechain_public_keys.nth(i); - const auto rhs_scpk = rhs.sidechain_public_keys.nth(i); - sidechain_public_keys_equal = sidechain_public_keys_equal && - (lhs_scpk->first == rhs_scpk->first) && - (lhs_scpk->second == rhs_scpk->second); - } - son_sets_equal = son_sets_equal && sidechain_public_keys_equal; - } return son_sets_equal; } }; @@ -44,4 +33,4 @@ FC_REFLECT( graphene::chain::son_info, (son_id) (weight) (signing_key) - (sidechain_public_keys) ) + (public_key) ) diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index d0b74e79..c9089bfb 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -35,15 +35,15 @@ namespace graphene { namespace chain { // Transactions signed since the last son payouts flat_map txs_signed; // Total Voted Active time i.e. duration selected as part of voted active SONs - uint64_t total_voted_time = 0; + flat_map total_voted_time; // Total Downtime barring the current down time in seconds, used for stats to present to user - uint64_t total_downtime = 0; + flat_map total_downtime; // Current Interval Downtime since last maintenance - uint64_t current_interval_downtime = 0; + flat_map current_interval_downtime; // Down timestamp, if son status is in_maintenance use this - fc::time_point_sec last_down_timestamp; + flat_map last_down_timestamp; // Last Active heartbeat timestamp - fc::time_point_sec last_active_timestamp; + flat_map last_active_timestamp; // Deregistered Timestamp fc::time_point_sec deregistered_timestamp; // Total sidechain transactions reported by SON network while SON was active @@ -64,23 +64,36 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_object_type; account_id_type son_account; - vote_id_type vote_id; - uint64_t total_votes = 0; + flat_map sidechain_vote_ids; + flat_map total_votes; string url; vesting_balance_id_type deposit; public_key_type signing_key; vesting_balance_id_type pay_vb; son_statistics_id_type statistics; - son_status status = son_status::inactive; + flat_map statuses = []() + { + flat_map statuses; + for(const auto& active_sidechain_type : active_sidechain_types) + { + statuses[active_sidechain_type] = son_status::inactive; + } + return statuses; + }(); flat_map sidechain_public_keys; void pay_son_fee(share_type pay, database& db); bool has_valid_config()const; bool has_valid_config(time_point_sec head_block_time)const; + + inline vote_id_type get_sidechain_vote_id(sidechain_type sidechain) const { return sidechain_vote_ids.at(sidechain); } + inline vote_id_type get_bitcoin_vote_id() const { return get_sidechain_vote_id(sidechain_type::bitcoin); } + inline vote_id_type get_hive_vote_id() const { return get_sidechain_vote_id(sidechain_type::hive); } }; struct by_account; - struct by_vote_id; + struct by_vote_id_bitcoin; + struct by_vote_id_hive; using son_multi_index_type = multi_index_container< son_object, indexed_by< @@ -90,8 +103,11 @@ namespace graphene { namespace chain { ordered_unique< tag, member >, - ordered_unique< tag, - member + ordered_unique< tag, + const_mem_fun + >, + ordered_unique< tag, + const_mem_fun > > >; @@ -117,14 +133,14 @@ FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintena FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object), (son_account) - (vote_id) + (sidechain_vote_ids) (total_votes) (url) (deposit) (signing_key) (pay_vb) (statistics) - (status) + (statuses) (sidechain_public_keys) ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index 315def33..63b546ea 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -21,7 +21,7 @@ namespace graphene { namespace chain { time_point_sec expires; flat_map addresses; - vector sons; + flat_map > sons; }; struct by_valid_from; diff --git a/libraries/chain/include/graphene/chain/voters_info.hpp b/libraries/chain/include/graphene/chain/voters_info.hpp index 53b0e74a..86f3e9cc 100644 --- a/libraries/chain/include/graphene/chain/voters_info.hpp +++ b/libraries/chain/include/graphene/chain/voters_info.hpp @@ -19,11 +19,11 @@ namespace graphene { namespace chain { * @ingroup object */ struct voters_info { - optional voters_for_committee_member; - optional voters_for_witness; - optional > voters_for_workers; - optional > voters_against_workers; - optional voters_for_son; + optional voters_for_committee_member; + optional voters_for_witness; + optional > voters_for_workers; + optional > voters_against_workers; + optional > voters_for_son; }; } } // graphene::chain @@ -37,4 +37,4 @@ FC_REFLECT( graphene::chain::voters_info, (voters_for_witness) (voters_for_workers) (voters_against_workers) - (voters_for_son) ) \ No newline at end of file + (voters_for_son)) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/votes_info.hpp b/libraries/chain/include/graphene/chain/votes_info.hpp index 0a515589..f405d83a 100644 --- a/libraries/chain/include/graphene/chain/votes_info.hpp +++ b/libraries/chain/include/graphene/chain/votes_info.hpp @@ -19,11 +19,11 @@ namespace graphene { namespace chain { * @ingroup object */ struct votes_info { - optional< vector< votes_info_object > > votes_for_committee_members; - optional< vector< votes_info_object > > votes_for_witnesses; - optional< vector< votes_info_object > > votes_for_workers; - optional< vector< votes_info_object > > votes_against_workers; - optional< vector< votes_info_object > > votes_for_sons; + optional< vector< votes_info_object > > votes_for_committee_members; + optional< vector< votes_info_object > > votes_for_witnesses; + optional< vector< votes_info_object > > votes_for_workers; + optional< vector< votes_info_object > > votes_against_workers; + optional< flat_map > > votes_for_sons; }; } } // graphene::chain @@ -37,4 +37,4 @@ FC_REFLECT( graphene::chain::votes_info, (votes_for_witnesses) (votes_for_workers) (votes_against_workers) - (votes_for_sons) ) \ No newline at end of file + (votes_for_sons)) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp index 2eff563f..945b218d 100644 --- a/libraries/chain/include/graphene/chain/witness_schedule_object.hpp +++ b/libraries/chain/include/graphene/chain/witness_schedule_object.hpp @@ -96,7 +96,7 @@ class son_schedule_object : public graphene::db::abstract_object current_shuffled_sons; + vector current_shuffled_sons; son_scheduler scheduler; uint32_t last_scheduling_block; diff --git a/libraries/chain/include/graphene/chain/witness_scheduler.hpp b/libraries/chain/include/graphene/chain/witness_scheduler.hpp index 42dfe149..40e0e1d2 100644 --- a/libraries/chain/include/graphene/chain/witness_scheduler.hpp +++ b/libraries/chain/include/graphene/chain/witness_scheduler.hpp @@ -162,8 +162,12 @@ class generic_witness_scheduler _schedule.pop_front(); auto it = _lame_duck.find( result ); - if( it != _lame_duck.end() ) - _lame_duck.erase( it ); + if( it != _lame_duck.end() ) { + set< WitnessID > removal_set; + removal_set.insert(*it); + remove_all( removal_set ); + _lame_duck.erase(it); + } if( debug ) check_invariant(); return result; } @@ -389,7 +393,7 @@ class generic_witness_scheduler // scheduled std::deque < WitnessID > _schedule; - // in _schedule, but not to be replaced + // in _schedule, but must be removed set< WitnessID > _lame_duck; }; diff --git a/libraries/chain/protocol/account.cpp b/libraries/chain/protocol/account.cpp index b980998c..bee6b1be 100644 --- a/libraries/chain/protocol/account.cpp +++ b/libraries/chain/protocol/account.cpp @@ -174,22 +174,27 @@ void account_options::validate() const { auto needed_witnesses = num_witness; auto needed_committee = num_committee; - auto needed_sons = num_son(); + FC_ASSERT( extensions.value.num_son.valid() , "Invalid son number" ); + flat_map needed_sons = *extensions.value.num_son; for( vote_id_type id : votes ) if( id.type() == vote_id_type::witness && needed_witnesses ) --needed_witnesses; else if ( id.type() == vote_id_type::committee && needed_committee ) --needed_committee; - else if ( id.type() == vote_id_type::son && needed_sons ) - --needed_sons; + else if ( id.type() == vote_id_type::son_bitcoin && needed_sons[sidechain_type::bitcoin] ) + --needed_sons[sidechain_type::bitcoin]; + else if ( id.type() == vote_id_type::son_hive && needed_sons[sidechain_type::hive] ) + --needed_sons[sidechain_type::hive]; FC_ASSERT( needed_witnesses == 0, "May not specify fewer witnesses than the number voted for."); FC_ASSERT( needed_committee == 0, "May not specify fewer committee members than the number voted for."); - FC_ASSERT( needed_sons == 0, - "May not specify fewer SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::bitcoin] == 0, + "May not specify fewer Bitcoin SONs than the number voted for."); + FC_ASSERT( needed_sons[sidechain_type::hive] == 0, + "May not specify fewer Hive SONs than the number voted for."); } void affiliate_reward_distribution::validate() const diff --git a/libraries/chain/sidechain_address_evaluator.cpp b/libraries/chain/sidechain_address_evaluator.cpp index 625ef2f0..bd97fef5 100644 --- a/libraries/chain/sidechain_address_evaluator.cpp +++ b/libraries/chain/sidechain_address_evaluator.cpp @@ -47,7 +47,7 @@ void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_addr { try { const auto& sidx = db().get_index_type().indices().get(); const auto& son_obj = sidx.find(op.payer); - FC_ASSERT( son_obj != sidx.end() && db().is_son_active(son_obj->id), "Non active SON trying to update deposit address object" ); + FC_ASSERT( son_obj != sidx.end() && db().is_son_active(op.sidechain, son_obj->id), "Non active SON trying to update deposit address object" ); const auto& sdpke_idx = db().get_index_type().indices().get(); FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid"); FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key"); diff --git a/libraries/chain/son_evaluator.cpp b/libraries/chain/son_evaluator.cpp index 776eb065..8d24bad6 100644 --- a/libraries/chain/son_evaluator.cpp +++ b/libraries/chain/son_evaluator.cpp @@ -38,14 +38,17 @@ void_result create_son_evaluator::do_evaluate(const son_create_operation& op) object_id_type create_son_evaluator::do_apply(const son_create_operation& op) { try { - vote_id_type vote_id; - db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) { - vote_id = get_next_vote_id(p, vote_id_type::son); + vote_id_type vote_id_bitcoin; + vote_id_type vote_id_hive; + db().modify(db().get_global_properties(), [&vote_id_bitcoin, &vote_id_hive](global_property_object& p) { + vote_id_bitcoin = get_next_vote_id(p, vote_id_type::son_bitcoin); + vote_id_hive = get_next_vote_id(p, vote_id_type::son_hive); }); const auto& new_son_object = db().create( [&]( son_object& obj ){ obj.son_account = op.owner_account; - obj.vote_id = vote_id; + obj.sidechain_vote_ids[sidechain_type::bitcoin] = vote_id_bitcoin; + obj.sidechain_vote_ids[sidechain_type::hive] = vote_id_hive; obj.url = op.url; obj.deposit = op.deposit; obj.signing_key = op.signing_key; @@ -94,7 +97,8 @@ object_id_type update_son_evaluator::do_apply(const son_update_operation& op) if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key; if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys; if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb; - if(so.status == son_status::deregistered) so.status = son_status::inactive; + for(auto& status : so.statuses) + if(status.second == son_status::deregistered) status.second = son_status::inactive; }); } return op.son_id; @@ -127,7 +131,8 @@ void_result deregister_son_evaluator::do_apply(const son_deregister_operation& o }); db().modify(*son, [&op](son_object &so) { - so.status = son_status::deregistered; + for(auto& status : so.statuses) + status.second = son_status::deregistered; }); auto stats_obj = ss_idx.find(son->statistics); @@ -144,18 +149,28 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& { try { FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_id); + const auto itr = idx.find(op.son_id); FC_ASSERT( itr != idx.end() ); FC_ASSERT(itr->son_account == op.owner_account); auto stats = itr->statistics( db() ); // Inactive SONs need not send heartbeats - FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats"); + bool status_need_to_send_heartbeats = false; + for(const auto& status : itr->statuses) + { + if( (status.second == son_status::active) || (status.second == son_status::in_maintenance) || (status.second == son_status::request_maintenance) ) + status_need_to_send_heartbeats = true; + } + FC_ASSERT(status_need_to_send_heartbeats, "Inactive SONs need not send heartbeats"); // Account for network delays fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval()); // Account for server ntp sync difference fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval()); - FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time"); - FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp"); + for(const auto& active_sidechain_type : active_sidechain_types) { + if(stats.last_active_timestamp.contains(active_sidechain_type)) + FC_ASSERT(op.ts > stats.last_active_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} without waiting minimum time", ("sidechain", active_sidechain_type)); + if(stats.last_down_timestamp.contains(active_sidechain_type)) + FC_ASSERT(op.ts > stats.last_down_timestamp.at(active_sidechain_type), "Heartbeat sent for sidechain = ${sidechain} is invalid can't be <= last down timestamp", ("sidechain", active_sidechain_type)); + } FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold"); FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold"); return void_result(); @@ -164,44 +179,48 @@ void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_id); + const auto itr = idx.find(op.son_id); if(itr != idx.end()) { const global_property_object& gpo = db().get_global_properties(); - vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(active_son_ids, active_son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); - bool is_son_active = true; + for(const auto& active_sidechain_sons : gpo.active_sons) { + const auto& sidechain = active_sidechain_sons.first; + const auto& active_sons = active_sidechain_sons.second; - if(it_son == active_son_ids.end()) { - is_son_active = false; - } + vector active_son_ids; + active_son_ids.reserve(active_sons.size()); + std::transform(active_sons.cbegin(), active_sons.cend(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info &swi) { + return swi.son_id; + }); - if(itr->status == son_status::in_maintenance) { - db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) - { - sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch(); - sso.last_active_timestamp = op.ts; - } ); + const auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id); + bool is_son_active = true; - db().modify(*itr, [&is_son_active](son_object &so) { - if(is_son_active) { - so.status = son_status::active; - } else { - so.status = son_status::inactive; - } - }); - } else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { - db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) - { - sso.last_active_timestamp = op.ts; - } ); + if (it_son == active_son_ids.end()) { + is_son_active = false; + } + + if (itr->statuses.at(sidechain) == son_status::in_maintenance) { + db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { + sso.current_interval_downtime[sidechain] += op.ts.sec_since_epoch() - sso.last_down_timestamp.at(sidechain).sec_since_epoch(); + sso.last_active_timestamp[sidechain] = op.ts; + }); + + db().modify(*itr, [&is_son_active, &sidechain](son_object &so) { + if (is_son_active) { + so.statuses[sidechain] = son_status::active; + } else { + so.statuses[sidechain] = son_status::inactive; + } + }); + } else if ((itr->statuses.at(sidechain) == son_status::active) || (itr->statuses.at(sidechain) == son_status::request_maintenance)) { + db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { + sso.last_active_timestamp[sidechain] = op.ts; + }); + } } } return op.son_id; @@ -213,29 +232,40 @@ void_result son_report_down_evaluator::do_evaluate(const son_report_down_operati FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer."); const auto& idx = db().get_index_type().indices().get(); FC_ASSERT( idx.find(op.son_id) != idx.end() ); - auto itr = idx.find(op.son_id); - auto stats = itr->statistics( db() ); - FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); - FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp"); + const auto itr = idx.find(op.son_id); + const auto stats = itr->statistics( db() ); + bool status_need_to_report_down = false; + for(const auto& status : itr->statuses) + { + if( (status.second == son_status::active) || (status.second == son_status::request_maintenance) ) + status_need_to_report_down = true; + } + FC_ASSERT(status_need_to_report_down, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down"); + for(const auto& active_sidechain_type : active_sidechain_types) { + FC_ASSERT(op.down_ts >= stats.last_active_timestamp.at(active_sidechain_type), "sidechain = ${sidechain} down_ts should be greater than last_active_timestamp", ("sidechain", active_sidechain_type)); + } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op) { try { const auto& idx = db().get_index_type().indices().get(); - auto itr = idx.find(op.son_id); + const auto itr = idx.find(op.son_id); if(itr != idx.end()) { - if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) { - db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso ) - { - sso.last_down_timestamp = op.down_ts; - }); + for( const auto& status : itr->statuses ) { + const auto& sidechain = status.first; - db().modify(*itr, [&op](son_object &so) { - so.status = son_status::in_maintenance; - }); - } + if ((status.second == son_status::active) || (status.second == son_status::request_maintenance)) { + db().modify(*itr, [&sidechain](son_object &so) { + so.statuses[sidechain] = son_status::in_maintenance; + }); + + db().modify(itr->statistics(db()), [&](son_statistics_object &sso) { + sso.last_down_timestamp[sidechain] = op.down_ts; + }); + } + } } return op.son_id; } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -249,9 +279,19 @@ void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operati FC_ASSERT( itr != idx.end() ); // Inactive SONs can't go to maintenance, toggle between active and request_maintenance states if(op.request_type == son_maintenance_request_type::request_maintenance) { - FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance"); - } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { - FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request"); + bool status_active = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::active) ) + status_active = true; + } + FC_ASSERT(status_active, "Inactive SONs can't request for maintenance"); + } else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + bool status_request_maintenance = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::request_maintenance) ) + status_request_maintenance = true; + } + FC_ASSERT(status_request_maintenance, "Only maintenance requested SONs can cancel the request"); } else { FC_ASSERT(false, "Invalid maintenance operation"); } @@ -264,15 +304,33 @@ object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operati auto itr = idx.find(op.son_id); if(itr != idx.end()) { - if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) { - db().modify(*itr, [](son_object &so) { - so.status = son_status::request_maintenance; - }); - } else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { - db().modify(*itr, [](son_object &so) { - so.status = son_status::active; - }); - } + bool status_active = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::active) ) + status_active = true; + } + if(status_active && op.request_type == son_maintenance_request_type::request_maintenance) { + db().modify(*itr, [](son_object &so) { + for(auto& status : so.statuses) { + status.second = son_status::request_maintenance; + } + }); + } + else + { + bool status_request_maintenance = false; + for(const auto& status : itr->statuses) { + if( (status.second == son_status::request_maintenance) ) + status_request_maintenance = true; + } + if(status_request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) { + db().modify(*itr, [](son_object &so) { + for(auto& status : so.statuses) { + status.second = son_status::active; + } + }); + } + } } return op.son_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index f9620282..8d0199d4 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -23,9 +23,9 @@ void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_de const auto &swdo_idx = db().get_index_type().indices().get(); const auto swdo = swdo_idx.find(op.sidechain_uid); if (swdo == swdo_idx.end()) { - auto &gpo = db().get_global_properties(); + const auto &gpo = db().get_global_properties(); bool expected = false; - for (auto &si : gpo.active_sons) { + for (auto &si : gpo.active_sons.at(op.sidechain)) { if (op.son_id == si.son_id) { expected = true; break; @@ -78,8 +78,8 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de swdo.peerplays_to = op.peerplays_to; swdo.peerplays_asset = op.peerplays_asset; - auto &gpo = db().get_global_properties(); - for (auto &si : gpo.active_sons) { + const auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons.at(op.sidechain)) { swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); @@ -142,11 +142,11 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); - FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_deposit_id); FC_ASSERT(itr != idx.end(), "Son wallet deposit not found"); + FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); FC_ASSERT(!itr->processed, "Son wallet deposit is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp index 0baed1cb..9b1ff1b7 100644 --- a/libraries/chain/son_wallet_evaluator.cpp +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -20,8 +20,16 @@ void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size()); if (son_sets_equal) { - for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) { - son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i); + for( const auto& cur_wallet_sidechain_sons : cur_wallet_sons ) { + const auto& sidechain = cur_wallet_sidechain_sons.first; + const auto& _cur_wallet_sidechain_sons = cur_wallet_sidechain_sons.second; + + son_sets_equal = son_sets_equal && (_cur_wallet_sidechain_sons.size() == new_wallet_sons.at(sidechain).size()); + if (son_sets_equal) { + for (size_t i = 0; i < cur_wallet_sons.size(); i++) { + son_sets_equal = son_sets_equal && _cur_wallet_sidechain_sons.at(i) == new_wallet_sons.at(sidechain).at(i); + } + } } } diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index 2110e49d..ae0a867b 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -23,15 +23,15 @@ void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_w const auto &swwo_idx = db().get_index_type().indices().get(); const auto swwo = swwo_idx.find(op.peerplays_uid); if (swwo == swwo_idx.end()) { - auto &gpo = db().get_global_properties(); + const auto &gpo = db().get_global_properties(); bool expected = false; - for (auto &si : gpo.active_sons) { + for (auto &si : gpo.active_sons.at(op.sidechain)) { if (op.son_id == si.son_id) { expected = true; break; } } - FC_ASSERT(expected, "Only active SON can create deposit"); + FC_ASSERT(expected, "Only active SON can create withdraw"); } else { bool exactly_the_same = true; exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain); @@ -76,8 +76,8 @@ object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_w swwo.withdraw_currency = op.withdraw_currency; swwo.withdraw_amount = op.withdraw_amount; - auto &gpo = db().get_global_properties(); - for (auto &si : gpo.active_sons) { + const auto &gpo = db().get_global_properties(); + for (auto &si : gpo.active_sons.at(op.sidechain)) { swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight)); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); @@ -140,11 +140,11 @@ void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_ { try{ FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." ); - FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); const auto& idx = db().get_index_type().indices().get(); const auto& itr = idx.find(op.son_wallet_withdraw_id); FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found"); + FC_ASSERT(db().get_global_properties().active_sons.at(itr->sidechain).size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present"); FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 9ea2ce34..d7337e42 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -3,7 +3,7 @@ file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp") add_library( peerplays_sidechain peerplays_sidechain_plugin.cpp sidechain_api.cpp - sidechain_net_manager.cpp + sidechain_net_handler_factory.cpp sidechain_net_handler.cpp sidechain_net_handler_bitcoin.cpp sidechain_net_handler_hive.cpp diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index 92591d0a..114f9bc7 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -13,16 +13,18 @@ class peerplays_sidechain_plugin_impl; } struct son_proposal_type { - son_proposal_type(int op, son_id_type son, object_id_type object) : + son_proposal_type(int op, sidechain_type sid, son_id_type son, object_id_type object) : op_type(op), + sidechain(sid), son_id(son), object_id(object) { } int op_type; + sidechain_type sidechain; son_id_type son_id; object_id_type object_id; bool operator<(const son_proposal_type &other) const { - return std::tie(op_type, son_id, object_id) < std::tie(other.op_type, other.son_id, other.object_id); + return std::tie(op_type, sidechain, son_id, object_id) < std::tie(other.op_type, other.sidechain, other.son_id, other.object_id); } }; @@ -42,15 +44,15 @@ public: std::unique_ptr my; std::set &get_sons(); - const son_id_type get_current_son_id(); - const son_object get_current_son_object(); + const son_id_type get_current_son_id(sidechain_type sidechain); + const son_object get_current_son_object(sidechain_type sidechain); const son_object get_son_object(son_id_type son_id); - bool is_active_son(son_id_type son_id); + bool is_active_son(sidechain_type sidechain, son_id_type son_id); bool is_son_deregistered(son_id_type son_id); fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(chain::public_key_type public_key); - void log_son_proposal_retry(int op_type, object_id_type object_id); - bool can_son_participate(int op_type, object_id_type object_id); + void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); + bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp new file mode 100644 index 00000000..3aa42894 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_factory.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace graphene { namespace peerplays_sidechain { + +class sidechain_net_handler_factory { +public: + sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin); + + std::unique_ptr create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const; + +private: + peerplays_sidechain_plugin &plugin; +}; + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp deleted file mode 100644 index 8bfda125..00000000 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include - -namespace graphene { namespace peerplays_sidechain { - -class sidechain_net_manager { -public: - sidechain_net_manager(peerplays_sidechain_plugin &_plugin); - virtual ~sidechain_net_manager(); - - bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options); - void process_proposals(); - void process_active_sons_change(); - void create_deposit_addresses(); - void process_deposits(); - void process_withdrawals(); - void process_sidechain_transactions(); - void send_sidechain_transactions(); - void settle_sidechain_transactions(); - - std::map> get_son_listener_log(); - -private: - peerplays_sidechain_plugin &plugin; - graphene::chain::database &database; - std::vector> net_handlers; - - void on_applied_block(const signed_block &b); -}; - -}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index ed80fbfc..461b1a92 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -11,7 +13,7 @@ #include #include #include -#include +#include #include namespace bpo = boost::program_options; @@ -33,36 +35,36 @@ public: void plugin_shutdown(); std::set &get_sons(); - const son_id_type get_current_son_id(); - const son_object get_current_son_object(); + const son_id_type get_current_son_id(sidechain_type sidechain); + const son_object get_current_son_object(sidechain_type sidechain); const son_object get_son_object(son_id_type son_id); - bool is_active_son(son_id_type son_id); + bool is_active_son(sidechain_type sidechain, son_id_type son_id); bool is_son_deregistered(son_id_type son_id); bool is_son_deregister_op_valid(const chain::operation &op); bool is_son_down_op_valid(const chain::operation &op); bool is_valid_son_proposal(const chain::proposal_object &proposal); fc::ecc::private_key get_private_key(son_id_type son_id); fc::ecc::private_key get_private_key(chain::public_key_type public_key); - void log_son_proposal_retry(int op_type, object_id_type object_id); - bool can_son_participate(int op_type, object_id_type object_id); + void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); + bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); void schedule_heartbeat_loop(); void heartbeat_loop(); void schedule_son_processing(); - void son_processing(); - void approve_proposals(); - void create_son_down_proposals(); - void create_son_deregister_proposals(); + void son_processing(sidechain_type sidechain); + void approve_proposals(sidechain_type sidechain); + void create_son_down_proposals(sidechain_type sidechain); + void create_son_deregister_proposals(sidechain_type sidechain); - void process_proposals(); - void process_active_sons_change(); - void create_deposit_addresses(); - void process_deposits(); - void process_withdrawals(); - void process_sidechain_transactions(); - void send_sidechain_transactions(); - void settle_sidechain_transactions(); + void process_proposals(sidechain_type sidechain); + void process_active_sons_change(sidechain_type sidechain); + void create_deposit_addresses(sidechain_type sidechain); + void process_deposits(sidechain_type sidechain); + void process_withdrawals(sidechain_type sidechain); + void process_sidechain_transactions(sidechain_type sidechain); + void send_sidechain_transactions(sidechain_type sidechain); + void settle_sidechain_transactions(sidechain_type sidechain); private: peerplays_sidechain_plugin &plugin; @@ -80,15 +82,19 @@ private: bool sidechain_enabled_hive; bool sidechain_enabled_peerplays; - son_id_type current_son_id; + std::map current_son_id; + std::mutex current_son_id_mutex; + std::mutex access_db_mutex; + std::mutex access_approve_prop_mutex; + std::mutex access_son_down_prop_mutex; - std::unique_ptr net_manager; + std::map> net_handlers; std::set sons; std::map private_keys; fc::future _heartbeat_task; - fc::future _son_processing_task; + std::map> _son_processing_task; std::map son_retry_count; - uint16_t retries_threshold; + uint16_t retries_threshold = 150; bool first_block_skipped; void on_applied_block(const signed_block &b); @@ -105,8 +111,20 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec sidechain_enabled_ethereum(false), sidechain_enabled_hive(false), sidechain_enabled_peerplays(false), - current_son_id(son_id_type(std::numeric_limits().max())), - net_manager(nullptr), + current_son_id([] { + std::map current_son_id; + for (const auto &active_sidechain_type : active_sidechain_types) { + current_son_id.emplace(active_sidechain_type, son_id_type(std::numeric_limits().max())); + } + return current_son_id; + }()), + net_handlers([] { + std::map> net_handlers; + for (const auto &active_sidechain_type : active_sidechain_types) { + net_handlers.emplace(active_sidechain_type, nullptr); + } + return net_handlers; + }()), first_block_skipped(false) { } @@ -121,8 +139,10 @@ peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { } try { - if (_son_processing_task.valid()) - _son_processing_task.cancel_and_wait(__FUNCTION__); + for (const auto &active_sidechain_type : active_sidechain_types) { + if (_son_processing_task.count(active_sidechain_type) != 0 && _son_processing_task.at(active_sidechain_type).valid()) + _son_processing_task.at(active_sidechain_type).wait(); + } } catch (fc::canceled_exception &) { //Expected exception. Move along. } catch (fc::exception &e) { @@ -262,10 +282,10 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { elog("No sons configured! Please add SON IDs and private keys to configuration."); } - net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); + sidechain_net_handler_factory net_handler_factory(plugin); if (sidechain_enabled_bitcoin && config_ready_bitcoin) { - net_manager->create_handler(sidechain_type::bitcoin, options); + net_handlers.at(sidechain_type::bitcoin) = net_handler_factory.create_handler(sidechain_type::bitcoin, options); ilog("Bitcoin sidechain handler running"); } @@ -275,12 +295,12 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { //} if (sidechain_enabled_hive && config_ready_hive) { - net_manager->create_handler(sidechain_type::hive, options); + net_handlers.at(sidechain_type::hive) = net_handler_factory.create_handler(sidechain_type::hive, options); ilog("Hive sidechain handler running"); } if (sidechain_enabled_peerplays && config_ready_peerplays) { - net_manager->create_handler(sidechain_type::peerplays, options); + net_handlers.at(sidechain_type::peerplays) = net_handler_factory.create_handler(sidechain_type::peerplays, options); ilog("Peerplays sidechain handler running"); } @@ -296,12 +316,13 @@ std::set &peerplays_sidechain_plugin_impl::get_sons() { return sons; } -const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() { - return current_son_id; +const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id(sidechain_type sidechain) { + const std::lock_guard lock(current_son_id_mutex); + return current_son_id.at(sidechain); } -const son_object peerplays_sidechain_plugin_impl::get_current_son_object() { - return get_son_object(current_son_id); +const son_object peerplays_sidechain_plugin_impl::get_current_son_object(sidechain_type sidechain) { + return get_son_object(get_current_son_id(sidechain)); } const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { @@ -312,16 +333,15 @@ const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son return *son_obj; } -bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) { +bool peerplays_sidechain_plugin_impl::is_active_son(sidechain_type sidechain, son_id_type son_id) { const auto &idx = plugin.database().get_index_type().indices().get(); auto son_obj = idx.find(son_id); if (son_obj == idx.end()) return false; const chain::global_property_object &gpo = plugin.database().get_global_properties(); - vector active_son_ids; - active_son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), + set active_son_ids; + std::transform(gpo.active_sons.at(sidechain).cbegin(), gpo.active_sons.at(sidechain).cend(), std::inserter(active_son_ids, active_son_ids.end()), [](const son_info &swi) { return swi.son_id; @@ -338,7 +358,13 @@ bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) { if (son_obj == idx.end()) return true; - if (son_obj->status == chain::son_status::deregistered) { + bool status_deregistered = true; + for (const auto &status : son_obj->statuses) { + if ((status.second != son_status::deregistered)) + status_deregistered = false; + } + + if (status_deregistered) { return true; } @@ -362,13 +388,23 @@ bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operatio } auto stats = son_obj->statistics(d); fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); int64_t down_threshold = gpo.parameters.son_down_time(); - if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && - ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { - return true; + + bool status_son_down_op_valid = true; + for (const auto &status : son_obj->statuses) { + if ((status.second != son_status::active) && (status.second != son_status::request_maintenance)) + status_son_down_op_valid = false; } - return false; + if (status_son_down_op_valid) { + for (const auto &active_sidechain_type : active_sidechain_types) { + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(active_sidechain_type) > last_maintenance_time) ? stats.last_active_timestamp.at(active_sidechain_type) : last_maintenance_time); + if (((fc::time_point::now() - last_active_ts) <= fc::seconds(down_threshold))) { + status_son_down_op_valid = false; + } + } + } + + return status_son_down_op_valid; } fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) { @@ -400,7 +436,23 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { chain::database &d = plugin.database(); for (son_id_type son_id : sons) { - if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { + const auto &son_obj = get_son_object(son_id); + + //! Check that son is in_maintenance + bool status_in_maintenance = false; + for (const auto &status : son_obj.statuses) { + if ((status.second == son_status::in_maintenance)) + status_in_maintenance = true; + } + + //! Check that son is active (at least for one sidechain_type) + bool is_son_active = false; + for (const auto &active_sidechain_type : active_sidechain_types) { + if (is_active_son(active_sidechain_type, son_id)) + is_son_active = true; + } + + if (is_son_active || status_in_maintenance) { ilog("Sending heartbeat for SON ${son}", ("son", son_id)); chain::son_heartbeat_operation op; @@ -426,19 +478,23 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { } void peerplays_sidechain_plugin_impl::schedule_son_processing() { - fc::time_point now = fc::time_point::now(); - int64_t time_to_next_son_processing = 500000; + const auto now = std::chrono::steady_clock::now(); + static const int64_t time_to_next_son_processing = 500000; - fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing)); + const auto next_wakeup = now + std::chrono::microseconds(time_to_next_son_processing); - _son_processing_task = fc::schedule([this] { - son_processing(); - }, - next_wakeup, "SON Processing"); + for (const auto &active_sidechain_type : active_sidechain_types) { + _son_processing_task[active_sidechain_type] = std::async(std::launch::async, [this, next_wakeup, active_sidechain_type] { + std::this_thread::sleep_until(next_wakeup); + son_processing(active_sidechain_type); + }); + } } -void peerplays_sidechain_plugin_impl::son_processing() { - if (plugin.database().get_global_properties().active_sons.size() <= 0) { +void peerplays_sidechain_plugin_impl::son_processing(sidechain_type sidechain) { + //! Check whether we have active SONs + if (plugin.database().get_global_properties().active_sons.count(sidechain) == 0 || + plugin.database().get_global_properties().active_sons.at(sidechain).empty()) { return; } @@ -448,50 +504,55 @@ void peerplays_sidechain_plugin_impl::son_processing() { // return; // Not synced //} - fc::time_point now_fine = fc::time_point::now(); - fc::time_point_sec now = now_fine - fc::milliseconds(3000); + const fc::time_point now_fine = fc::time_point::now(); + const fc::time_point_sec now = now_fine - fc::milliseconds(3000); if (plugin.database().head_block_time() < now) { return; // Not synced } - chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1); - ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ", - ("scheduled_son_id", scheduled_son_id)("now", now)); + //! Get scheduled_son_id according to sidechain_type + const chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(sidechain, 1); + ilog("Scheduled SON: ${scheduled_son_id} Sidechain: ${sidechain} Now: ${now}", + ("scheduled_son_id", scheduled_son_id)("sidechain", sidechain)("now", now)); for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_son_deregistered(son_id)) { continue; } - current_son_id = son_id; + + { + const std::lock_guard lock(current_son_id_mutex); + current_son_id.at(sidechain) = son_id; + } // These tasks are executed by // - All active SONs, no matter if scheduled // - All previously active SONs - approve_proposals(); - process_proposals(); - process_sidechain_transactions(); + approve_proposals(sidechain); + process_proposals(sidechain); + process_sidechain_transactions(sidechain); - if (plugin.is_active_son(son_id)) { + if (plugin.is_active_son(sidechain, son_id)) { // Tasks that are executed by scheduled and active SON only - if (current_son_id == scheduled_son_id) { + if (get_current_son_id(sidechain) == scheduled_son_id) { - create_son_down_proposals(); + create_son_down_proposals(sidechain); - create_son_deregister_proposals(); + create_son_deregister_proposals(sidechain); - process_active_sons_change(); + process_active_sons_change(sidechain); - create_deposit_addresses(); + create_deposit_addresses(sidechain); - process_deposits(); + process_deposits(sidechain); - process_withdrawals(); + process_withdrawals(sidechain); - process_sidechain_transactions(); + process_sidechain_transactions(sidechain); - send_sidechain_transactions(); + send_sidechain_transactions(sidechain); - settle_sidechain_transactions(); + settle_sidechain_transactions(sidechain); } } } @@ -514,8 +575,8 @@ bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposa return false; } -void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object_id_type object_id) { - son_proposal_type prop_type(op_type, get_current_son_id(), object_id); +void peerplays_sidechain_plugin_impl::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) { + son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id); auto itr = son_retry_count.find(prop_type); if (itr != son_retry_count.end()) { itr->second++; @@ -524,18 +585,27 @@ void peerplays_sidechain_plugin_impl::log_son_proposal_retry(int op_type, object } } -bool peerplays_sidechain_plugin_impl::can_son_participate(int op_type, object_id_type object_id) { - son_proposal_type prop_type(op_type, get_current_son_id(), object_id); +bool peerplays_sidechain_plugin_impl::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) { + son_proposal_type prop_type(op_type, sidechain, get_current_son_id(sidechain), object_id); auto itr = son_retry_count.find(prop_type); return (itr == son_retry_count.end() || itr->second < retries_threshold); } std::map> peerplays_sidechain_plugin_impl::get_son_listener_log() { - return net_manager->get_son_listener_log(); + std::map> result; + for (const auto &active_sidechain_type : active_sidechain_types) { + result.emplace(active_sidechain_type, net_handlers.at(active_sidechain_type)->get_son_listener_log()); + } + return result; } -void peerplays_sidechain_plugin_impl::approve_proposals() { - +void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) { + // prevent approving duplicate proposals with lock for parallel execution. + // We can have the same propsals, but in the case of parallel execution we can run + // into problem of approving the same propsal since it might happens that previous + // approved proposal didn't have time or chance to populate the list of available + // active proposals which is consulted here in the code. + std::lock_guard lck(access_approve_prop_mutex); auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) { if (!is_valid_son_proposal(proposal)) { return; @@ -549,6 +619,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { fc::future fut = fc::async([&]() { try { trx.validate(); + std::lock_guard lck(access_db_mutex); plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -568,7 +639,6 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { } for (const auto proposal_id : proposals) { - const object *obj = plugin.database().find_object(proposal_id); const chain::proposal_object *proposal_ptr = dynamic_cast(obj); if (proposal_ptr == nullptr) { @@ -576,15 +646,16 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { } const proposal_object proposal = *proposal_ptr; - if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) { + if (proposal.available_active_approvals.find(get_current_son_object(sidechain).son_account) != proposal.available_active_approvals.end()) { continue; } - check_approve_proposal(get_current_son_id(), proposal); + check_approve_proposal(get_current_son_id(sidechain), proposal); } } -void peerplays_sidechain_plugin_impl::create_son_down_proposals() { +void peerplays_sidechain_plugin_impl::create_son_down_proposals(sidechain_type sidechain) { + std::lock_guard lck(access_son_down_prop_mutex); auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) { chain::database &d = plugin.database(); const chain::global_property_object &gpo = d.get_global_properties(); @@ -595,7 +666,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { son_down_op.down_ts = last_active_ts; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = get_current_son_object().son_account; + proposal_op.fee_paying_account = get_current_son_object(sidechain).son_account; proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op)); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime); @@ -607,24 +678,32 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties(); const auto &idx = d.get_index_type().indices().get(); std::set sons_being_reported_down = d.get_sons_being_reported_down(); - chain::son_id_type my_son_id = get_current_son_id(); - for (auto son_inf : gpo.active_sons) { + chain::son_id_type my_son_id = get_current_son_id(sidechain); + + //! Fixme - check this part of the code + for (auto son_inf : gpo.active_sons.at(sidechain)) { if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) { continue; } auto son_obj = idx.find(son_inf.son_id); auto stats = son_obj->statistics(d); fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval; - fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time); + fc::time_point_sec last_active_ts = ((stats.last_active_timestamp.at(sidechain) > last_maintenance_time) ? stats.last_active_timestamp.at(sidechain) : last_maintenance_time); int64_t down_threshold = gpo.parameters.son_down_time(); - if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) && - ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { + + bool status_son_down_valid = true; + for (const auto &status : son_obj->statuses) { + if ((status.second != son_status::active) && (status.second != son_status::request_maintenance)) + status_son_down_valid = false; + } + if ((status_son_down_valid) && ((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) { ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id)))); chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts); chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); fc::future fut = fc::async([&]() { try { trx.validate(); + std::lock_guard lck(access_db_mutex); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -639,10 +718,10 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { } } -void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { +void peerplays_sidechain_plugin_impl::create_son_deregister_proposals(sidechain_type sidechain) { chain::database &d = plugin.database(); std::set sons_to_be_dereg = d.get_sons_to_be_deregistered(); - chain::son_id_type my_son_id = get_current_son_id(); + chain::son_id_type my_son_id = get_current_son_id(sidechain); if (sons_to_be_dereg.size() > 0) { // We shouldn't raise proposals for the SONs for which a de-reg @@ -660,6 +739,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { fc::future fut = fc::async([&]() { try { trx.validate(); + std::lock_guard lck(access_db_mutex); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -676,36 +756,36 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { } } -void peerplays_sidechain_plugin_impl::process_proposals() { - net_manager->process_proposals(); +void peerplays_sidechain_plugin_impl::process_proposals(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_proposals(); } -void peerplays_sidechain_plugin_impl::process_active_sons_change() { - net_manager->process_active_sons_change(); +void peerplays_sidechain_plugin_impl::process_active_sons_change(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_active_sons_change(); } -void peerplays_sidechain_plugin_impl::create_deposit_addresses() { - net_manager->create_deposit_addresses(); +void peerplays_sidechain_plugin_impl::create_deposit_addresses(sidechain_type sidechain) { + net_handlers.at(sidechain)->create_deposit_addresses(); } -void peerplays_sidechain_plugin_impl::process_deposits() { - net_manager->process_deposits(); +void peerplays_sidechain_plugin_impl::process_deposits(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_deposits(); } -void peerplays_sidechain_plugin_impl::process_withdrawals() { - net_manager->process_withdrawals(); +void peerplays_sidechain_plugin_impl::process_withdrawals(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_withdrawals(); } -void peerplays_sidechain_plugin_impl::process_sidechain_transactions() { - net_manager->process_sidechain_transactions(); +void peerplays_sidechain_plugin_impl::process_sidechain_transactions(sidechain_type sidechain) { + net_handlers.at(sidechain)->process_sidechain_transactions(); } -void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { - net_manager->send_sidechain_transactions(); +void peerplays_sidechain_plugin_impl::send_sidechain_transactions(sidechain_type sidechain) { + net_handlers.at(sidechain)->send_sidechain_transactions(); } -void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() { - net_manager->settle_sidechain_transactions(); +void peerplays_sidechain_plugin_impl::settle_sidechain_transactions(sidechain_type sidechain) { + net_handlers.at(sidechain)->settle_sidechain_transactions(); } void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { @@ -758,20 +838,20 @@ std::set &peerplays_sidechain_plugin::get_sons() { return my->get_sons(); } -const son_id_type peerplays_sidechain_plugin::get_current_son_id() { - return my->get_current_son_id(); +const son_id_type peerplays_sidechain_plugin::get_current_son_id(sidechain_type sidechain) { + return my->get_current_son_id(sidechain); } -const son_object peerplays_sidechain_plugin::get_current_son_object() { - return my->get_current_son_object(); +const son_object peerplays_sidechain_plugin::get_current_son_object(sidechain_type sidechain) { + return my->get_current_son_object(sidechain); } const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { return my->get_son_object(son_id); } -bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) { - return my->is_active_son(son_id); +bool peerplays_sidechain_plugin::is_active_son(sidechain_type sidechain, son_id_type son_id) { + return my->is_active_son(sidechain, son_id); } bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) { @@ -786,12 +866,12 @@ fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_k return my->get_private_key(public_key); } -void peerplays_sidechain_plugin::log_son_proposal_retry(int op_type, object_id_type object_id) { - my->log_son_proposal_retry(op_type, object_id); +void peerplays_sidechain_plugin::log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id) { + my->log_son_proposal_retry(sidechain, op_type, object_id); } -bool peerplays_sidechain_plugin::can_son_participate(int op_type, object_id_type object_id) { - return my->can_son_participate(op_type, object_id); +bool peerplays_sidechain_plugin::can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id) { + return my->can_son_participate(sidechain, op_type, object_id); } std::map> peerplays_sidechain_plugin::get_son_listener_log() { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 7c87a9ef..224144a0 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -182,7 +182,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ ((sed.sidechain == sidechain_type::hive) && (sed.sidechain_currency.compare("HIVE") == 0)) || enable_peerplays_asset_deposits); - bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain_type::peerplays) && + bool withdraw_condition = (sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain == sidechain) && //! Fixme -> sidechain_type::peerplays ((sed.sidechain_currency == object_id_to_string(gpo.parameters.btc_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hbd_asset())) || (sed.sidechain_currency == object_id_to_string(gpo.parameters.hive_asset()))); @@ -191,7 +191,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ if (deposit_condition) { for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { + if (plugin.is_active_son(sidechain, son_id)) { son_wallet_deposit_create_operation op; op.payer = plugin.get_son_object(son_id).son_account; @@ -253,7 +253,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } for (son_id_type son_id : plugin.get_sons()) { - if (plugin.is_active_son(son_id)) { + if (plugin.is_active_son(sidechain, son_id)) { son_wallet_withdraw_create_operation op; op.payer = plugin.get_son_object(son_id).son_account; @@ -297,7 +297,7 @@ void sidechain_net_handler::process_proposals() { const auto po = idx.find(proposal_id); if (po != idx.end()) { - if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) { + if (po->available_active_approvals.find(plugin.get_current_son_object(sidechain).son_account) != po->available_active_approvals.end()) { continue; } @@ -380,12 +380,12 @@ void sidechain_net_handler::process_proposals() { elog("=================================================="); } - if (should_process && (op_idx_0 == chain::operation::tag::value || plugin.can_son_participate(op_idx_0, object_id))) { + if (should_process && (op_idx_0 == chain::operation::tag::value || plugin.can_son_participate(sidechain, op_idx_0, object_id))) { bool should_approve = process_proposal(*po); if (should_approve) { - if (approve_proposal(po->id, plugin.get_current_son_id())) { + if (approve_proposal(po->id, plugin.get_current_son_id(sidechain))) { if (op_idx_0 != chain::operation::tag::value) { - plugin.log_son_proposal_retry(op_idx_0, object_id); + plugin.log_son_proposal_retry(sidechain, op_idx_0, object_id); } } } @@ -399,14 +399,14 @@ void sidechain_net_handler::process_active_sons_change() { } void sidechain_net_handler::create_deposit_addresses() { - if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) { return; } process_sidechain_addresses(); } void sidechain_net_handler::process_deposits() { - if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) { return; } @@ -414,7 +414,7 @@ void sidechain_net_handler::process_deposits() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { - if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag::value, swdo.id)) { + if (swdo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag::value, swdo.id)) { return; } //Ignore the deposits which are not valid anymore, considered refunds. @@ -436,12 +436,12 @@ void sidechain_net_handler::process_deposits() { wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); return; } - plugin.log_son_proposal_retry(chain::operation::tag::value, swdo.id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, swdo.id); }); } void sidechain_net_handler::process_withdrawals() { - if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) { + if (database.get_global_properties().active_sons.at(sidechain).size() < database.get_chain_properties().immutable_parameters.min_son_count) { return; } @@ -449,7 +449,7 @@ void sidechain_net_handler::process_withdrawals() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { - if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(chain::operation::tag::value, swwo.id)) { + if (swwo.id == object_id_type(0, 0, 0) || !plugin.can_son_participate(sidechain, chain::operation::tag::value, swwo.id)) { return; } @@ -461,7 +461,7 @@ void sidechain_net_handler::process_withdrawals() { wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); return; } - plugin.log_son_proposal_retry(chain::operation::tag::value, swwo.id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, swwo.id); }); } @@ -470,7 +470,7 @@ void sidechain_net_handler::process_sidechain_transactions() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid)); std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { - if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id())) { + if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id(sidechain))) { return; } @@ -485,13 +485,13 @@ void sidechain_net_handler::process_sidechain_transactions() { const chain::global_property_object &gpo = database.get_global_properties(); sidechain_transaction_sign_operation sts_op; - sts_op.signer = plugin.get_current_son_id(); + sts_op.signer = plugin.get_current_son_id(sidechain); sts_op.payer = gpo.parameters.son_account(); sts_op.sidechain_transaction_id = sto.id; sts_op.signature = processed_sidechain_tx; proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); proposal_op.proposed_ops.emplace_back(sts_op); @@ -500,7 +500,7 @@ void sidechain_net_handler::process_sidechain_transactions() { return; } - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -531,11 +531,11 @@ void sidechain_net_handler::send_sidechain_transactions() { } sidechain_transaction_send_operation sts_op; - sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.payer = plugin.get_current_son_object(sidechain).son_account; sts_op.sidechain_transaction_id = sto.id; sts_op.sidechain_transaction = sidechain_transaction; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), sts_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -560,7 +560,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { return; } - if (!plugin.can_son_participate(chain::operation::tag::value, sto.object_id)) { + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, sto.object_id)) { return; } @@ -577,7 +577,7 @@ void sidechain_net_handler::settle_sidechain_transactions() { const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -605,13 +605,13 @@ void sidechain_net_handler::settle_sidechain_transactions() { } } - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - plugin.log_son_proposal_retry(chain::operation::tag::value, sto.object_id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, sto.object_id); } catch (fc::exception &e) { elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); } @@ -673,7 +673,7 @@ void sidechain_net_handler::on_applied_block(const signed_block &b) { sidechain_event_data sed; sed.timestamp = database.head_block_time(); sed.block_num = database.head_block_num(); - sed.sidechain = sidechain_type::peerplays; + sed.sidechain = sidechain; //! Fixme -> sidechain_type::peerplays sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_from = sidechain_from; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index 82511bed..b48ccbb6 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -417,7 +417,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) { - // ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -451,8 +451,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) const auto swo = idx.find(swo_id); if (swo != idx.end()) { - auto active_sons = gpo.active_sons; - vector wallet_sons = swo->sons; + const auto &active_sons = gpo.active_sons.at(sidechain); + vector wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -463,10 +463,10 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) } if (son_sets_equal) { - auto active_sons = gpo.active_sons; + const auto &active_sons = gpo.active_sons.at(sidechain); vector son_pubkeys_bitcoin; for (const son_info &si : active_sons) { - son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + son_pubkeys_bitcoin.push_back(si.public_key); } string reply_str = create_primary_wallet_address(active_sons); @@ -676,7 +676,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) read_transaction_data(sto->transaction, tx_hex, in_amounts, redeem_script); bitcoin_transaction tx = unpack(parse_hex(tx_hex)); - bitcoin::bytes pubkey = parse_hex(son->sidechain_public_keys.at(sidechain_type::bitcoin)); + bitcoin::bytes pubkey = parse_hex(son->sidechain_public_keys.at(sidechain)); vector sigs = read_byte_arrays_from_string(signature); for (size_t i = 0; i < tx.vin.size(); i++) { const auto &sighash_str = get_signature_hash(tx, parse_hex(redeem_script), static_cast(in_amounts[i]), i, 1, true).str(); @@ -706,8 +706,8 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { const auto &active_sw = swi.rbegin(); if (active_sw != swi.rend()) { - if ((active_sw->addresses.find(sidechain_type::bitcoin) == active_sw->addresses.end()) || - (active_sw->addresses.at(sidechain_type::bitcoin).empty())) { + if ((active_sw->addresses.find(sidechain) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain).empty())) { if (proposal_exists(chain::operation::tag::value, active_sw->id)) { return; @@ -715,19 +715,19 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { const chain::global_property_object &gpo = database.get_global_properties(); - auto active_sons = gpo.active_sons; + const auto &active_sons = gpo.active_sons.at(sidechain); string reply_str = create_primary_wallet_address(active_sons); std::stringstream active_pw_ss(reply_str); boost::property_tree::ptree active_pw_pt; boost::property_tree::read_json(active_pw_ss, active_pw_pt); if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { - if (!plugin.can_son_participate(chain::operation::tag::value, active_sw->id)) { + if (!plugin.can_son_participate(sidechain, chain::operation::tag::value, active_sw->id)) { return; } proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -737,7 +737,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { son_wallet_update_operation swu_op; swu_op.payer = gpo.parameters.son_account(); swu_op.son_wallet_id = active_sw->id; - swu_op.sidechain = sidechain_type::bitcoin; + swu_op.sidechain = sidechain; swu_op.address = res.str(); proposal_op.proposed_ops.emplace_back(swu_op); @@ -752,18 +752,18 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { stc_op.object_id = prev_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = prev_sw->sons; + stc_op.signers = prev_sw->sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); } } - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - plugin.log_son_proposal_retry(chain::operation::tag::value, active_sw->id); + plugin.log_son_proposal_retry(sidechain, chain::operation::tag::value, active_sw->id); } catch (fc::exception &e) { elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; @@ -778,9 +778,8 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { const chain::global_property_object &gpo = database.get_global_properties(); std::vector> pubkeys; - for (auto &son : gpo.active_sons) { - std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + for (auto &son : gpo.active_sons.at(sidechain)) { + auto pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(son.public_key))); pubkeys.push_back(std::make_pair(pubkey, son.weight)); } @@ -800,7 +799,7 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { if (addr.get_address() != sao.deposit_address) { sidechain_address_update_operation op; - op.payer = plugin.get_current_son_object().son_account; + op.payer = plugin.get_current_son_object(sidechain).son_account; op.sidechain_address_id = sao.id; op.sidechain_address_account = sao.sidechain_address_account; op.sidechain = sao.sidechain; @@ -810,7 +809,7 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { op.withdraw_public_key = sao.withdraw_public_key; op.withdraw_address = sao.withdraw_address; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -842,7 +841,7 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -856,10 +855,10 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj stc_op.object_id = swdo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -886,7 +885,7 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw const chain::global_property_object &gpo = database.get_global_properties(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -900,10 +899,10 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -951,8 +950,7 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain using namespace bitcoin; std::vector> pubkey_weights; for (auto si : sto.signers) { - std::string pub_key_str = si.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); } btc_weighted_multisig_address addr(pubkey_weights, network_type); @@ -1001,8 +999,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s std::vector> pubkey_weights; for (auto &son : son_pubkeys) { - std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(son.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } @@ -1019,7 +1016,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address) { - const auto &address_data = prev_swo.addresses.find(sidechain_type::bitcoin); + const auto &address_data = prev_swo.addresses.find(sidechain); if (address_data == prev_swo.addresses.end()) { return ""; } @@ -1070,12 +1067,12 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_wallet_deposit_object &swdo) { const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + if (obj == idx.rend() || obj->addresses.find(sidechain) == obj->addresses.end()) { return ""; } // Get redeem script for deposit address std::string redeem_script = get_redeemscript_for_userdeposit(swdo.sidechain_from); - std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + std::string pw_address_json = obj->addresses.find(sidechain)->second; std::stringstream ss(pw_address_json); boost::property_tree::ptree json; @@ -1117,11 +1114,11 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) { const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + if (obj == idx.rend() || obj->addresses.find(sidechain) == obj->addresses.end()) { return ""; } - std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; + std::string pw_address_json = obj->addresses.find(sidechain)->second; std::stringstream ss(pw_address_json); boost::property_tree::ptree json; @@ -1186,7 +1183,7 @@ std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector< std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) { using namespace bitcoin; - std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain); + std::string pubkey = plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain); std::string prvkey = get_private_key(pubkey); std::vector in_amounts; std::string tx_hex; @@ -1302,14 +1299,13 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons const auto &idx = database.get_index_type().indices().get(); auto obj = idx.rbegin(); - if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) { + if (obj == idx.rend() || obj->addresses.find(sidechain) == obj->addresses.end()) { return ""; } std::vector> pubkey_weights; - for (auto &son : obj->sons) { - std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin); - auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str))); + for (auto &son : obj->sons.at(sidechain)) { + auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(son.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp new file mode 100644 index 00000000..1c6632d8 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_factory.cpp @@ -0,0 +1,31 @@ +#include + +#include +#include +#include + +namespace graphene { namespace peerplays_sidechain { + +sidechain_net_handler_factory::sidechain_net_handler_factory(peerplays_sidechain_plugin &_plugin) : + plugin(_plugin) { +} + +std::unique_ptr sidechain_net_handler_factory::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) const { + switch (sidechain) { + case sidechain_type::bitcoin: { + return std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); + } + case sidechain_type::hive: { + return std::unique_ptr(new sidechain_net_handler_hive(plugin, options)); + } + case sidechain_type::peerplays: { + return std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); + } + default: + assert(false); + } + + return nullptr; +} + +}} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index d63e6743..c62911ec 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -180,7 +180,7 @@ sidechain_net_handler_hive::~sidechain_net_handler_hive() { } bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { - //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + //ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -213,8 +213,8 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { const auto swo = idx.find(swo_id); if (swo != idx.end()) { - auto active_sons = gpo.active_sons; - vector wallet_sons = swo->sons; + auto active_sons = gpo.active_sons.at(sidechain); + vector wallet_sons = swo->sons.at(sidechain); bool son_sets_equal = (active_sons.size() == wallet_sons.size()); @@ -251,7 +251,7 @@ bool sidechain_net_handler_hive::process_proposal(const proposal_object &po) { uint32_t total_weight = 0; for (const auto &wallet_son : wallet_sons) { total_weight = total_weight + wallet_son.weight; - account_auths[wallet_son.sidechain_public_keys.at(sidechain)] = wallet_son.weight; + account_auths[wallet_son.public_key] = wallet_son.weight; } std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); @@ -487,12 +487,12 @@ void sidechain_net_handler_hive::process_primary_wallet() { const chain::global_property_object &gpo = database.get_global_properties(); - auto active_sons = gpo.active_sons; + const auto &active_sons = gpo.active_sons.at(sidechain); fc::flat_map account_auths; uint32_t total_weight = 0; for (const auto &active_son : active_sons) { total_weight = total_weight + active_son.weight; - account_auths[active_son.sidechain_public_keys.at(sidechain)] = active_son.weight; + account_auths[active_son.public_key] = active_son.weight; } std::string memo_key = node_rpc_client->get_account_memo_key("son-account"); @@ -530,7 +530,7 @@ void sidechain_net_handler_hive::process_primary_wallet() { } proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -547,11 +547,11 @@ void sidechain_net_handler_hive::process_primary_wallet() { stc_op.object_id = active_sw->id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -575,7 +575,7 @@ void sidechain_net_handler_hive::process_sidechain_addresses() { if (sao.expires == time_point_sec::maximum()) { if (sao.deposit_address == "") { sidechain_address_update_operation op; - op.payer = plugin.get_current_son_object().son_account; + op.payer = plugin.get_current_son_object(sidechain).son_account; op.sidechain_address_id = sao.id; op.sidechain_address_account = sao.sidechain_address_account; op.sidechain = sao.sidechain; @@ -585,7 +585,7 @@ void sidechain_net_handler_hive::process_sidechain_addresses() { op.withdraw_public_key = sao.withdraw_public_key; op.withdraw_address = sao.withdraw_address; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -617,7 +617,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object } proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -633,7 +633,7 @@ bool sidechain_net_handler_hive::process_deposit(const son_wallet_deposit_object ai_op.issue_to_account = swdo.peerplays_from; proposal_op.proposed_ops.emplace_back(ai_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -690,7 +690,7 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob //===== proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); @@ -704,10 +704,10 @@ bool sidechain_net_handler_hive::process_withdrawal(const son_wallet_withdraw_ob stc_op.object_id = swwo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_op.proposed_ops.emplace_back(stc_op); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -730,7 +730,7 @@ std::string sidechain_net_handler_hive::process_sidechain_transaction(const side std::string chain_id_str = node_rpc_client->get_chain_id(); const hive::chain_id_type chain_id(chain_id_str); - fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); + fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); signature_type st = htrx.sign(*privkey, chain_id); std::stringstream ss_st; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index d5a9ec32..b2945251 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -53,7 +53,7 @@ sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po) { - ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id())); + ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain))); bool should_approve = false; @@ -140,7 +140,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() { if (sao.expires == time_point_sec::maximum()) { if (sao.deposit_address == "") { sidechain_address_update_operation op; - op.payer = plugin.get_current_son_object().son_account; + op.payer = plugin.get_current_son_object(sidechain).son_account; op.sidechain_address_id = sao.id; op.sidechain_address_account = sao.sidechain_address_account; op.sidechain = sao.sidechain; @@ -150,7 +150,7 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() { op.withdraw_public_key = sao.withdraw_public_key; op.withdraw_address = sao.withdraw_address; - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -197,15 +197,15 @@ bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_o stc_op.object_id = swdo.id; stc_op.sidechain = sidechain; stc_op.transaction = tx_str; - stc_op.signers = gpo.active_sons; + stc_op.signers = gpo.active_sons.at(sidechain); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.fee_paying_account = plugin.get_current_son_object(sidechain).son_account; proposal_op.proposed_ops.emplace_back(stc_op); uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime); - signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id(sidechain)), proposal_op); try { trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); @@ -230,7 +230,7 @@ std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const signed_transaction trx; fc::raw::unpack(ss_trx, trx, 1000); - fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); + fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object(sidechain).sidechain_public_keys.at(sidechain))); signature_type st = trx.sign(*privkey, database.get_chain_id()); std::stringstream ss_st; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp deleted file mode 100644 index e2cb1608..00000000 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -namespace graphene { namespace peerplays_sidechain { - -sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) : - plugin(_plugin), - database(_plugin.database()) { - - //database.applied_block.connect([&](const signed_block &b) { - // on_applied_block(b); - //}); -} - -sidechain_net_manager::~sidechain_net_manager() { -} - -bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) { - - bool ret_val = false; - - switch (sidechain) { - case sidechain_type::bitcoin: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - case sidechain_type::hive: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_hive(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - case sidechain_type::peerplays: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_peerplays(plugin, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; - } - default: - assert(false); - } - - return ret_val; -} - -void sidechain_net_manager::process_proposals() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_proposals(); - } -} - -void sidechain_net_manager::process_active_sons_change() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_active_sons_change(); - } -} - -void sidechain_net_manager::create_deposit_addresses() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->create_deposit_addresses(); - } -} - -void sidechain_net_manager::process_deposits() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_deposits(); - } -} - -void sidechain_net_manager::process_withdrawals() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_withdrawals(); - } -} - -void sidechain_net_manager::process_sidechain_transactions() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->process_sidechain_transactions(); - } -} - -void sidechain_net_manager::send_sidechain_transactions() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->send_sidechain_transactions(); - } -} - -void sidechain_net_manager::settle_sidechain_transactions() { - for (size_t i = 0; i < net_handlers.size(); i++) { - net_handlers.at(i)->settle_sidechain_transactions(); - } -} - -std::map> sidechain_net_manager::get_son_listener_log() { - std::map> result; - for (size_t i = 0; i < net_handlers.size(); i++) { - result[net_handlers.at(i)->get_sidechain()] = net_handlers.at(i)->get_son_listener_log(); - } - return result; -} - -void sidechain_net_manager::on_applied_block(const signed_block &b) { -} - -}} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 185db897..0086c654 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1610,6 +1610,7 @@ class wallet_api * * @param voting_account the name or id of the account who is voting with their shares * @param son the name or id of the SONs' owner account + * @param sidechain the name of the sidechain * @param approve true if you wish to vote in favor of that SON, false to * remove your vote in favor of that SON * @param broadcast true if you wish to broadcast the transaction @@ -1617,6 +1618,7 @@ class wallet_api */ signed_transaction vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast = false); @@ -1640,6 +1642,8 @@ class wallet_api * @param sons_to_reject the names or ids of the SONs owner accounts you wish * to reject (these will be removed from the list of SONs * you currently approve). This list can be empty. + * @param sidechain the name of the sidechain + * * @param desired_number_of_sons the number of SONs you believe the network * should have. You must vote for at least this many * SONs. You can set this to 0 to abstain from @@ -1650,6 +1654,7 @@ class wallet_api signed_transaction update_son_votes(string voting_account, std::vector sons_to_approve, std::vector sons_to_reject, + sidechain_type sidechain, uint16_t desired_number_of_sons, bool broadcast = false); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 09aa5bdc..43ca49fd 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2230,69 +2230,99 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (owner_account) ) } + //! Fixme - do we need to specify sidechain_type as params here? map list_active_sons() - { try { - global_property_object gpo = get_global_properties(); - vector son_ids; - son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(son_ids, son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - std::vector> son_objects = _remote_db->get_sons(son_ids); - vector owners; - for(auto obj: son_objects) + { + try { - std::string acc_id = account_id_to_string(obj->son_account); - owners.push_back(acc_id); - } - vector< optional< account_object> > accs = _remote_db->get_accounts(owners); - std::remove_if(son_objects.begin(), son_objects.end(), - [](const fc::optional& obj) -> bool { return obj.valid(); }); - map result; - std::transform(accs.begin(), accs.end(), son_objects.begin(), - std::inserter(result, result.end()), - [](fc::optional& acct, fc::optional son) { - FC_ASSERT(acct, "Invalid active SONs list in global properties."); - return std::make_pair(string(acct->name), std::move(son->id)); - }); - return result; - } FC_CAPTURE_AND_RETHROW() } - - map get_son_network_status() - { try { - global_property_object gpo = get_global_properties(); - vector son_ids; - son_ids.reserve(gpo.active_sons.size()); - std::transform(gpo.active_sons.begin(), gpo.active_sons.end(), - std::inserter(son_ids, son_ids.end()), - [](const son_info& swi) { - return swi.son_id; - }); - - map result; - std::vector> son_objects = _remote_db->get_sons(son_ids); - for(auto son_obj: son_objects) { - string status; - if (son_obj) { - son_statistics_object sso = get_object(son_obj->statistics); - if (sso.last_active_timestamp + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { - status = "OK, regular SON heartbeat"; - } else { - if (sso.last_active_timestamp + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { - status = "OK, irregular SON heartbeat, but not triggering SON down proposal"; - } else { - status = "NOT OK, irregular SON heartbeat, triggering SON down proposal"; - } - } - } else { - status = "NOT OK, invalid SON id"; + const global_property_object& gpo = get_global_properties(); + set son_ids_set; + for(const auto& active_sidechain_type : active_sidechain_types) + { + std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), + std::inserter(son_ids_set, son_ids_set.end()), + [](const son_info &swi) { + return swi.son_id; + }); } - result[son_obj->id] = status; + vector son_ids; + son_ids.reserve(son_ids_set.size()); + for(const auto& son_id : son_ids_set) + { + son_ids.emplace_back(son_id); + } + + std::vector> son_objects = _remote_db->get_sons(son_ids); + vector owners; + for(auto obj: son_objects) + { + std::string acc_id = account_id_to_string(obj->son_account); + owners.push_back(acc_id); + } + vector< optional< account_object> > accs = _remote_db->get_accounts(owners); + std::remove_if(son_objects.begin(), son_objects.end(), + [](const fc::optional& obj) -> bool { return obj.valid(); }); + map result; + std::transform(accs.begin(), accs.end(), son_objects.begin(), + std::inserter(result, result.end()), + [](fc::optional& acct, fc::optional son) { + FC_ASSERT(acct, "Invalid active SONs list in global properties."); + return std::make_pair(string(acct->name), std::move(son->id)); + }); + return result; } - return result; - } FC_CAPTURE_AND_RETHROW() } + FC_CAPTURE_AND_RETHROW() + } + + //! Fixme - do we need to specify sidechain_type as params here? + map get_son_network_status() + { + try + { + const global_property_object& gpo = get_global_properties(); + + set son_ids_set; + for(const auto& active_sidechain_type : active_sidechain_types) { + std::transform(gpo.active_sons.at(active_sidechain_type).cbegin(), gpo.active_sons.at(active_sidechain_type).cend(), + std::inserter(son_ids_set, son_ids_set.end()), + [](const son_info &swi) { + return swi.son_id; + }); + } + vector son_ids; + son_ids.reserve(son_ids_set.size()); + std::transform(son_ids_set.cbegin(), son_ids_set.cend(), + std::inserter(son_ids, son_ids.end()), + [](const son_id_type& sit) { + return sit; + }); + + map result; + std::vector> son_objects = _remote_db->get_sons(son_ids); + for(auto son_obj: son_objects) { + string status; + if (son_obj) { + son_statistics_object sso = get_object(son_obj->statistics); + for(const auto& active_sidechain_type : active_sidechain_types) { + if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_heartbeat_frequency()) > time_point::now()) { + status = "[OK, regular SON heartbeat for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; + } else { + if (sso.last_active_timestamp.at(active_sidechain_type) + fc::seconds(gpo.parameters.son_down_time()) > time_point::now()) { + status = "[OK, irregular SON heartbeat, but not triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; + } else { + status = "[NOT OK, irregular SON heartbeat, triggering SON down proposal for sidechain " + std::to_string(static_cast(active_sidechain_type)) + "] "; + } + } + } + } else { + status = "NOT OK, invalid SON id"; + } + result[son_obj->id] = status; + } + return result; + } + FC_CAPTURE_AND_RETHROW() + } optional get_active_son_wallet() { try { @@ -2800,6 +2830,7 @@ public: signed_transaction vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast /* = false */) { try { @@ -2813,19 +2844,20 @@ public: account_object voting_account_object = get_account(voting_account); account_id_type son_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_account_id); - if (!son_obj) - FC_THROW("Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + if (approve) { - auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) - FC_THROW("Account ${account} was already voting for son ${son}", ("account", voting_account)("son", son)); + FC_THROW("Account ${account} has already voted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } else { - unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) - FC_THROW("Account ${account} is already not voting for son ${son}", ("account", voting_account)("son", son)); + FC_THROW("Account ${account} has already unvoted for son ${son} for sidechain ${sidechain}", ("account", voting_account)("son", son)("sidechain", sidechain)); } account_update_operation account_update_op; account_update_op.account = voting_account_object.id; @@ -2842,6 +2874,7 @@ public: signed_transaction update_son_votes(string voting_account, std::vector sons_to_approve, std::vector sons_to_reject, + sidechain_type sidechain, uint16_t desired_number_of_sons, bool broadcast /* = false */) { try { @@ -2857,9 +2890,10 @@ public: { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); - if (!son_obj) - FC_THROW("Account ${son} is not registered as a SON", ("son", son)); - auto insert_result = voting_account_object.options.votes.insert(son_obj->vote_id); + FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + + auto insert_result = voting_account_object.options.votes.insert(son_obj->get_sidechain_vote_id(sidechain)); if (!insert_result.second) FC_THROW("Account ${account} was already voting for SON ${son}", ("account", voting_account)("son", son)); } @@ -2867,13 +2901,15 @@ public: { account_id_type son_owner_account_id = get_account_id(son); fc::optional son_obj = _remote_db->get_son_by_account_id(son_owner_account_id); - if (!son_obj) - FC_THROW("Account ${son} is not registered as a SON", ("son", son)); - unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->vote_id); + FC_ASSERT(son_obj, "Account ${son} is not registered as a son", ("son", son)); + FC_ASSERT(sidechain == sidechain_type::bitcoin || sidechain == sidechain_type::hive, "Unexpected sidechain type"); + + unsigned votes_removed = voting_account_object.options.votes.erase(son_obj->get_sidechain_vote_id(sidechain)); if (!votes_removed) FC_THROW("Account ${account} is already not voting for SON ${son}", ("account", voting_account)("son", son)); } - voting_account_object.options.extensions.value.num_son = desired_number_of_sons; + FC_ASSERT( voting_account_object.options.extensions.value.num_son.valid() , "Invalid son number" ); + (*voting_account_object.options.extensions.value.num_son)[sidechain] = desired_number_of_sons; account_update_operation account_update_op; account_update_op.account = voting_account_object.id; @@ -5428,19 +5464,21 @@ signed_transaction wallet_api::vote_for_committee_member(string voting_account, signed_transaction wallet_api::vote_for_son(string voting_account, string son, + sidechain_type sidechain, bool approve, bool broadcast /* = false */) { - return my->vote_for_son(voting_account, son, approve, broadcast); + return my->vote_for_son(voting_account, son, sidechain, approve, broadcast); } signed_transaction wallet_api::update_son_votes(string voting_account, std::vector sons_to_approve, std::vector sons_to_reject, + sidechain_type sidechain, uint16_t desired_number_of_sons, bool broadcast /* = false */) { - return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, desired_number_of_sons, broadcast); + return my->update_son_votes(voting_account, sons_to_approve, sons_to_reject, sidechain, desired_number_of_sons, broadcast); } signed_transaction wallet_api::sidechain_deposit_transaction( const string &son_name_or_id, diff --git a/tests/cli/son.cpp b/tests/cli/son.cpp index 38a3799b..87257735 100644 --- a/tests/cli/son.cpp +++ b/tests/cli/son.cpp @@ -138,10 +138,18 @@ BOOST_AUTO_TEST_CASE( create_sons ) auto son1_obj = con.wallet_api_ptr->get_son("son1account"); BOOST_CHECK(son1_obj.son_account == con.wallet_api_ptr->get_account_id("son1account")); BOOST_CHECK_EQUAL(son1_obj.url, "http://son1"); + BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); + BOOST_CHECK_EQUAL(son1_obj.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); + BOOST_CHECK_EQUAL(son1_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); auto son2_obj = con.wallet_api_ptr->get_son("son2account"); BOOST_CHECK(son2_obj.son_account == con.wallet_api_ptr->get_account_id("son2account")); BOOST_CHECK_EQUAL(son2_obj.url, "http://son2"); + BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); + BOOST_CHECK_EQUAL(son2_obj.sidechain_public_keys[sidechain_type::hive], "hive account 2"); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 24); + BOOST_CHECK_EQUAL(son2_obj.get_sidechain_vote_id(sidechain_type::hive).instance(), 25); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -172,6 +180,10 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) auto son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember"); BOOST_CHECK(son_data.son_account == sonmember_acct.get_id()); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 1"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 1"); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::bitcoin).instance(), 22); + BOOST_CHECK_EQUAL(son_data.get_sidechain_vote_id(sidechain_type::hive).instance(), 23); // update SON sidechain_public_keys.clear(); @@ -181,6 +193,8 @@ BOOST_AUTO_TEST_CASE( cli_update_son ) con.wallet_api_ptr->update_son("sonmember", "http://sonmember_updated", "", sidechain_public_keys, true); son_data = con.wallet_api_ptr->get_son("sonmember"); BOOST_CHECK(son_data.url == "http://sonmember_updated"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::bitcoin], "bitcoin_address 2"); + BOOST_CHECK_EQUAL(son_data.sidechain_public_keys[sidechain_type::hive], "hive account 2"); // update SON signing key sidechain_public_keys.clear(); @@ -221,8 +235,11 @@ BOOST_AUTO_TEST_CASE( son_voting ) son_object son2_obj; signed_transaction vote_son1_tx; signed_transaction vote_son2_tx; - uint64_t son1_start_votes, son1_end_votes; - uint64_t son2_start_votes, son2_end_votes; + flat_map son1_start_votes, son1_end_votes; + flat_map son2_start_votes, son2_end_votes; + + //! Get nathan account + const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_start_votes = son1_obj.total_votes; @@ -232,85 +249,101 @@ BOOST_AUTO_TEST_CASE( son_voting ) con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); // Vote for a son1account BOOST_TEST_MESSAGE("Voting for son1account"); - vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", true, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, true, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes > son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] > son1_start_votes[sidechain_type::hive]); // Vote for a son2account BOOST_TEST_MESSAGE("Voting for son2account"); - vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", true, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, true, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, true, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is there son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes > son2_start_votes); - - //! Get nathan account - const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] > son2_start_votes[sidechain_type::hive]); //! Check son1account voters - auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account"); - BOOST_REQUIRE(voters_for_son1account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account.voters_for_son->voters[0].instance, nathan_account_object.id.instance()); + auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); //! Check son2account voters - auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account"); - BOOST_REQUIRE(voters_for_son2account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 1); - BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account.voters_for_son->voters[0].instance, nathan_account_object.id.instance()); + auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); //! Check votes of nathan - auto nathan_votes = con.wallet_api_ptr->get_votes("nathan"); - BOOST_REQUIRE(nathan_votes.votes_for_sons); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 2); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son1_obj.id.instance()); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(1).id.instance(), son2_obj.id.instance()); + auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son1account BOOST_TEST_MESSAGE("Withdraw vote for a son1account"); - vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", false, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::bitcoin, false, true); + vote_son1_tx = con.wallet_api_ptr->vote_for_son("nathan", "son1account", sidechain_type::hive, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son1_end_votes[sidechain_type::hive] == son1_start_votes[sidechain_type::hive]); //! Check son1account voters - voters_for_son1account = con.wallet_api_ptr->get_voters("son1account"); - BOOST_REQUIRE(voters_for_son1account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son1account.voters_for_son->voters.size(), 0); + voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); //! Check votes of nathan - nathan_votes = con.wallet_api_ptr->get_votes("nathan"); - BOOST_REQUIRE(nathan_votes.votes_for_sons); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->size(), 1); - BOOST_CHECK_EQUAL(nathan_votes.votes_for_sons->at(0).id.instance(), son2_obj.id.instance()); + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); // Withdraw vote for a son2account BOOST_TEST_MESSAGE("Withdraw vote for a son2account"); - vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", false, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::bitcoin, false, true); + vote_son2_tx = con.wallet_api_ptr->vote_for_son("nathan", "son2account", sidechain_type::hive, false, true); BOOST_CHECK(generate_maintenance_block()); // Verify that the vote is removed son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); + BOOST_CHECK(son2_end_votes[sidechain_type::hive] == son2_start_votes[sidechain_type::hive]); //! Check son2account voters - voters_for_son2account = con.wallet_api_ptr->get_voters("son2account"); - BOOST_REQUIRE(voters_for_son2account.voters_for_son); - BOOST_CHECK_EQUAL(voters_for_son2account.voters_for_son->voters.size(), 0); + voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); //! Check votes of nathan - nathan_votes = con.wallet_api_ptr->get_votes("nathan"); - BOOST_CHECK(!nathan_votes.votes_for_sons.valid()); + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_CHECK(!nathan_votes_for_son); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -363,7 +396,8 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); std::string name = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -371,37 +405,46 @@ BOOST_FIXTURE_TEST_CASE( select_top_fifteen_sons, cli_fixture ) { std::string name1 = "sonaccount" + fc::to_pretty_string(i); std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); for(unsigned int i = 0; i < son_number - 1; i++) { std::string name1 = "sonaccount" + fc::to_pretty_string(i + 2); std::string name2 = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); for(unsigned int i = 0; i < son_number - 2; i++) { std::string name1 = "sonaccount" + fc::to_pretty_string(i + 3); std::string name2 = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); BOOST_CHECK(generate_maintenance_block()); - BOOST_CHECK(gpo.active_sons.size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -466,8 +509,11 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) son_object son1_obj; son_object son2_obj; - uint64_t son1_start_votes, son1_end_votes; - uint64_t son2_start_votes, son2_end_votes; + flat_map son1_start_votes, son1_end_votes; + flat_map son2_start_votes, son2_end_votes; + + //! Get nathan account + const auto nathan_account_object = con.wallet_api_ptr->get_account("nathan"); // Get votes at start son1_obj = con.wallet_api_ptr->get_son("son1account"); @@ -485,114 +531,189 @@ BOOST_AUTO_TEST_CASE( update_son_votes_test ) accepted.push_back("son1account"); accepted.push_back("son2account"); con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 2, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 2, true); generate_block(); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes > son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] > son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes > son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check son1account voters + auto voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son1account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + + //! Check son2account voters + auto voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::bitcoin).voters[0].instance, nathan_account_object.id.instance()); + BOOST_REQUIRE_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 1); + BOOST_CHECK_EQUAL((uint32_t)voters_for_son2account->at(sidechain_type::hive).voters[0].instance, nathan_account_object.id.instance()); + + //! Check votes of nathan + auto nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(1).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 2); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son1_obj.id.instance()); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(1).id.instance(), son2_obj.id.instance()); // Withdraw vote for SON 1 accepted.clear(); rejected.clear(); rejected.push_back("son1account"); con.wallet_api_ptr->create_vesting_balance("nathan", "1000", "1.3.0", vesting_balance_type::gpos, true); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes < son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] < son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); // voice distribution changed, SON2 now has all voices son2_end_votes = son2_obj.total_votes; - BOOST_CHECK((son2_end_votes > son2_start_votes)); // nathan spent funds for vb, it has different voting power + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] > son2_start_votes[sidechain_type::bitcoin]); // nathan spent funds for vb, it has different voting power son2_start_votes = son2_end_votes; + //! Check son1account voters + voters_for_son1account = con.wallet_api_ptr->get_voters("son1account").voters_for_son; + BOOST_REQUIRE(voters_for_son1account); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son1account->at(sidechain_type::hive).voters.size(), 0); + + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + // Try to reject incorrect SON accepted.clear(); rejected.clear(); rejected.push_back("son1accnt"); - BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_CHECK_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); generate_block(); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(nathan_votes_for_son); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::bitcoin).at(0).id.instance(), son2_obj.id.instance()); + BOOST_REQUIRE_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).size(), 1); + BOOST_CHECK_EQUAL(nathan_votes_for_son->at(sidechain_type::hive).at(0).id.instance(), son2_obj.id.instance()); + // Reject SON2 accepted.clear(); rejected.clear(); rejected.push_back("son2account"); - update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 0, true); + update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 0, true); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes < son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] < son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check son2account voters + voters_for_son2account = con.wallet_api_ptr->get_voters("son2account").voters_for_son; + BOOST_REQUIRE(voters_for_son2account); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::bitcoin).voters.size(), 0); + BOOST_CHECK_EQUAL(voters_for_son2account->at(sidechain_type::hive).voters.size(), 0); + + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + // Try to accept and reject the same SON accepted.clear(); rejected.clear(); rejected.push_back("son1accnt"); accepted.push_back("son1accnt"); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::hive, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + // Try to accept and reject empty lists accepted.clear(); rejected.clear(); - BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, - rejected, 1, true), fc::exception); + BOOST_REQUIRE_THROW(update_votes_tx = con.wallet_api_ptr->update_son_votes("nathan", accepted, rejected, + sidechain_type::bitcoin, 1, true), fc::exception); BOOST_CHECK(generate_maintenance_block()); // Verify the votes son1_obj = con.wallet_api_ptr->get_son("son1account"); son1_end_votes = son1_obj.total_votes; - BOOST_CHECK(son1_end_votes == son1_start_votes); + BOOST_CHECK(son1_end_votes[sidechain_type::bitcoin] == son1_start_votes[sidechain_type::bitcoin]); son1_start_votes = son1_end_votes; son2_obj = con.wallet_api_ptr->get_son("son2account"); son2_end_votes = son2_obj.total_votes; - BOOST_CHECK(son2_end_votes == son2_start_votes); + BOOST_CHECK(son2_end_votes[sidechain_type::bitcoin] == son2_start_votes[sidechain_type::bitcoin]); son2_start_votes = son2_end_votes; + //! Check votes of nathan + nathan_votes_for_son = con.wallet_api_ptr->get_votes("nathan").votes_for_sons; + BOOST_REQUIRE(!nathan_votes_for_son); + } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); edump((e.to_detail_string())); @@ -607,7 +728,8 @@ BOOST_AUTO_TEST_CASE( related_functions ) try { global_property_object gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_CHECK(gpo.active_sons.size() == 0); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 0); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 0); flat_map sidechain_public_keys; @@ -624,7 +746,8 @@ BOOST_AUTO_TEST_CASE( related_functions ) sth.create_son("son2account", "http://son2", sidechain_public_keys); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_CHECK(gpo.active_sons.size() == 2); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == 2); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == 2); } catch( fc::exception& e ) { BOOST_TEST_MESSAGE("SON cli wallet tests exception"); @@ -670,7 +793,8 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) for(unsigned int i = 1; i < son_number + 1; i++) { std::string name = "sonaccount" + fc::to_pretty_string(i); - vote_tx = con.wallet_api_ptr->vote_for_son(name, name, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name, name, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); @@ -678,13 +802,16 @@ BOOST_FIXTURE_TEST_CASE( cli_list_active_sons, cli_fixture ) { std::string name1 = "sonaccount" + fc::to_pretty_string(i); std::string name2 = "sonaccount" + fc::to_pretty_string(i + 1); - vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::bitcoin, true, true); + vote_tx = con.wallet_api_ptr->vote_for_son(name1, name2, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); gpo = con.wallet_api_ptr->get_global_properties(); - BOOST_TEST_MESSAGE("gpo: " << gpo.active_sons.size()); + BOOST_TEST_MESSAGE("gpo active_sons[bitcoin]: " << gpo.active_sons.at(sidechain_type::bitcoin).size()); + BOOST_TEST_MESSAGE("gpo active_sons[hive]: " << gpo.active_sons.at(sidechain_type::hive).size()); - BOOST_CHECK(gpo.active_sons.size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::bitcoin).size() == son_number); + BOOST_CHECK(gpo.active_sons.at(sidechain_type::hive).size() == son_number); map active_sons = con.wallet_api_ptr->list_active_sons(); BOOST_CHECK(active_sons.size() == son_number); @@ -737,12 +864,14 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) con.wallet_api_ptr->transfer( "nathan", "sonaccount" + fc::to_pretty_string(i), "1000", "1.3.0", "Here are some CORE tokens for your new account", true ); con.wallet_api_ptr->create_vesting_balance("sonaccount" + fc::to_pretty_string(i), "500", "1.3.0", vesting_balance_type::gpos, true); - con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::bitcoin, true, true); + con.wallet_api_ptr->vote_for_son("sonaccount" + fc::to_pretty_string(i), name, sidechain_type::hive, true, true); } BOOST_CHECK(generate_maintenance_block()); son_object son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -750,7 +879,8 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) // check SON is in request_maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); // restore SON activity con.wallet_api_ptr->cancel_request_son_maintenance(name, true); @@ -758,7 +888,8 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) // check SON is active son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::active); // put SON in maintenance mode con.wallet_api_ptr->request_son_maintenance(name, true); @@ -766,14 +897,16 @@ BOOST_AUTO_TEST_CASE( maintenance_test ) // check SON is in request_maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::request_maintenance); // process maintenance BOOST_CHECK(generate_maintenance_block()); // check SON is in maintenance son_obj = con.wallet_api_ptr->get_son(name); - BOOST_CHECK(son_obj.status == son_status::in_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); + BOOST_CHECK(son_obj.statuses.at(sidechain_type::hive) == son_status::in_maintenance); } catch( fc::exception& e ) { diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 4fb3a24d..807c495a 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -1040,14 +1040,9 @@ BOOST_FIXTURE_TEST_CASE( hardfork_son2_time, database_fixture ) generate_blocks(HARDFORK_SON3_TIME); // after this hardfork maximum son account should not reset the value - // on 7 after maintenance interval anymore. It must be GRAPHENE_DEFAULT_MAX_SONS + // on 7 after maintenance interval anymore. It must be HARDFORK_SON2_TIME BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), GRAPHENE_DEFAULT_MAX_SONS); - generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); - generate_block(); - - BOOST_CHECK_EQUAL(db.get_global_properties().parameters.maximum_son_count(), 15); - } FC_LOG_AND_RETHROW() } BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture ) diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index 755ad37f..72f50b9d 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace graphene::chain; using namespace graphene::chain::test; @@ -63,9 +64,134 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { INVOKE(sidechain_address_add_test); - GET_ACTOR(alice); + generate_block(); + + //! ----- BEGIN CREATE SON bob ----- + ACTORS((bob)); + upgrade_to_lifetime_member(bob); + + transfer( committee_account, bob_id, asset( 1000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + + set_expiration(db, trx); + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(10*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.clear(); + trx.operations.push_back(op); + + // amount in the son balance need to be at least 50 + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), fc::exception ); + + op.amount = asset(50*GRAPHENE_BLOCKCHAIN_PRECISION); + trx.clear(); + + trx.operations.push_back(op); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + deposit = ptx.operation_results[0].get(); + + auto deposit_vesting = db.get(ptx.operation_results[0].get()); + + BOOST_CHECK_EQUAL(deposit(db).balance.amount.value, 50*GRAPHENE_BLOCKCHAIN_PRECISION); + auto now = db.head_block_time(); + BOOST_CHECK_EQUAL(deposit(db).is_withdraw_allowed(now, asset(50*GRAPHENE_BLOCKCHAIN_PRECISION)), false); // cant withdraw + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment ; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(1*GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + op.policy = linear_vesting_policy_initializer {}; + op.validate(); + + trx.clear(); + trx.operations.push_back(op); + trx.validate(); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // bob became son + { + flat_map sidechain_public_keys; + sidechain_public_keys[sidechain_type::bitcoin] = "bitcoin address"; + sidechain_public_keys[sidechain_type::hive] = "hive address"; + + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url; + op.deposit = deposit; + op.pay_vb = payment; + op.signing_key = bob_public_key; + op.sidechain_public_keys = sidechain_public_keys; + + trx.clear(); + trx.operations.push_back(op); + sign(trx, bob_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + + { + const auto &idx = db.get_index_type().indices().get(); + BOOST_REQUIRE(idx.size() == 1); + auto obj = idx.find(bob_id); + BOOST_REQUIRE(obj != idx.end()); + BOOST_CHECK(obj->url == test_url); + BOOST_CHECK(obj->signing_key == bob_public_key); + BOOST_CHECK(obj->sidechain_public_keys.at(sidechain_type::bitcoin) == "bitcoin address"); + BOOST_CHECK(obj->deposit.instance == deposit.instance.value); + BOOST_CHECK(obj->pay_vb.instance == payment.instance.value); + } + + // Note payment time just to generate enough blocks to make budget + const auto block_interval = db.get_global_properties().parameters.block_interval; + auto pay_fee_time = db.head_block_time().sec_since_epoch(); + generate_block(); + // Do maintenance from the upcoming block + auto schedule_maint = [&]() + { + db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dpo ) + { + _dpo.next_maintenance_time = db.head_block_time() + 1; + } ); + }; + + // Generate enough blocks to make budget + while( db.head_block_time().sec_since_epoch() - pay_fee_time < 100 * block_interval ) + { + generate_block(); + } + + // Enough blocks generated schedule maintenance now + schedule_maint(); + // This block triggers maintenance + generate_block(); + + //! ----- END CREATE SON bob ----- + + GET_ACTOR(alice); + const auto& idx = db.get_index_type().indices().get(); BOOST_REQUIRE( idx.size() == 1 ); auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin, time_point_sec::maximum() ) ); @@ -77,19 +203,7 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) { std::string new_withdraw_address = "withdraw_address"; generate_block(); - auto& son = db.create( [&]( son_object& sobj ) - { - sobj.son_account = bob_id; - sobj.statistics = db.create([&](son_statistics_object& s){s.owner = sobj.id;}).id; - }); - generate_block(); - db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - { - son_info sinfo; - sinfo.son_id = son.id; - _gpo.active_sons.push_back(sinfo); - }); - generate_block(); + set_expiration(db, trx); { BOOST_TEST_MESSAGE("Send sidechain_address_update_operation"); trx.clear(); diff --git a/tests/tests/son_operations_tests.cpp b/tests/tests/son_operations_tests.cpp index 1e3bb7e4..5bb003aa 100644 --- a/tests/tests/son_operations_tests.cpp +++ b/tests/tests/son_operations_tests.cpp @@ -193,12 +193,14 @@ try { // Modify SON's status to active db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; + _s.statuses[sidechain_type::hive] = son_status::in_maintenance; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); + _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time() - db.get_global_properties().parameters.son_deregister_time()); }); auto deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -218,7 +220,8 @@ try { generate_block(); BOOST_REQUIRE( idx.size() == 1 ); - BOOST_REQUIRE( obj->status == son_status::deregistered ); + BOOST_REQUIRE( obj->statuses.at(sidechain_type::bitcoin) == son_status::deregistered ); + BOOST_REQUIRE( obj->statuses.at(sidechain_type::hive) == son_status::deregistered ); BOOST_REQUIRE( son_stats_obj->deregistered_timestamp == now ); deposit_vesting = db.get(vesting_balance_id_type(0)); @@ -604,12 +607,14 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { // Modify SON's status to active db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::active; + _s.statuses[sidechain_type::bitcoin] = son_status::active; + _s.statuses[sidechain_type::hive] = son_status::active; }); db.modify( *son_stats_obj, [&]( son_statistics_object& _s) { - _s.last_down_timestamp = fc::time_point_sec(db.head_block_time()); + _s.last_down_timestamp[sidechain_type::bitcoin] = fc::time_point_sec(db.head_block_time()); + _s.last_down_timestamp[sidechain_type::hive] = fc::time_point_sec(db.head_block_time()); }); { @@ -626,7 +631,8 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::request_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::request_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::request_maintenance); } { @@ -643,16 +649,20 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); } // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; + _s.statuses[sidechain_type::hive] = son_status::in_maintenance; }); - uint64_t downtime = 0; + flat_map downtime; + downtime[sidechain_type::bitcoin] = 0; + downtime[sidechain_type::hive] = 0; { generate_block(); @@ -668,16 +678,21 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); - downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); - BOOST_CHECK( obj->status == son_status::inactive); - BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); + downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::inactive); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::inactive); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); } // Modify SON's status to in_maintenance db.modify( *obj, [&]( son_object& _s) { - _s.status = son_status::in_maintenance; + _s.statuses[sidechain_type::bitcoin] = son_status::in_maintenance; + _s.statuses[sidechain_type::hive] = son_status::in_maintenance; }); // SON is selected as one of the active SONs @@ -685,7 +700,8 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { { son_info son_inf; son_inf.son_id = son_id_type(0); - _gpo.active_sons.push_back(son_inf); + _gpo.active_sons[sidechain_type::bitcoin].push_back(son_inf); + _gpo.active_sons[sidechain_type::hive].push_back(son_inf); }); { @@ -702,10 +718,15 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch()); - downtime += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.sec_since_epoch(); - BOOST_CHECK( obj->status == son_status::active); - BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch()); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive) + op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch()); + downtime[sidechain_type::bitcoin] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin).sec_since_epoch(); + downtime[sidechain_type::hive] += op.ts.sec_since_epoch() - son_stats_obj->last_down_timestamp.at(sidechain_type::hive).sec_since_epoch(); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); } { @@ -722,9 +743,12 @@ BOOST_AUTO_TEST_CASE( son_heartbeat_test ) { PUSH_TX( db, trx, ~0); generate_block(); trx.clear(); - BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime, downtime); - BOOST_CHECK( obj->status == son_status::active); - BOOST_CHECK( son_stats_obj->last_active_timestamp == op.ts); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::bitcoin), downtime.at(sidechain_type::bitcoin)); + BOOST_REQUIRE_EQUAL(son_stats_obj->current_interval_downtime.at(sidechain_type::hive), downtime.at(sidechain_type::hive)); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) == op.ts); + BOOST_CHECK( son_stats_obj->last_active_timestamp.at(sidechain_type::hive) == op.ts); } } FC_LOG_AND_RETHROW() } @@ -749,7 +773,8 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { auto son_stats_obj = sidx.find( obj->statistics ); BOOST_REQUIRE( son_stats_obj != sidx.end() ); - BOOST_CHECK( obj->status == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::active); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::active); { // Check that transaction fails if down_ts < last_active_timestamp @@ -758,7 +783,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); - op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp - fc::seconds(1)); + op.down_ts = fc::time_point_sec(son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin) - fc::seconds(1)); trx.operations.push_back(op); set_expiration(db, trx); @@ -775,7 +800,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = alice_id; op.son_id = son_id_type(0); - op.down_ts = son_stats_obj->last_active_timestamp; + op.down_ts = son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin); trx.operations.push_back(op); set_expiration(db, trx); @@ -792,7 +817,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); - op.down_ts = son_stats_obj->last_active_timestamp; + op.down_ts = son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin); trx.operations.push_back(op); set_expiration(db, trx); @@ -801,8 +826,10 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { generate_block(); trx.clear(); - BOOST_CHECK( obj->status == son_status::in_maintenance); - BOOST_CHECK( son_stats_obj->last_down_timestamp == op.down_ts); + BOOST_CHECK( obj->statuses.at(sidechain_type::bitcoin) == son_status::in_maintenance); + BOOST_CHECK( obj->statuses.at(sidechain_type::hive) == son_status::in_maintenance); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::bitcoin) == op.down_ts); + BOOST_CHECK( son_stats_obj->last_down_timestamp.at(sidechain_type::hive) == op.down_ts); } { @@ -812,7 +839,7 @@ BOOST_AUTO_TEST_CASE( son_report_down_test ) { son_report_down_operation op; op.payer = db.get_global_properties().parameters.son_account(); op.son_id = son_id_type(0); - op.down_ts = son_stats_obj->last_active_timestamp; + op.down_ts = son_stats_obj->last_active_timestamp.at(sidechain_type::bitcoin); trx.operations.push_back(op); set_expiration(db, trx); diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index cef29b54..4b1e4fe2 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -154,13 +154,14 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { op.payer = db.get_global_properties().parameters.son_account(); + //! Fixme - add hive tests { son_info si; si.son_id = son_id_type(0); si.weight = 1000; si.signing_key = alice_public_key; - si.sidechain_public_keys[sidechain_type::bitcoin] = ""; - op.sons.push_back(si); + si.public_key = ""; + op.sons[sidechain_type::bitcoin].push_back(si); } { @@ -168,8 +169,8 @@ BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { si.son_id = son_id_type(1); si.weight = 1000; si.signing_key = bob_public_key; - si.sidechain_public_keys[sidechain_type::bitcoin] = ""; - op.sons.push_back(si); + si.public_key = ""; + op.sons[sidechain_type::bitcoin].push_back(si); } trx.operations.push_back(op);