From a688bb93ed4e16232a907aa8c76e240c83c771bf Mon Sep 17 00:00:00 2001 From: obucinac Date: Tue, 4 Feb 2020 19:31:45 +0100 Subject: [PATCH] son_wallet_object operations and multisig wallet recreation by RPC (#263) * Extend GPO.active_sons to contain votes and all public keys * Introduce son_wallet_object * son_wallet_object operations * Create son_wallet_object on new set of SONs, to initiate primary wallet recreation * son_wallet_object API and cli wallet commands * Send RPC command to bitcoin node to recreate multisig wallet * Updating wallet info through operation instead through database.modify() for persistance * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Update libraries/chain/include/graphene/chain/protocol/son_wallet.hpp * Fix #include * Refactor primary wallet recreation * PW recreation refactoring, prevent duplicated recreations, update wallet address through proposal * Quickfix for checking payer in evaluator * Fix failing son_wallet_tests - Check for son_btc_account is temporarely disabled * Remove redundant file Co-authored-by: gladcow --- CMakeLists.txt | 2 +- libraries/app/database_api.cpp | 56 +++ libraries/app/impacted.cpp | 12 +- .../app/include/graphene/app/database_api.hpp | 30 ++ libraries/chain/CMakeLists.txt | 2 + libraries/chain/db_init.cpp | 8 +- libraries/chain/db_maint.cpp | 36 +- libraries/chain/db_notify.cpp | 12 +- libraries/chain/hardfork.d/SON.hf | 5 +- .../graphene/chain/protocol/operations.hpp | 9 +- .../graphene/chain/protocol/son_wallet.hpp | 40 ++ .../include/graphene/chain/protocol/types.hpp | 2 + .../graphene/chain/son_wallet_evaluator.hpp | 25 ++ .../graphene/chain/son_wallet_object.hpp | 17 +- libraries/chain/son_wallet_evaluator.cpp | 81 ++++ .../plugins/peerplays_sidechain/addresses.txt | 391 ------------------ .../peerplays_sidechain_plugin.hpp | 9 +- .../sidechain_net_handler.hpp | 21 +- .../sidechain_net_handler_bitcoin.hpp | 5 +- .../sidechain_net_manager.hpp | 6 +- .../peerplays_sidechain_plugin.cpp | 123 +++++- .../sidechain_net_handler.cpp | 11 +- .../sidechain_net_handler_bitcoin.cpp | 97 ++++- .../sidechain_net_manager.cpp | 20 +- .../wallet/include/graphene/wallet/wallet.hpp | 23 ++ libraries/wallet/wallet.cpp | 30 ++ programs/js_operation_serializer/main.cpp | 1 + tests/common/database_fixture.cpp | 46 ++- tests/common/database_fixture.hpp | 18 +- tests/tests/son_wallet_tests.cpp | 225 ++++++++++ 30 files changed, 895 insertions(+), 468 deletions(-) create mode 100644 libraries/chain/include/graphene/chain/protocol/son_wallet.hpp create mode 100644 libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp create mode 100644 libraries/chain/son_wallet_evaluator.cpp delete mode 100644 libraries/plugins/peerplays_sidechain/addresses.txt create mode 100644 tests/tests/son_wallet_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6853e2c8..b26bbc57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux endif( APPLE ) if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-invalid-offsetof -Wno-terminate -Wno-sign-compare" ) elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" ) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index c4566d24..c6c8a952 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -152,6 +152,11 @@ class database_api_impl : public std::enable_shared_from_this map lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const; uint64_t get_son_count()const; + // SON wallets + optional get_active_son_wallet(); + optional get_son_wallet_by_time_point(time_point_sec time_point); + vector> get_son_wallets(uint32_t limit); + // Sidechain addresses vector> get_sidechain_addresses(const vector& sidechain_address_ids)const; vector> get_sidechain_addresses_by_account(account_id_type account)const; @@ -1770,6 +1775,57 @@ uint64_t database_api_impl::get_son_count()const return _db.get_index_type().indices().size(); } +////////////////////////////////////////////////////////////////////// +// // +// SON Wallets // +// // +////////////////////////////////////////////////////////////////////// + +optional database_api::get_active_son_wallet() +{ + return my->get_active_son_wallet(); +} + +optional database_api_impl::get_active_son_wallet() +{ + const auto& idx = _db.get_index_type().indices().get(); + auto obj = idx.rbegin(); + if (obj != idx.rend()) { + return *obj; + } + return {}; +} + +optional database_api::get_son_wallet_by_time_point(time_point_sec time_point) +{ + return my->get_son_wallet_by_time_point(time_point); +} + +optional database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point) +{ + const auto& son_wallets_by_id = _db.get_index_type().indices().get(); + for (const son_wallet_object& swo : son_wallets_by_id) { + if ((time_point >= swo.valid_from) && (time_point < swo.expires)) + return swo; + } + return {}; +} + +vector> database_api::get_son_wallets(uint32_t limit) +{ + return my->get_son_wallets(limit); +} + +vector> database_api_impl::get_son_wallets(uint32_t limit) +{ + FC_ASSERT( limit <= 1000 ); + vector> result; + const auto& son_wallets_by_id = _db.get_index_type().indices().get(); + for (const son_wallet_object& swo : son_wallets_by_id) + result.push_back(swo); + return result; +} + ////////////////////////////////////////////////////////////////////// // // // Sidechain Accounts // diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 0aefe922..6834fa51 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -310,9 +310,18 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const son_report_down_operation& op ){ + _impacted.insert( op.payer ); + } void operator()( const son_maintenance_operation& op ){ _impacted.insert( op.owner_account ); } + void operator()( const son_wallet_recreate_operation& op ){ + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_update_operation& op ){ + _impacted.insert( op.payer ); + } void operator()( const sidechain_address_add_operation& op ){ _impacted.insert( op.sidechain_address_account ); } @@ -322,9 +331,6 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ){ _impacted.insert( op.sidechain_address_account ); } - void operator()( const son_report_down_operation& op ){ - _impacted.insert( op.payer ); - } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index e3252ec6..76ef822c 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -603,6 +604,30 @@ class database_api */ uint64_t get_son_count()const; + ///////////////////////// + // SON Wallets // + ///////////////////////// + + /** + * @brief Get active SON wallet + * @return Active SON wallet object + */ + optional get_active_son_wallet(); + + /** + * @brief Get SON wallet that was active for a given time point + * @param time_point Time point + * @return SON wallet object, for the wallet that was active for a given time point + */ + optional get_son_wallet_by_time_point(time_point_sec time_point); + + /** + * @brief Get full list of SON wallets + * @param limit Maximum number of results to return + * @return A list of SON wallet objects + */ + vector> get_son_wallets(uint32_t limit); + ///////////////////////// // Sidechain Addresses // ///////////////////////// @@ -855,6 +880,11 @@ FC_API(graphene::app::database_api, (lookup_son_accounts) (get_son_count) + // SON wallets + (get_active_son_wallet) + (get_son_wallet_by_time_point) + (get_son_wallets) + // Sidechain addresses (get_sidechain_addresses) (get_sidechain_addresses_by_account) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index bba9e7f1..b392bc5e 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -117,6 +117,8 @@ add_library( graphene_chain son_evaluator.cpp son_object.cpp + son_wallet_evaluator.cpp + sidechain_address_evaluator.cpp ${HEADERS} diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 5b5f8029..c41aad87 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,7 @@ #include #include #include +#include #include #include @@ -250,11 +252,13 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); } void database::initialize_indexes() @@ -299,6 +303,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 929346b9..935e520e 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -484,7 +484,41 @@ void database::update_active_sons() ilog( "Active SONs set NOT CHANGED" ); } else { ilog( "Active SONs set CHANGED" ); - // Store new SON info, initiate wallet recreation and transfer of funds + + bool should_recreate_pw = true; + + // Expire for current son_wallet_object wallet, if exists + const auto& idx_swi = get_index_type().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*obj).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); + } + } + + should_recreate_pw = !wallet_son_sets_equal; + + if (should_recreate_pw) { + modify(*obj, [&, obj](son_wallet_object &swo) { + swo.expires = head_block_time(); + }); + } + } + + 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()); + }); + } + vector sons_to_remove; // find all cur_active_sons members that is not in new_active_sons for_each(cur_active_sons.begin(), cur_active_sons.end(), diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index c7946906..fbd3888d 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -297,9 +297,18 @@ struct get_impacted_account_visitor void operator()( const son_heartbeat_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const son_report_down_operation& op ) { + _impacted.insert( op.payer ); + } void operator()( const son_maintenance_operation& op ) { _impacted.insert( op.owner_account ); } + void operator()( const son_wallet_recreate_operation& op ) { + _impacted.insert( op.payer ); + } + void operator()( const son_wallet_update_operation& op ) { + _impacted.insert( op.payer ); + } void operator()( const sidechain_address_add_operation& op ) { _impacted.insert( op.sidechain_address_account ); } @@ -309,9 +318,6 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ) { _impacted.insert( op.sidechain_address_account ); } - void operator()( const son_report_down_operation& op ) { - _impacted.insert( op.payer ); - } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/hardfork.d/SON.hf b/libraries/chain/hardfork.d/SON.hf index 355c5b96..5c4e1e76 100644 --- a/libraries/chain/hardfork.d/SON.hf +++ b/libraries/chain/hardfork.d/SON.hf @@ -1,5 +1,6 @@ -// SON HARDFORK Monday, September 21, 2020 1:43:11 PM +// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800 +// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791 #ifndef HARDFORK_SON_TIME #include -#define HARDFORK_SON_TIME (fc::time_point_sec( time(NULL) - (60 * 60) )) +#define HARDFORK_SON_TIME (fc::time_point_sec( 1577836800 )) #endif diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 07695705..74fd532c 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -47,6 +47,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -142,11 +143,13 @@ namespace graphene { namespace chain { son_update_operation, son_delete_operation, son_heartbeat_operation, + son_report_down_operation, + son_maintenance_operation, + son_wallet_recreate_operation, + son_wallet_update_operation, sidechain_address_add_operation, sidechain_address_update_operation, - sidechain_address_delete_operation, - son_report_down_operation, - son_maintenance_operation + sidechain_address_delete_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp new file mode 100644 index 00000000..f41cfa1f --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + + struct son_wallet_recreate_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + vector sons; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_wallet_update_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + son_wallet_id_type son_wallet_id; + graphene::peerplays_sidechain::sidechain_type sidechain; + string address; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) ) +FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) ) +FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 1d056740..abfce9c8 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -430,6 +430,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (bet_object_type) (son_object_type) (son_proposal_object_type) + (son_wallet_object_type) (sidechain_address_object_type) (OBJECT_TYPE_COUNT) ) @@ -504,6 +505,7 @@ FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type ) FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) diff --git a/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp b/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp new file mode 100644 index 00000000..78e8655f --- /dev/null +++ b/libraries/chain/include/graphene/chain/son_wallet_evaluator.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class recreate_son_wallet_evaluator : public evaluator +{ +public: + typedef son_wallet_recreate_operation operation_type; + + void_result do_evaluate(const son_wallet_recreate_operation& o); + object_id_type do_apply(const son_wallet_recreate_operation& o); +}; + +class update_son_wallet_evaluator : public evaluator +{ +public: + typedef son_wallet_update_operation operation_type; + + void_result do_evaluate(const son_wallet_update_operation& o); + object_id_type do_apply(const son_wallet_update_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index c3ea204d..aec28342 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include namespace graphene { namespace chain { @@ -16,16 +17,26 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = son_wallet_object_type; + time_point_sec valid_from; + time_point_sec expires; + flat_map addresses; + vector sons; }; - struct by_sidechain_type; - struct by_address; + struct by_valid_from; + struct by_expires; using son_wallet_multi_index_type = multi_index_container< son_wallet_object, indexed_by< ordered_unique< tag, member + >, + ordered_unique< tag, + member + >, + ordered_unique< tag, + member > > >; @@ -33,4 +44,4 @@ namespace graphene { namespace chain { } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object), - (addresses) ) + (valid_from) (expires) (addresses) (sons) ) diff --git a/libraries/chain/son_wallet_evaluator.cpp b/libraries/chain/son_wallet_evaluator.cpp new file mode 100644 index 00000000..55d4645d --- /dev/null +++ b/libraries/chain/son_wallet_evaluator.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include + +namespace graphene { namespace chain { + +void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.rbegin(); + if(itr != idx.rend()) + { + // Compare current wallet SONs and to-be lists of active sons + auto cur_wallet_sons = (*itr).sons; + auto new_wallet_sons = op.sons; + + 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); + } + } + + FC_ASSERT(son_sets_equal == false, "Wallet recreation not needed, active SONs set is not changed."); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.rbegin(); + if(itr != idx.rend()) + { + db().modify(*itr, [&, op](son_wallet_object &swo) { + swo.expires = db().head_block_time(); + }); + } + + const auto& new_son_wallet_object = db().create( [&]( son_wallet_object& obj ){ + obj.valid_from = db().head_block_time(); + obj.expires = time_point_sec::maximum(); + obj.sons = op.sons; + }); + return new_son_wallet_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op) +{ try{ + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); + //FC_ASSERT(db().get_global_properties().parameters.get_son_btc_account_id() != GRAPHENE_NULL_ACCOUNT, "SON paying account not set."); + FC_ASSERT( op.payer == db().get_global_properties().parameters.get_son_btc_account_id() ); + + const auto& idx = db().get_index_type().indices().get(); + FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() ); + auto itr = idx.find(op.son_wallet_id); + FC_ASSERT( itr->addresses.find(peerplays_sidechain::sidechain_type::bitcoin) == itr->addresses.end() || + itr->addresses.at(peerplays_sidechain::sidechain_type::bitcoin).empty(), "Sidechain wallet address already set"); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + auto itr = idx.find(op.son_wallet_id); + if(itr != idx.end()) + { + db().modify(*itr, [&op](son_wallet_object &swo) { + swo.addresses[op.sidechain] = op.address; + }); + } + return op.son_wallet_id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain diff --git a/libraries/plugins/peerplays_sidechain/addresses.txt b/libraries/plugins/peerplays_sidechain/addresses.txt deleted file mode 100644 index df57167d..00000000 --- a/libraries/plugins/peerplays_sidechain/addresses.txt +++ /dev/null @@ -1,391 +0,0 @@ -2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J -{ - "address": "2N5aFW5WFaYZLuJWx9RGziHBdEMj9Zf8s3J", - "scriptPubKey": "a914873aad1ecf7510c80b83d8ca94d21432dc71b88787", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/2']0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd))#7s0qfnvz", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00141d30ac0c47f7b32460265daf49c5925236f5882d", - "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "1d30ac0c47f7b32460265daf49c5925236f5882d", - "pubkey": "0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd", - "address": "bcrt1qr5c2crz877ejgcpxtkh5n3vj2gm0tzpdqrw3n0", - "scriptPubKey": "00141d30ac0c47f7b32460265daf49c5925236f5882d" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/2'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2MxAnE469fhhdvUqUB7daU997VSearb2mn7 -{ - "address": "2MxAnE469fhhdvUqUB7daU997VSearb2mn7", - "scriptPubKey": "a914360175a50918495a20573aed68d506a790420fe587", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/3']02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d))#p3st4e4e", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00146c1c0571f3132eb0702f487123d2026495592830", - "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "6c1c0571f3132eb0702f487123d2026495592830", - "pubkey": "02b510a452d6e80f943e4cc85af5cad6c528bda87fc92b821dd246a1a76c175b0d", - "address": "bcrt1qdswq2u0nzvhtqup0fpcj85szvj24j2psgfwy6w", - "scriptPubKey": "00146c1c0571f3132eb0702f487123d2026495592830" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/3'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL -{ - "address": "2NAYptFvTU8vJ1pC7CxvVA9R7D3NdBJHpwL", - "scriptPubKey": "a914bdce56e7f2fc04614c0f6f4d1d59fff63b0b73f187", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/4']020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462))#3r63c8fu", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00146abd0d5f055df80b41bc6449328eda09f61a2be3", - "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "6abd0d5f055df80b41bc6449328eda09f61a2be3", - "pubkey": "020d771492947feb54abbcbc5f5e86ef26df3747c377573c709507a47f10636462", - "address": "bcrt1qd27s6hc9thuqksduv3yn9rk6p8mp52lr2e846e", - "scriptPubKey": "00146abd0d5f055df80b41bc6449328eda09f61a2be3" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/4'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR -{ - "address": "2N9zPaLDfaJazUmVfr3wgn8BK75tid2kkzR", - "scriptPubKey": "a914b7abe6d957106da3a21782eea1164f4964b521ba87", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/5']03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce))#e3sfze3l", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8", - "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "7a4fd72ff8e192004c70d8139b4ca53e1467d8c8", - "pubkey": "03585ae695cfbbc8e1a93feeb6438c62d744b2581ba36a1e5ca780edd35aedb8ce", - "address": "bcrt1q0f8awtlcuxfqqnrsmqfekn998c2x0kxgf4t7dm", - "scriptPubKey": "00147a4fd72ff8e192004c70d8139b4ca53e1467d8c8" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/5'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL -{ - "address": "2NDN7cDH3E57E1B8TwTYvBgF7CndL4FTBPL", - "scriptPubKey": "a914dcb019e8330b4fffc50ba22bbf90215922a1379787", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/6']028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013))#3326m2za", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652", - "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "ccd3dee026a1d641352f5b8c7d72805d75fd0652", - "pubkey": "028c78c069d3d6eeb73373eb54edfa61f2e974c01c21b979b0b3f7058805b95013", - "address": "bcrt1qenfaacpx58tyzdf0twx86u5qt46l6pjjef5y7u", - "scriptPubKey": "0014ccd3dee026a1d641352f5b8c7d72805d75fd0652" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/6'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG -{ - "address": "2MzEmSiwrRzozxE6gfZ14LAyDHZ4DYP1zVG", - "scriptPubKey": "a9144cb2b8f97d8e7ad5bfb81afd611394387f374ab887", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/7']02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b))#7gvhakzu", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", - "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "e3607a0f745e2fb8b04fe1fa7f078c35009a77c1", - "pubkey": "02f7d952e00d9c262c20c3526d4029245ab890a28dbdcbadfec964578c47719f7b", - "address": "bcrt1quds85rm5tchm3vz0u8a87puvx5qf5a7pqw8u7l", - "scriptPubKey": "0014e3607a0f745e2fb8b04fe1fa7f078c35009a77c1" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/7'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg -{ - "address": "2NDCdm1WVJVCMWJzRaSSy9NDvpNKiqkbrMg", - "scriptPubKey": "a914dae51c6601ef4e05817f67d57d3ac0c8cb64948e87", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/8']03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b))#ckuy6v83", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7", - "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "60c1c7776b1f92cd36fe6138b99212ebe6381fe7", - "pubkey": "03b358000050ffc6318a44d08ee9da9484d5a7d95f509241adf8a52555a0fdde6b", - "address": "bcrt1qvrquwamtr7fv6dh7vyutnysja0nrs8l8vzcnwj", - "scriptPubKey": "001460c1c7776b1f92cd36fe6138b99212ebe6381fe7" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/8'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX -{ - "address": "2Mu2iz3Jfqjyv3hBQGQZSGmZGZxhJp91TNX", - "scriptPubKey": "a91413930c6d40f5f01b169f2fc7884c2b3984ff8a3087", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/9']022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac))#f8cdpnn9", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4", - "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "35b75b7c34a7f908955b8b93aaa677aa47fab2c4", - "pubkey": "022752cd513f04f68074bd90a96a82b523a197171376382dedf3413bbdccae0dac", - "address": "bcrt1qxkm4klp55lus392m3wf64fnh4frl4vkyng4ffg", - "scriptPubKey": "001435b75b7c34a7f908955b8b93aaa677aa47fab2c4" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/9'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM -{ - "address": "2N1sFbwcn4QVbvjp7yRsN4mg4mBFbvC8gKM", - "scriptPubKey": "a9145e91543069ae37d8bace2f59aade945f5916dc3287", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/10']03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af))#x7xpvc0y", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", - "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2", - "pubkey": "03114bf9794439221c0f7e910d7b64f242847184381a5ef238cef8d70f8868b4af", - "address": "bcrt1qe6e4hqvckkx8dlx83fcdr5aza5w5xf0jd22xet", - "scriptPubKey": "0014ceb35b8198b58c76fcc78a70d1d3a2ed1d4325f2" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/10'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - -2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy -{ - "address": "2NDmxr6ufBE7Zgdgq9hShF2grx2YPEiTyNy", - "scriptPubKey": "a914e132c578bd294a01a472d42b376b29e5ef6678c987", - "ismine": true, - "solvable": true, - "desc": "sh(wpkh([153472fd/0'/0'/11']02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b))#2janhauz", - "iswatchonly": false, - "isscript": true, - "iswitness": false, - "script": "witness_v0_keyhash", - "hex": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762", - "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", - "embedded": { - "isscript": false, - "iswitness": true, - "witness_version": 0, - "witness_program": "831d63f8d99be71f9b385f2ccc92ab2783da3762", - "pubkey": "02eb6e43fb6ad9f1bf23bf821a25d5a1e84550380e0973ea5b00b087b3e9059c7b", - "address": "bcrt1qsvwk87xen0n3lxectukvey4ty7pa5dmz0yanuv", - "scriptPubKey": "0014831d63f8d99be71f9b385f2ccc92ab2783da3762" - }, - "label": "", - "ischange": false, - "timestamp": 1571845292, - "hdkeypath": "m/0'/0'/11'", - "hdseedid": "7fc94a298c785719434c63458bb41bc1995a270d", - "hdmasterfingerprint": "153472fd", - "labels": [ - { - "name": "", - "purpose": "receive" - } - ] -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 e986fe8e..9612cf55 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 @@ -2,11 +2,7 @@ #include -#include #include -#include - -#include namespace graphene { namespace peerplays_sidechain { using namespace chain; @@ -30,6 +26,11 @@ class peerplays_sidechain_plugin : public graphene::app::plugin virtual void plugin_startup() override; std::unique_ptr my; + + son_id_type get_son_id(); + son_object get_son_object(); + bool is_active_son(); + std::map& get_private_keys(); }; } } //graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index e841a639..da6321a9 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -1,27 +1,32 @@ #pragma once -#include -#include - #include #include +#include +#include +#include + namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler { public: - sidechain_net_handler(std::shared_ptr db, const boost::program_options::variables_map& options); + sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); virtual ~sidechain_net_handler(); + graphene::peerplays_sidechain::sidechain_type get_sidechain(); std::vector get_sidechain_addresses(); -protected: - std::shared_ptr database; - graphene::peerplays_sidechain::sidechain_type sidechain; - void sidechain_event_data_received(const sidechain_event_data& sed); + virtual void recreate_primary_wallet() = 0; + +protected: + peerplays_sidechain_plugin& plugin; + graphene::chain::database& database; + graphene::peerplays_sidechain::sidechain_type sidechain; + virtual std::string create_multisignature_wallet( const std::vector public_keys ) = 0; virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0; virtual std::string sign_transaction( const std::string& transaction ) = 0; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index 4c37c530..803b24de 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -19,6 +19,7 @@ public: bool receive_mempool_entry_tx( const std::string& tx_hash ); uint64_t receive_estimated_fee(); void send_btc_tx( const std::string& tx_hex ); + std::string add_multisig_address( const std::vector public_keys ); bool connection_is_not_defined() const; private: @@ -56,9 +57,11 @@ private: class sidechain_net_handler_bitcoin : public sidechain_net_handler { public: - sidechain_net_handler_bitcoin(std::shared_ptr db, const boost::program_options::variables_map& options); + sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options); virtual ~sidechain_net_handler_bitcoin(); + void recreate_primary_wallet(); + bool connection_is_not_defined() const; std::string create_multisignature_wallet( const std::vector public_keys ); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp index c60aa73b..bd6f1ab3 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_manager.hpp @@ -12,12 +12,14 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_manager { public: - sidechain_net_manager(std::shared_ptr db); + sidechain_net_manager(peerplays_sidechain_plugin& _plugin); virtual ~sidechain_net_manager(); bool create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map& options); + void recreate_primary_wallet(); private: - std::shared_ptr database; + peerplays_sidechain_plugin& plugin; + graphene::chain::database& database; std::vector> net_handlers; }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 86caec2d..6f7a0d77 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,9 +1,14 @@ #include +#include +#include + #include #include -#include + #include +#include +#include #include #include @@ -25,11 +30,21 @@ class peerplays_sidechain_plugin_impl boost::program_options::options_description& cfg); void plugin_initialize(const boost::program_options::variables_map& options); void plugin_startup(); + + son_id_type get_son_id(); + son_object get_son_object(); + bool is_active_son(); + std::map& get_private_keys(); + void schedule_heartbeat_loop(); void heartbeat_loop(); + void create_son_down_proposals(); + void recreate_primary_wallet(); + void process_deposits(); + //void process_withdrawals(); void on_block_applied( const signed_block& b ); void on_objects_new(const vector& new_object_ids); - void create_son_down_proposals(); + private: peerplays_sidechain_plugin& plugin; @@ -40,6 +55,7 @@ class peerplays_sidechain_plugin_impl std::map _private_keys; std::set _sons; fc::future _heartbeat_task; + }; peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin& _plugin) : @@ -121,7 +137,10 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt throw; } - net_manager = std::unique_ptr(new sidechain_net_manager(plugin.app().chain_database())); + plugin.database().applied_block.connect( [&] (const signed_block& b) { on_block_applied(b); } ); + plugin.database().new_objects.connect( [&] (const vector& ids, const flat_set& impacted_accounts) { on_objects_new(ids); } ); + + net_manager = std::unique_ptr(new sidechain_net_manager(plugin)); config_ready_bitcoin = options.count( "bitcoin-node-ip" ) && options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" ) && @@ -153,24 +172,62 @@ void peerplays_sidechain_plugin_impl::plugin_startup() { if (config_ready_son) { ilog("SON running"); + + ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); + schedule_heartbeat_loop(); + } else { + elog("No sons configured! Please add SON IDs and private keys to configuration."); } if (config_ready_bitcoin) { ilog("Bitcoin sidechain handler running"); } - if( !_sons.empty() && !_private_keys.empty() ) - { - ilog("Starting heartbeats for ${n} sons.", ("n", _sons.size())); - heartbeat_loop(); - } else - elog("No sons configured! Please add SON IDs and private keys to configuration."); - //if (config_ready_ethereum) { // ilog("Ethereum sidechain handler running"); //} } +son_id_type peerplays_sidechain_plugin_impl::get_son_id() +{ + return *(_sons.begin()); +} + +son_object peerplays_sidechain_plugin_impl::get_son_object() +{ + const auto& idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find( get_son_id() ); + if (son_obj == idx.end()) + return {}; + return *son_obj; +} + +bool peerplays_sidechain_plugin_impl::is_active_son() +{ + const auto& idx = plugin.database().get_index_type().indices().get(); + auto son_obj = idx.find( get_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(), + std::inserter(active_son_ids, active_son_ids.end()), + [](const son_info& swi) { + return swi.son_id; + }); + + auto it = std::find(active_son_ids.begin(), active_son_ids.end(), get_son_id()); + + return (it != active_son_ids.end()); +} + +std::map& peerplays_sidechain_plugin_impl::get_private_keys() +{ + return _private_keys; +} + void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() { fc::time_point now = fc::time_point::now(); @@ -280,6 +337,17 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() } } +void peerplays_sidechain_plugin_impl::recreate_primary_wallet() +{ + net_manager->recreate_primary_wallet(); +} + +void peerplays_sidechain_plugin_impl::process_deposits() { +} + +//void peerplays_sidechain_plugin_impl::process_withdrawals() { +//} + void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) { chain::database& d = plugin.database(); @@ -293,7 +361,15 @@ void peerplays_sidechain_plugin_impl::on_block_applied( const signed_block& b ) chain::son_id_type next_son_id = d.get_scheduled_son(1); if(next_son_id == my_son_id) { + create_son_down_proposals(); + + recreate_primary_wallet(); + + process_deposits(); + + //process_withdrawals(); + } } @@ -351,6 +427,11 @@ void peerplays_sidechain_plugin_impl::on_objects_new(const vectorproposed_transaction.operations[0].which() == chain::operation::tag::value) { approve_proposal( proposal->id ); } + + if(proposal->proposed_transaction.operations.size() == 1 + && proposal->proposed_transaction.operations[0].which() == chain::operation::tag::value) { + approve_proposal( proposal->id ); + } } } } @@ -384,8 +465,6 @@ void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options: { ilog("peerplays sidechain plugin: plugin_initialize()"); my->plugin_initialize(options); - database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } ); - database().new_objects.connect([this](const vector& ids, const flat_set& impacted_accounts) { my->on_objects_new(ids); }); } void peerplays_sidechain_plugin::plugin_startup() @@ -394,5 +473,25 @@ void peerplays_sidechain_plugin::plugin_startup() my->plugin_startup(); } +son_id_type peerplays_sidechain_plugin::get_son_id() +{ + return my->get_son_id(); +} + +son_object peerplays_sidechain_plugin::get_son_object() +{ + return my->get_son_object(); +} + +bool peerplays_sidechain_plugin::is_active_son() +{ + return my->is_active_son(); +} + +std::map& peerplays_sidechain_plugin::get_private_keys() +{ + return my->get_private_keys(); +} + } } // graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 85196685..bb036d4a 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -6,21 +6,26 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler::sidechain_net_handler(std::shared_ptr db, const boost::program_options::variables_map& options) : - database(db) +sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : + plugin(_plugin), + database(_plugin.database()) { } sidechain_net_handler::~sidechain_net_handler() { } +graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidechain() { + return sidechain; +} + std::vector sidechain_net_handler::get_sidechain_addresses() { std::vector result; switch (sidechain) { case sidechain_type::bitcoin: { - const auto& sidechain_addresses_idx = database->get_index_type(); + const auto& sidechain_addresses_idx = database.get_index_type(); const auto& sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get(); const auto& sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain); std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second, diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index a2bd8d94..e6bda28e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -12,6 +12,9 @@ #include #include +#include +#include +#include namespace graphene { namespace peerplays_sidechain { @@ -129,6 +132,41 @@ void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex ) } } +std::string bitcoin_rpc_client::add_multisig_address( const std::vector public_keys ) +{ + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", \"method\": \"addmultisigaddress\", \"params\": ["); + std::string params = "2, ["; + std::string pubkeys = ""; + for (std::string pubkey : public_keys) { + if (!pubkeys.empty()) { + pubkeys = pubkeys + ","; + } + pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\""); + } + params = params + pubkeys + std::string("]"); + body = body + params + std::string("] }"); + + const auto reply = send_post_request( body ); + + if( reply.body.empty() ) + return ""; + + std::string reply_str( reply.body.begin(), reply.body.end() ); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json( ss, json ); + + if( reply.status == 200 ) { + return reply_str; + } + + if( json.count( "error" ) && !json.get_child( "error" ).empty() ) { + wlog( "BTC multisig address creation failed! Reply: ${msg}", ("msg", reply_str) ); + } + return ""; +} + bool bitcoin_rpc_client::connection_is_not_defined() const { return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); @@ -186,8 +224,8 @@ void zmq_listener::handle_zmq() { // ============================================================================= -sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr db, const boost::program_options::variables_map& options) : - sidechain_net_handler(db, options) { +sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin& _plugin, const boost::program_options::variables_map& options) : + sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::bitcoin; ip = options.at("bitcoin-node-ip").as(); @@ -215,6 +253,57 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(std::shared_ptr().indices().get(); + auto obj = idx_swi.rbegin(); + if (obj != idx_swi.rend()) { + + if ((obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) || + (obj->addresses.at(sidechain_type::bitcoin).empty())) { + + const chain::global_property_object& gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + vector son_pubkeys_bitcoin; + for ( const son_info& si : active_sons ) { + son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + } + string reply_str = create_multisignature_wallet(son_pubkeys_bitcoin); + + ilog(reply_str); + + std::stringstream ss(reply_str); + boost::property_tree::ptree pt; + boost::property_tree::read_json( ss, pt ); + if( pt.count( "error" ) && pt.get_child( "error" ).empty() ) { + ilog(__FUNCTION__); + + std::stringstream res; + boost::property_tree::json_parser::write_json(res, pt.get_child("result")); + + son_wallet_update_operation op; + op.payer = gpo.parameters.get_son_btc_account_id(); + op.son_wallet_id = (*obj).id; + op.sidechain = sidechain_type::bitcoin; + op.address = res.str(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_son_object().son_account; + proposal_op.proposed_ops.push_back( op_wrapper( 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_keys().begin()->second, proposal_op); + try { + database.push_transaction(trx); + } catch(fc::exception e){ + ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}",("e", e.what())); + } + } + } + } +} + bool sidechain_net_handler_bitcoin::connection_is_not_defined() const { return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined(); @@ -222,7 +311,7 @@ bool sidechain_net_handler_bitcoin::connection_is_not_defined() const std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector public_keys ) { - return ""; + return bitcoin_client->add_multisig_address(public_keys); } std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) @@ -248,7 +337,7 @@ void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data if( block != "" ) { const auto& vins = extract_info_from_block( block ); - const auto& sidechain_addresses_idx = database->get_index_type().indices().get(); + const auto& sidechain_addresses_idx = database.get_index_type().indices().get(); for( const auto& v : vins ) { const auto& addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain_type::bitcoin, v.address)); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index 7c39fd81..e21af593 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -1,12 +1,14 @@ #include #include +#include #include namespace graphene { namespace peerplays_sidechain { -sidechain_net_manager::sidechain_net_manager(std::shared_ptr db) : - database(db) +sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin& _plugin) : + plugin(_plugin), + database(_plugin.database()) { ilog(__FUNCTION__); } @@ -22,10 +24,10 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s switch (sidechain) { case sidechain_type::bitcoin: { - std::unique_ptr h = std::unique_ptr(new sidechain_net_handler_bitcoin(database, options)); - net_handlers.push_back(std::move(h)); - ret_val = true; - break; + 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; } default: assert(false); @@ -34,5 +36,11 @@ bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type s return ret_val; } +void sidechain_net_manager::recreate_primary_wallet() { + for ( size_t i = 0; i < net_handlers.size(); i++ ) { + net_handlers.at(i)->recreate_primary_wallet(); + } +} + } } // graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 39a2d8c7..3d470703 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1390,6 +1390,26 @@ class wallet_api */ map list_active_sons(); + /** + * @brief Get active SON wallet + * @return Active SON wallet object + */ + optional get_active_son_wallet(); + + /** + * @brief Get SON wallet that was active for a given time point + * @param time_point Time point + * @return SON wallet object, for the wallet that was active for a given time point + */ + optional get_son_wallet_by_time_point(time_point_sec time_point); + + /** + * @brief Get full list of SON wallets + * @param limit Maximum number of results to return + * @return A list of SON wallet objects + */ + vector> get_son_wallets(uint32_t limit); + /** Adds sidechain address owned by the given account for a given sidechain. * * An account can have at most one sidechain address for one sidechain. @@ -2221,6 +2241,9 @@ FC_API( graphene::wallet::wallet_api, (delete_son) (list_sons) (list_active_sons) + (get_active_son_wallet) + (get_son_wallet_by_time_point) + (get_son_wallets) (add_sidechain_address) (update_sidechain_address) (delete_sidechain_address) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 39b71b37..8e438ec4 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2006,6 +2006,21 @@ public: return result; } FC_CAPTURE_AND_RETHROW() } + optional get_active_son_wallet() + { try { + return _remote_db->get_active_son_wallet(); + } FC_CAPTURE_AND_RETHROW() } + + optional get_son_wallet_by_time_point(time_point_sec time_point) + { try { + return _remote_db->get_son_wallet_by_time_point(time_point); + } FC_CAPTURE_AND_RETHROW() } + + vector> get_son_wallets(uint32_t limit) + { try { + return _remote_db->get_son_wallets(limit); + } FC_CAPTURE_AND_RETHROW() } + signed_transaction add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, string address, @@ -4470,6 +4485,21 @@ map wallet_api::list_active_sons() return my->list_active_sons(); } +optional wallet_api::get_active_son_wallet() +{ + return my->get_active_son_wallet(); +} + +optional wallet_api::get_son_wallet_by_time_point(time_point_sec time_point) +{ + return my->get_son_wallet_by_time_point(time_point); +} + +vector> wallet_api::get_son_wallets(uint32_t limit) +{ + return my->get_son_wallets(limit); +} + signed_transaction wallet_api::add_sidechain_address(string account, peerplays_sidechain::sidechain_type sidechain, string address, diff --git a/programs/js_operation_serializer/main.cpp b/programs/js_operation_serializer/main.cpp index 2901f2e0..f9056a55 100644 --- a/programs/js_operation_serializer/main.cpp +++ b/programs/js_operation_serializer/main.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index c9917c95..4e171b14 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -265,11 +266,11 @@ void database_fixture::verify_asset_supplies( const database& db ) total_balances[betting_market_group.asset_id] += o.fees_collected; } - + uint64_t sweeps_vestings = 0; for( const sweeps_vesting_balance_object& svbo: db.get_index_type< sweeps_vesting_balance_index >().indices() ) sweeps_vestings += svbo.balance; - + total_balances[db.get_global_properties().parameters.sweeps_distribution_asset()] += sweeps_vestings / SWEEPS_VESTING_BALANCE_MULTIPLIER; total_balances[asset_id_type()] += db.get_dynamic_global_properties().witness_budget; total_balances[asset_id_type()] += db.get_dynamic_global_properties().son_budget; @@ -413,6 +414,23 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i generate_block(skip); } +bool database_fixture::generate_maintenance_block() { + try { + fc::ecc::private_key committee_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan"))); + uint32_t skip = ~database::skip_fork_db; + auto maint_time = db.get_dynamic_global_properties().next_maintenance_time; + auto slots_to_miss = db.get_slot_at_time(maint_time); + db.generate_block(db.get_slot_time(slots_to_miss), + db.get_scheduled_witness(slots_to_miss), + committee_key, + skip); + return true; + } catch (std::exception& e) + { + return false; + } +} + account_create_operation database_fixture::make_account( const std::string& name /* = "nathan" */, public_key_type key /* = key_id_type() */ @@ -731,7 +749,7 @@ const witness_object& database_fixture::create_witness( const account_object& ow witness_create_operation op; op.witness_account = owner.id; op.block_signing_key = signing_private_key.get_public_key(); - + secret_hash_type::encoder enc; fc::raw::pack(enc, signing_private_key); fc::raw::pack(enc, secret_hash_type()); @@ -1116,12 +1134,12 @@ int64_t database_fixture::get_balance( const account_object& account, const asse } int64_t database_fixture::get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, - account_id_type dividend_holder_account_id, - asset_id_type dividend_payout_asset_type) const + account_id_type dividend_holder_account_id, + asset_id_type dividend_payout_asset_type) const { - const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = + const pending_dividend_payout_balance_for_holder_object_index& pending_payout_balance_index = db.get_index_type(); - auto pending_payout_iter = + auto pending_payout_iter = pending_payout_balance_index.indices().get().find(boost::make_tuple(dividend_holder_asset_type, dividend_payout_asset_type, dividend_holder_account_id)); if (pending_payout_iter == pending_payout_balance_index.indices().get().end()) return 0; @@ -1342,7 +1360,7 @@ void database_fixture::delete_sport(sport_id_type sport_id) sport_delete_op.sport_id = sport_id; process_operation_by_witnesses(sport_delete_op); } FC_CAPTURE_AND_RETHROW( (sport_id) ) } - + const event_group_object& database_fixture::create_event_group(internationalized_string_type name, sport_id_type sport_id) { try { event_group_create_operation event_group_create_op; @@ -1372,7 +1390,7 @@ void database_fixture::delete_event_group(event_group_id_type event_group_id) process_operation_by_witnesses(event_group_delete_op); } FC_CAPTURE_AND_RETHROW( (event_group_id) ) } - + void database_fixture::try_update_event_group(event_group_id_type event_group_id, fc::optional sport_id, fc::optional name, @@ -1412,7 +1430,7 @@ void database_fixture::update_event_impl(event_id_type event_id, fc::optional event_group_id, fc::optional name, fc::optional season, - fc::optional status, + fc::optional status, bool force) { try { event_update_operation event_update_op; @@ -1449,9 +1467,9 @@ void database_fixture::update_betting_market_rules(betting_market_rules_id_type process_operation_by_witnesses(betting_market_rules_update_op); } FC_CAPTURE_AND_RETHROW( (name)(description) ) } -const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, - event_id_type event_id, - betting_market_rules_id_type rules_id, +const betting_market_group_object& database_fixture::create_betting_market_group(internationalized_string_type description, + event_id_type event_id, + betting_market_rules_id_type rules_id, asset_id_type asset_id, bool never_in_play, uint32_t delay_before_settling) @@ -1521,7 +1539,7 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark bet_place_op.amount_to_bet = amount_to_bet; bet_place_op.backer_multiplier = backer_multiplier; bet_place_op.back_or_lay = back_or_lay; - + trx.operations.push_back(bet_place_op); trx.validate(); processed_transaction ptx = db.push_transaction(trx, ~0); diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 200d1897..a190b9c6 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -199,6 +199,12 @@ struct database_fixture { */ void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true, uint32_t skip = ~0); + /////////// + /// @brief Skip intermediate blocks, and generate a maintenance block + /// @returns true on success + /////////// + bool generate_maintenance_block(); + account_create_operation make_account( const std::string& name = "nathan", public_key_type = public_key_type() @@ -295,7 +301,7 @@ struct database_fixture { int64_t get_balance( account_id_type account, asset_id_type a )const; int64_t get_balance( const account_object& account, const asset_object& a )const; int64_t get_dividend_pending_payout_balance(asset_id_type dividend_holder_asset_type, - account_id_type dividend_holder_account_id, + account_id_type dividend_holder_account_id, asset_id_type dividend_payout_asset_type) const; vector< operation_history_object > get_operation_history( account_id_type account_id )const; void process_operation_by_witnesses(operation op); @@ -321,7 +327,7 @@ struct database_fixture { fc::optional season, fc::optional status, bool force); - BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag, + BOOST_PARAMETER_MEMBER_FUNCTION((void), update_event, keywords::tag, (required (event_id, (event_id_type))) (optional (event_group_id, (fc::optional), fc::optional()) (name, (fc::optional), fc::optional()) @@ -336,9 +342,9 @@ struct database_fixture { void update_betting_market_rules(betting_market_rules_id_type rules_id, fc::optional name, fc::optional description); - const betting_market_group_object& create_betting_market_group(internationalized_string_type description, - event_id_type event_id, - betting_market_rules_id_type rules_id, + const betting_market_group_object& create_betting_market_group(internationalized_string_type description, + event_id_type event_id, + betting_market_rules_id_type rules_id, asset_id_type asset_id, bool never_in_play, uint32_t delay_before_settling); @@ -347,7 +353,7 @@ struct database_fixture { fc::optional rules_id, fc::optional status, bool force); - BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag, + BOOST_PARAMETER_MEMBER_FUNCTION((void), update_betting_market_group, keywords::tag, (required (betting_market_group_id, (betting_market_group_id_type))) (optional (description, (fc::optional), fc::optional()) (rules_id, (fc::optional), fc::optional()) diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp new file mode 100644 index 00000000..1a912e05 --- /dev/null +++ b/tests/tests/son_wallet_tests.cpp @@ -0,0 +1,225 @@ +#include + +#include "../common/database_fixture.hpp" + +#include +#include +#include + +using namespace graphene; +using namespace graphene::chain; +using namespace graphene::chain::test; + +BOOST_FIXTURE_TEST_SUITE( son_wallet_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( son_wallet_recreate_test ) { + + BOOST_TEST_MESSAGE("son_wallet_recreate_test"); + + generate_blocks(HARDFORK_SON_TIME); + generate_block(); + set_expiration(db, trx); + + ACTORS((alice)(bob)); + + upgrade_to_lifetime_member(alice); + upgrade_to_lifetime_member(bob); + + transfer( committee_account, alice_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + transfer( committee_account, bob_id, asset( 500000*GRAPHENE_BLOCKCHAIN_PRECISION ) ); + + generate_block(); + set_expiration(db, trx); + + std::string test_url = "https://create_son_test"; + + // create deposit vesting + vesting_balance_id_type deposit_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_alice = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_alice; + { + vesting_balance_create_operation op; + op.creator = alice_id; + op.owner = alice_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, alice_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_alice = ptx.operation_results[0].get(); + } + + generate_block(); + set_expiration(db, trx); + + // alice becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = alice_id; + op.url = test_url; + op.deposit = deposit_alice; + op.pay_vb = payment_alice; + op.signing_key = alice_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + // create deposit vesting + vesting_balance_id_type deposit_bob; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::son; + op.policy = dormant_vesting_policy_initializer {}; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + deposit_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // create payment normal vesting + vesting_balance_id_type payment_bob ; + { + vesting_balance_create_operation op; + op.creator = bob_id; + op.owner = bob_id; + op.amount = asset(500 * GRAPHENE_BLOCKCHAIN_PRECISION); + op.balance_type = vesting_balance_type::normal; + trx.operations.push_back(op); + sign(trx, bob_private_key); + processed_transaction ptx = PUSH_TX(db, trx, ~0); + trx.clear(); + payment_bob = ptx.operation_results[0].get(); + } + generate_block(); + set_expiration(db, trx); + + // bob becomes son + { + flat_map sidechain_public_keys; + sidechain_public_keys[graphene::peerplays_sidechain::sidechain_type::bitcoin] = "bitcoin address"; + + son_create_operation op; + op.owner_account = bob_id; + op.url = test_url; + op.deposit = deposit_bob; + op.pay_vb = payment_bob; + op.signing_key = bob_public_key; + op.sidechain_public_keys = sidechain_public_keys; + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + trx.clear(); + } + generate_block(); + set_expiration(db, trx); + + generate_blocks(60); + set_expiration(db, trx); + + { + BOOST_TEST_MESSAGE("Send son_wallet_recreate_operation"); + + son_wallet_recreate_operation op; + + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + + { + son_info si; + si.son_id = son_id_type(0); + si.total_votes = 1000; + si.signing_key = alice_public_key; + si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = ""; + op.sons.push_back(si); + } + + { + son_info si; + si.son_id = son_id_type(1); + si.total_votes = 1000; + si.signing_key = bob_public_key; + si.sidechain_public_keys[peerplays_sidechain::sidechain_type::bitcoin] = ""; + op.sons.push_back(si); + } + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + BOOST_TEST_MESSAGE("Check son_wallet_recreate_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find(son_wallet_id_type(0)); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_REQUIRE( obj->expires == time_point_sec::maximum() ); +} + +BOOST_AUTO_TEST_CASE( son_wallet_update_test ) { + + BOOST_TEST_MESSAGE("son_wallet_update_test"); + + INVOKE(son_wallet_recreate_test); + GET_ACTOR(alice); + + { + BOOST_TEST_MESSAGE("Send son_wallet_update_operation"); + + son_wallet_update_operation op; + + op.payer = db.get_global_properties().parameters.get_son_btc_account_id(); + op.son_wallet_id = son_wallet_id_type(0); + op.sidechain = graphene::peerplays_sidechain::sidechain_type::bitcoin; + op.address = "bitcoin address"; + + trx.operations.push_back(op); + sign(trx, alice_private_key); + PUSH_TX(db, trx, ~0); + } + generate_block(); + + { + BOOST_TEST_MESSAGE("Check son_wallet_update_operation results"); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.size() == 1 ); + auto obj = idx.find(son_wallet_id_type(0)); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_REQUIRE( obj->addresses.at(graphene::peerplays_sidechain::sidechain_type::bitcoin) == "bitcoin address" ); + } + +} + +BOOST_AUTO_TEST_SUITE_END()