diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 90538087..e95817aa 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -343,13 +343,13 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ){ _impacted.insert( op.sidechain_address_account ); } - void operator()( const bitcoin_transaction_send_operation& op ){ + void operator()( const sidechain_transaction_create_operation& op ){ _impacted.insert( op.payer ); } - void operator()( const bitcoin_transaction_sign_operation& op ){ + void operator()( const sidechain_transaction_sign_operation& op ){ _impacted.insert( op.payer ); } - void operator()( const bitcoin_send_transaction_process_operation& op ){ + void operator()( const sidechain_transaction_send_operation& op ){ _impacted.insert( op.payer ); } }; diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 9c068ba5..73db113d 100755 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -130,7 +130,7 @@ add_library( graphene_chain ) add_dependencies( graphene_chain build_hardfork_hpp ) -target_link_libraries( graphene_chain fc graphene_db peerplays_sidechain ) +target_link_libraries( graphene_chain fc graphene_db ) target_include_directories( graphene_chain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 781146af..b3311c2d 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -269,9 +269,9 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); - register_evaluator(); - register_evaluator(); - register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -321,7 +321,7 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); - add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 6cf7c7b0..db245f0a 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -330,13 +330,13 @@ struct get_impacted_account_visitor void operator()( const sidechain_address_delete_operation& op ) { _impacted.insert( op.sidechain_address_account ); } - void operator()( const bitcoin_transaction_send_operation& op ) { + void operator()( const sidechain_transaction_create_operation& op ) { _impacted.insert( op.payer ); } - void operator()( const bitcoin_transaction_sign_operation& op ) { + void operator()( const sidechain_transaction_sign_operation& op ) { _impacted.insert( op.payer ); } - void operator()( const bitcoin_send_transaction_process_operation& op ) { + void operator()( const sidechain_transaction_send_operation& op ) { _impacted.insert( op.payer ); } }; @@ -443,6 +443,8 @@ void get_relevant_accounts( const object* obj, flat_set& accoun assert( aobj != nullptr ); accounts.insert( aobj->sidechain_address_account ); break; + } case sidechain_transaction_object_type:{ + break; } } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index c4c00627..857c6dab 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -40,7 +40,7 @@ #include -#include +#include #include diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index d633056f..24bb7ba5 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -157,9 +157,9 @@ namespace graphene { namespace chain { sidechain_address_add_operation, sidechain_address_update_operation, sidechain_address_delete_operation, - bitcoin_transaction_send_operation, - bitcoin_transaction_sign_operation, - bitcoin_send_transaction_process_operation + sidechain_transaction_create_operation, + sidechain_transaction_sign_operation, + sidechain_transaction_send_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp index 7418f55e..e79f65de 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_address.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp index eb7e942d..4ddbd7ce 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp @@ -1,60 +1,71 @@ #pragma once #include #include -#include +#include namespace graphene { namespace chain { - struct bitcoin_transaction_send_operation : public base_operation + struct sidechain_transaction_create_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; + asset fee; + account_id_type payer; - // TODO: BTC Transaction Structs go here - fc::flat_map> signatures; + sidechain_type sidechain; + object_id_type object_id; + std::string transaction; + std::vector signers; account_id_type fee_payer()const { return payer; } - void validate()const {} share_type calculate_fee(const fee_parameters_type& k)const { return 0; } }; - struct bitcoin_transaction_sign_operation : public base_operation + struct sidechain_transaction_sign_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; - proposal_id_type proposal_id; - std::vector signatures; + asset fee; + account_id_type payer; + + sidechain_transaction_id_type sidechain_transaction_id; + std::string transaction; + block_id_type block; + bool complete; account_id_type fee_payer()const { return payer; } - void validate()const {} share_type calculate_fee( const fee_parameters_type& k )const { return 0; } }; - struct bitcoin_send_transaction_process_operation : public base_operation + struct sidechain_transaction_send_operation : public base_operation { struct fee_parameters_type { uint64_t fee = 0; }; - asset fee; - account_id_type payer; + asset fee; + account_id_type payer; - bitcoin_transaction_id_type bitcoin_transaction_id; + sidechain_transaction_id_type sidechain_transaction_id; account_id_type fee_payer()const { return payer; } - void validate()const {} share_type calculate_fee( const fee_parameters_type& k )const { return 0; } }; } } // graphene::chain -FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(signatures) ) +FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer) + (sidechain) + (object_id) + (transaction) + (signers) ) -FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) ) +FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(payer) + (sidechain_transaction_id) + (transaction) + (block) + (complete) ) -FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation::fee_parameters_type, (fee) ) -FC_REFLECT( graphene::chain::bitcoin_send_transaction_process_operation, (fee)(payer)(bitcoin_transaction_id) ) \ No newline at end of file +FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer) + (sidechain_transaction_id) ) \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/protocol/son.hpp b/libraries/chain/include/graphene/chain/protocol/son.hpp index 20353b91..3dfbc52d 100644 --- a/libraries/chain/include/graphene/chain/protocol/son.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace graphene { namespace chain { diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp index ddc1ff53..a1b44fac 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_deposit.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include @@ -14,7 +15,7 @@ namespace graphene { namespace chain { son_id_type son_id; fc::time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; @@ -46,7 +47,10 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer) - (son_id) (timestamp) (sidechain) (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) (peerplays_from) (peerplays_to) (peerplays_asset)) + (son_id) (timestamp) (sidechain) + (sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount) + (peerplays_from) (peerplays_to) (peerplays_asset) ) + FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer) - (son_wallet_deposit_id)) + (son_wallet_deposit_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp index 4e9d4971..353d695e 100644 --- a/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp +++ b/libraries/chain/include/graphene/chain/protocol/son_wallet_withdraw.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include @@ -14,12 +15,12 @@ namespace graphene { namespace chain { son_id_type son_id; fc::time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string peerplays_uid; std::string peerplays_transaction_id; chain::account_id_type peerplays_from; chain::asset peerplays_asset; - peerplays_sidechain::sidechain_type withdraw_sidechain; + sidechain_type withdraw_sidechain; std::string withdraw_address; std::string withdraw_currency; safe withdraw_amount; @@ -45,7 +46,10 @@ namespace graphene { namespace chain { FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer) - (son_id) (timestamp) (sidechain) (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) + (son_id) (timestamp) (sidechain) + (peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset) + (withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) ) + FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) ) FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer) - (son_wallet_withdraw_id)) + (son_wallet_withdraw_id) ) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 1caf1f9c..63863ca1 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -151,7 +151,7 @@ namespace graphene { namespace chain { son_wallet_deposit_object_type, son_wallet_withdraw_object_type, sidechain_address_object_type, - bitcoin_transaction_object_type, + sidechain_transaction_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -219,7 +219,7 @@ namespace graphene { namespace chain { class son_wallet_deposit_object; class son_wallet_withdraw_object; class sidechain_address_object; - class bitcoin_transaction_object; + class sidechain_transaction_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -252,7 +252,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type; typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type; typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type; - typedef object_id< protocol_ids, bitcoin_transaction_object_type,bitcoin_transaction_object> bitcoin_transaction_id_type; + typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type; // implementation types class global_property_object; @@ -443,7 +443,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (son_wallet_deposit_object_type) (son_wallet_withdraw_object_type) (sidechain_address_object_type) - (bitcoin_transaction_object_type) + (sidechain_transaction_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, @@ -521,7 +521,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type ) FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type ) FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type ) -FC_REFLECT_TYPENAME( graphene::chain::bitcoin_transaction_id_type ) +FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type ) FC_REFLECT( graphene::chain::void_t, ) diff --git a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp index 1a8b6967..86e1b16d 100644 --- a/libraries/chain/include/graphene/chain/sidechain_address_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_address_object.hpp @@ -3,7 +3,8 @@ #include #include -#include +#include +#include namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/sidechain_defs.hpp b/libraries/chain/include/graphene/chain/sidechain_defs.hpp new file mode 100644 index 00000000..f51b9eaf --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_defs.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace graphene { namespace chain { + +enum class sidechain_type { + bitcoin, + ethereum, + eos, + peerplays +}; + +} } + +namespace graphene { namespace peerplays_sidechain { + +using sidechain_type = graphene::chain::sidechain_type; + +} } + +FC_REFLECT_ENUM(graphene::chain::sidechain_type, + (bitcoin) + (ethereum) + (eos) + (peerplays) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp index aac04698..3bc64bfa 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp @@ -4,32 +4,31 @@ namespace graphene { namespace chain { -class bitcoin_transaction_send_evaluator : public evaluator +class sidechain_transaction_create_evaluator : public evaluator { public: - typedef bitcoin_transaction_send_operation operation_type; + typedef sidechain_transaction_create_operation operation_type; - void_result do_evaluate(const bitcoin_transaction_send_operation& o); - object_id_type do_apply(const bitcoin_transaction_send_operation& o); + void_result do_evaluate(const sidechain_transaction_create_operation& o); + object_id_type do_apply(const sidechain_transaction_create_operation& o); }; -class bitcoin_transaction_sign_evaluator : public evaluator +class sidechain_transaction_sign_evaluator : public evaluator { public: - typedef bitcoin_transaction_sign_operation operation_type; + typedef sidechain_transaction_sign_operation operation_type; - void_result do_evaluate(const bitcoin_transaction_sign_operation& o); - object_id_type do_apply(const bitcoin_transaction_sign_operation& o); - void update_proposal( const bitcoin_transaction_sign_operation& o ); + void_result do_evaluate(const sidechain_transaction_sign_operation& o); + object_id_type do_apply(const sidechain_transaction_sign_operation& o); }; -class bitcoin_send_transaction_process_evaluator : public evaluator +class sidechain_transaction_send_evaluator : public evaluator { public: - typedef bitcoin_send_transaction_process_operation operation_type; + typedef sidechain_transaction_send_operation operation_type; - void_result do_evaluate(const bitcoin_send_transaction_process_operation& o); - object_id_type do_apply(const bitcoin_send_transaction_process_operation& o); + void_result do_evaluate(const sidechain_transaction_send_operation& o); + object_id_type do_apply(const sidechain_transaction_send_operation& o); }; } } // namespace graphene::chain \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp index 95417852..f4f596cf 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp @@ -1,39 +1,69 @@ #pragma once +#include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; /** - * @class bitcoin_transaction_object - * @brief tracks state of bitcoin transaction. + * @class sidechain_transaction_object + * @brief tracks state of sidechain transaction during signing process. * @ingroup object */ - class bitcoin_transaction_object : public abstract_object + class sidechain_transaction_object : public abstract_object { public: static const uint8_t space_id = protocol_ids; - static const uint8_t type_id = bitcoin_transaction_object_type; - // Bitcoin structs go here. - bool processed = false; - fc::flat_map> signatures; + static const uint8_t type_id = sidechain_transaction_object_type; + + sidechain_type sidechain; + object_id_type object_id; + std::string transaction; + std::vector> signers; + + block_id_type block; + bool valid = false; + bool complete = false; + bool sent = false; }; - struct by_processed; - using bitcoin_transaction_multi_index_type = multi_index_container< - bitcoin_transaction_object, + struct by_object_id; + struct by_sidechain_and_complete; + struct by_sidechain_and_complete_and_sent; + using sidechain_transaction_multi_index_type = multi_index_container< + sidechain_transaction_object, indexed_by< ordered_unique< tag, member >, - ordered_non_unique< tag, - member + ordered_unique< tag, + member + >, + ordered_non_unique< tag, + composite_key, + member + > + >, + ordered_non_unique< tag, + composite_key, + member, + member + > > > >; - using bitcoin_transaction_index = generic_index; + using sidechain_transaction_index = generic_index; } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::db::object), - (processed)(signatures) ) +FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ), + (sidechain) + (object_id) + (transaction) + (signers) + (block) + (valid) + (complete) + (sent) ) diff --git a/libraries/chain/include/graphene/chain/son_info.hpp b/libraries/chain/include/graphene/chain/son_info.hpp index d30f0f6b..bc0c823b 100644 --- a/libraries/chain/include/graphene/chain/son_info.hpp +++ b/libraries/chain/include/graphene/chain/son_info.hpp @@ -1,6 +1,6 @@ #pragma once #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/son_object.hpp b/libraries/chain/include/graphene/chain/son_object.hpp index ec346185..5aee1265 100644 --- a/libraries/chain/include/graphene/chain/son_object.hpp +++ b/libraries/chain/include/graphene/chain/son_object.hpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; diff --git a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp index 4c30cc49..6ddc44c2 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_deposit_object.hpp @@ -1,6 +1,7 @@ #pragma once +#include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -17,7 +18,7 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_wallet_deposit_object_type; time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string sidechain_uid; std::string sidechain_transaction_id; std::string sidechain_from; @@ -45,7 +46,7 @@ namespace graphene { namespace chain { member >, ordered_non_unique< tag, - member + member >, ordered_unique< tag, member @@ -55,7 +56,7 @@ namespace graphene { namespace chain { >, ordered_non_unique< tag, composite_key, + member, member > > diff --git a/libraries/chain/include/graphene/chain/son_wallet_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_object.hpp index aec28342..315def33 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_object.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -20,7 +20,7 @@ namespace graphene { namespace chain { time_point_sec valid_from; time_point_sec expires; - flat_map addresses; + flat_map addresses; vector sons; }; diff --git a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp index 71245ba7..ef39aadf 100644 --- a/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp +++ b/libraries/chain/include/graphene/chain/son_wallet_withdraw_object.hpp @@ -1,6 +1,7 @@ #pragma once +#include #include -#include +#include namespace graphene { namespace chain { using namespace graphene::db; @@ -17,12 +18,12 @@ namespace graphene { namespace chain { static const uint8_t type_id = son_wallet_withdraw_object_type; time_point_sec timestamp; - peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::string peerplays_uid; std::string peerplays_transaction_id; chain::account_id_type peerplays_from; chain::asset peerplays_asset; - peerplays_sidechain::sidechain_type withdraw_sidechain; + sidechain_type withdraw_sidechain; std::string withdraw_address; std::string withdraw_currency; safe withdraw_amount; @@ -47,14 +48,14 @@ namespace graphene { namespace chain { member >, ordered_non_unique< tag, - member + member >, ordered_non_unique< tag, member >, ordered_non_unique< tag, composite_key, + member, member > > diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index a016ce27..3c72b9e9 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -1,140 +1,128 @@ #include +#include #include +#include #include #include -#include -#include -namespace graphene -{ -namespace chain -{ +namespace graphene { namespace chain { -void_result bitcoin_transaction_send_evaluator::do_evaluate(const bitcoin_transaction_send_operation &op) -{ - 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." ); - return void_result(); - } - FC_CAPTURE_AND_RETHROW((op)) -} +void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op) +{ 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." ); -object_id_type bitcoin_transaction_send_evaluator::do_apply(const bitcoin_transaction_send_operation &op) -{ - try - { - const auto &new_bitcoin_transaction_object = db().create([&](bitcoin_transaction_object &obj) { - obj.processed = false; - obj.signatures = op.signatures; + FC_ASSERT((op.object_id.is() || op.object_id.is() || op.object_id.is()), "Invalid object id"); + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.object_id); + FC_ASSERT(sto_obj == sto_idx.end(), "Sidechain transaction for a given object is already created"); + + FC_ASSERT(!op.transaction.empty(), "Sidechain transaction data not set"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op) +{ try { + const auto &new_sidechain_transaction_object = db().create([&](sidechain_transaction_object &sto) { + sto.sidechain = op.sidechain; + sto.object_id = op.object_id; + sto.transaction = op.transaction; + std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signers, sto.signers.end()), [](const son_id_type son_id) { + return std::make_pair(son_id, false); }); - return new_bitcoin_transaction_object.id; - } - FC_CAPTURE_AND_RETHROW((op)) -} + sto.block = db().head_block_id(); + sto.valid = true; + sto.complete = false; + sto.sent = false; + }); + return new_sidechain_transaction_object.id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } -void_result bitcoin_transaction_sign_evaluator::do_evaluate(const bitcoin_transaction_sign_operation &op) -{ - try - { - FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - const auto &proposal_idx = db().get_index_type().indices().get(); - const auto &proposal_itr = proposal_idx.find(op.proposal_id); - FC_ASSERT(proposal_idx.end() != proposal_itr, "proposal not found"); - // Checks can this SON approve this proposal - auto can_this_son_approve_this_proposal = [&]() { - const auto &sidx = db().get_index_type().indices().get(); - auto son_obj = sidx.find(op.payer); - if (son_obj == sidx.end()) - { - return false; - } - // TODO: Check if the SON is included in the PW script. - return true; - }; +void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_transaction_sign_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass - FC_ASSERT(can_this_son_approve_this_proposal(), "Invalid approval received"); - return void_result(); - } - FC_CAPTURE_AND_RETHROW((op)) -} + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); -object_id_type bitcoin_transaction_sign_evaluator::do_apply(const bitcoin_transaction_sign_operation &op) -{ - try - { - const auto &proposal = op.proposal_id(db()); - const auto &sidx = db().get_index_type().indices().get(); - auto son_obj = sidx.find(op.payer); + const auto &son_idx = db().get_index_type().indices().get(); + const auto &son_obj = son_idx.find(op.payer); + FC_ASSERT(son_obj != son_idx.end(), "SON object not found"); - db().modify(proposal, [&](proposal_object &po) { - auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get(); - bitcoin_transaction_send_op.signatures[son_obj->id] = op.signatures; - po.proposed_transaction.operations[0] = bitcoin_transaction_send_op; - }); - - db().modify( son_obj->statistics( db() ), [&]( son_statistics_object& sso ) { - sso.txs_signed += 1; - } ); - - update_proposal(op); - } - FC_CAPTURE_AND_RETHROW((op)) -} - -void bitcoin_transaction_sign_evaluator::update_proposal(const bitcoin_transaction_sign_operation &op) -{ - database &d = db(); - proposal_update_operation update_op; - - update_op.fee_paying_account = op.payer; - update_op.proposal = op.proposal_id; - update_op.active_approvals_to_add = {op.payer}; - - bool skip_fee_old = trx_state->skip_fee; - bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check; - trx_state->skip_fee = true; - trx_state->skip_fee_schedule_check = true; - - d.apply_operation(*trx_state, update_op); - - trx_state->skip_fee = skip_fee_old; - trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old; -} - -void_result bitcoin_send_transaction_process_evaluator::do_evaluate(const bitcoin_send_transaction_process_operation &op) -{ - 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." ); - const auto& btidx = db().get_index_type().indices().get(); - const auto btobj = btidx.find(op.bitcoin_transaction_id); - FC_ASSERT(btobj != btidx.end(), "Bitcoin Transaction Object not found"); - FC_ASSERT(btobj->processed == false, "Bitcoin Transaction already processed"); - return void_result(); - } - FC_CAPTURE_AND_RETHROW((op)) -} - -object_id_type bitcoin_send_transaction_process_evaluator::do_apply(const bitcoin_send_transaction_process_operation &op) -{ - try - { - const auto &btidx = db().get_index_type().indices().get(); - auto btobj = btidx.find(op.bitcoin_transaction_id); - if (btobj != btidx.end()) - { - db().modify(*btobj, [&op](bitcoin_transaction_object &bto) { - bto.processed = true; - }); + bool expected = false; + for (auto signer : sto_obj->signers) { + if (signer.first == son_obj->id) { + expected = !signer.second; } - return op.bitcoin_transaction_id; } - FC_CAPTURE_AND_RETHROW((op)) -} + FC_ASSERT(expected, "Signer not expected"); + + FC_ASSERT(sto_obj->block == op.block, "Sidechain transaction already signed in this block"); + + FC_ASSERT(sto_obj->valid, "Transaction not valid"); + FC_ASSERT(!sto_obj->complete, "Transaction signing completed"); + FC_ASSERT(!sto_obj->sent, "Transaction already sent"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_transaction_sign_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + const auto &son_idx = db().get_index_type().indices().get(); + auto son_obj = son_idx.find(op.payer); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + sto.transaction = op.transaction; + sto.block = db().head_block_id(); + sto.complete = op.complete; + for (size_t i = 0; i < sto.signers.size(); i++) { + if (sto.signers.at(i).first == son_obj->id) { + sto.signers.at(i).second = true; + } + } + }); + + db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) { + sso.txs_signed += 1; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_transaction_send_operation &op) +{ try { + FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass + + const auto &sto_idx = db().get_index_type().indices().get(); + const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id); + FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found"); + + FC_ASSERT(sto_obj->valid, "Transaction not valid"); + FC_ASSERT(sto_obj->complete, "Transaction signing not complete"); + FC_ASSERT(!sto_obj->sent, "Transaction already sent"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_transaction_send_operation &op) +{ try { + const auto &sto_idx = db().get_index_type().indices().get(); + auto sto_obj = sto_idx.find(op.sidechain_transaction_id); + + db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { + sto.block = db().head_block_id(); + sto.sent = true; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } } // namespace chain } // namespace graphene diff --git a/libraries/chain/son_wallet_deposit_evaluator.cpp b/libraries/chain/son_wallet_deposit_evaluator.cpp index c0f95e78..e2734ea4 100644 --- a/libraries/chain/son_wallet_deposit_evaluator.cpp +++ b/libraries/chain/son_wallet_deposit_evaluator.cpp @@ -23,21 +23,22 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de const auto& idx = db().get_index_type().indices().get(); auto itr = idx.find(op.sidechain_uid); if (itr == idx.end()) { - const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swto ){ - swto.timestamp = op.timestamp; - swto.sidechain = op.sidechain; - swto.sidechain_uid = op.sidechain_uid; - swto.sidechain_transaction_id = op.sidechain_transaction_id; - swto.sidechain_from = op.sidechain_from; - swto.sidechain_to = op.sidechain_to; - swto.sidechain_amount = op.sidechain_amount; - swto.peerplays_from = op.peerplays_from; - swto.peerplays_to = op.peerplays_to; - swto.peerplays_asset = op.peerplays_asset; + const auto& new_son_wallet_deposit_object = db().create( [&]( son_wallet_deposit_object& swdo ){ + swdo.timestamp = op.timestamp; + swdo.sidechain = op.sidechain; + swdo.sidechain_uid = op.sidechain_uid; + swdo.sidechain_transaction_id = op.sidechain_transaction_id; + swdo.sidechain_from = op.sidechain_from; + swdo.sidechain_to = op.sidechain_to; + swdo.sidechain_currency = op.sidechain_currency; + swdo.sidechain_amount = op.sidechain_amount; + swdo.peerplays_from = op.peerplays_from; + swdo.peerplays_to = op.peerplays_to; + swdo.peerplays_asset = op.peerplays_asset; auto &gpo = db().get_global_properties(); for (auto &si : gpo.active_sons) { - swto.expected_reports.insert(si.son_id); + swdo.expected_reports.insert(si.son_id); auto stats_itr = db().get_index_type().indices().get().find(si.son_id); db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) { @@ -48,14 +49,14 @@ object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_de }); } - swto.received_reports.insert(op.son_id); + swdo.received_reports.insert(op.son_id); - swto.processed = false; + swdo.processed = false; }); return new_son_wallet_deposit_object.id; } else { - db().modify(*itr, [&op](son_wallet_deposit_object &swto) { - swto.received_reports.insert(op.son_id); + db().modify(*itr, [&op](son_wallet_deposit_object &swdo) { + swdo.received_reports.insert(op.son_id); }); auto stats_itr = db().get_index_type().indices().get().find(op.son_id); db().modify(*stats_itr, [&op](son_statistics_object &sso) { @@ -73,47 +74,8 @@ void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_d 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"); - - const database& d = db(); - - const account_object& from_account = itr->peerplays_to(d); // reversed, for deposit - const account_object& to_account = itr->peerplays_from(d); // reversed, for deposit - const asset_object& asset_type = itr->peerplays_asset.asset_id(d); - - try { - - GRAPHENE_ASSERT( - is_authorized_asset( d, from_account, asset_type ), - transfer_from_account_not_whitelisted, - "'from' account ${from} is not whitelisted for asset ${asset}", - ("from",from_account.id) - ("asset",itr->peerplays_asset.asset_id) - ); - GRAPHENE_ASSERT( - is_authorized_asset( d, to_account, asset_type ), - transfer_to_account_not_whitelisted, - "'to' account ${to} is not whitelisted for asset ${asset}", - ("to",to_account.id) - ("asset",itr->peerplays_asset.asset_id) - ); - - if( asset_type.is_transfer_restricted() ) - { - GRAPHENE_ASSERT( - from_account.id == asset_type.issuer || to_account.id == asset_type.issuer, - transfer_restricted_transfer_asset, - "Asset {asset} has transfer_restricted flag enabled", - ("asset", itr->peerplays_asset.asset_id) - ); - } - - bool insufficient_balance = d.get_balance( from_account, asset_type ).amount >= itr->peerplays_asset.amount; - FC_ASSERT( insufficient_balance, - "Insufficient Balance: ${balance}, unable to transfer '${total_transfer}' from account '${a}' to '${t}'", - ("a",from_account.name)("t",to_account.name)("total_transfer",d.to_pretty_string(itr->peerplays_asset))("balance",d.to_pretty_string(d.get_balance(from_account, asset_type))) ); - - return void_result(); - } FC_RETHROW_EXCEPTIONS( error, "Unable to transfer ${a} from ${f} to ${t}", ("a",d.to_pretty_string(itr->peerplays_asset))("f",from_account.name)("t",to_account.name) ); + FC_ASSERT(!itr->processed, "Son wallet deposit is already processed"); + return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_process_operation& op) @@ -122,17 +84,9 @@ object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_d auto itr = idx.find(op.son_wallet_deposit_id); if(itr != idx.end()) { - if (itr->processed == false) { - db().modify(*itr, [&op](son_wallet_deposit_object &swto) { - swto.processed = true; - }); - - const account_id_type from_account = itr->peerplays_to; // reversed, for deposit - const account_id_type to_account = itr->peerplays_from; // reversed, for deposit - - db().adjust_balance( from_account, -itr->peerplays_asset ); - db().adjust_balance( to_account, itr->peerplays_asset ); - } + db().modify(*itr, [&op](son_wallet_deposit_object &swdo) { + swdo.processed = true; + }); } return op.son_wallet_deposit_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/chain/son_wallet_withdraw_evaluator.cpp b/libraries/chain/son_wallet_withdraw_evaluator.cpp index d0611b7c..7a3930b1 100644 --- a/libraries/chain/son_wallet_withdraw_evaluator.cpp +++ b/libraries/chain/son_wallet_withdraw_evaluator.cpp @@ -73,6 +73,7 @@ void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_ 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(!itr->processed, "Son wallet withdraw is already processed"); return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -82,11 +83,9 @@ object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_ auto itr = idx.find(op.son_wallet_withdraw_id); if(itr != idx.end()) { - if (itr->processed == false) { - db().modify(*itr, [&op](son_wallet_withdraw_object &swto) { - swto.processed = true; - }); - } + db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) { + swwo.processed = true; + }); } return op.son_wallet_withdraw_id; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp index b0484efd..6dd49252 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/defs.hpp @@ -10,16 +10,10 @@ #include #include +#include namespace graphene { namespace peerplays_sidechain { -enum class sidechain_type { - bitcoin, - ethereum, - eos, - peerplays -}; - using bytes = std::vector; struct prev_out { @@ -76,5 +70,3 @@ struct sidechain_event_data { }; }} // namespace graphene::peerplays_sidechain - -FC_REFLECT_ENUM(graphene::peerplays_sidechain::sidechain_type, (bitcoin)(ethereum)(eos)(peerplays)) 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 0d39b855..c69efaf4 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 @@ -5,6 +5,7 @@ #include namespace graphene { namespace peerplays_sidechain { + using namespace chain; namespace detail { @@ -26,8 +27,9 @@ public: std::unique_ptr my; std::set &get_sons(); - son_id_type &get_current_son_id(); - son_object get_son_object(son_id_type son_id); + const son_id_type get_current_son_id(); + const son_object get_current_son_object(); + const son_object get_son_object(son_id_type son_id); bool is_active_son(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); 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 f3a7aa41..5814b208 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 @@ -5,6 +5,11 @@ #include #include +#include +#include +#include +#include +#include #include #include @@ -15,7 +20,7 @@ public: 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(); + sidechain_type get_sidechain(); std::vector get_sidechain_deposit_addresses(); std::vector get_sidechain_withdraw_addresses(); std::string get_private_key(std::string public_key); @@ -23,23 +28,22 @@ public: void sidechain_event_data_received(const sidechain_event_data &sed); void process_deposits(); void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); virtual void recreate_primary_wallet() = 0; - virtual void process_deposit(const son_wallet_deposit_object &swdo) = 0; - virtual void process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; + virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0; + virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; + virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) = 0; + virtual bool send_sidechain_transaction(const sidechain_transaction_object &sto) = 0; protected: peerplays_sidechain_plugin &plugin; graphene::chain::database &database; - graphene::peerplays_sidechain::sidechain_type sidechain; + sidechain_type sidechain; std::map private_keys; - 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; - virtual std::string send_transaction(const std::string &transaction) = 0; - private: }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index ec9689f6..637a5254 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 @@ -7,8 +7,6 @@ #include #include -#include -#include namespace graphene { namespace peerplays_sidechain { @@ -22,23 +20,27 @@ public: class bitcoin_rpc_client { public: bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password); - bool connection_is_not_defined() const; - std::string addmultisigaddress(const std::vector public_keys); + std::string addmultisigaddress(const uint32_t nrequired, const std::vector public_keys); + std::string createpsbt(const std::vector &ins, const fc::flat_map outs); std::string createrawtransaction(const std::vector &ins, const fc::flat_map outs); std::string createwallet(const std::string &wallet_name); + std::string decodepsbt(std::string const &tx_psbt); + std::string decoderawtransaction(std::string const &tx_hex); std::string encryptwallet(const std::string &passphrase); uint64_t estimatesmartfee(); + std::string finalizepsbt(std::string const &tx_psbt); + std::string getaddressinfo(const std::string &address); std::string getblock(const std::string &block_hash, int32_t verbosity = 2); void importaddress(const std::string &address_or_script); std::vector listunspent(); std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount); std::string loadwallet(const std::string &filename); - void sendrawtransaction(const std::string &tx_hex); - std::string signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key); + bool sendrawtransaction(const std::string &tx_hex); std::string signrawtransactionwithwallet(const std::string &tx_hash); std::string unloadwallet(const std::string &filename); std::string walletlock(); + std::string walletprocesspsbt(std::string const &tx_psbt); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); private: @@ -59,9 +61,6 @@ private: class zmq_listener { public: zmq_listener(std::string _ip, uint32_t _zmq); - bool connection_is_not_defined() const { - return zmq_port == 0; - } fc::signal event_received; @@ -84,8 +83,10 @@ public: virtual ~sidechain_net_handler_bitcoin(); void recreate_primary_wallet(); - void process_deposit(const son_wallet_deposit_object &swdo); - void process_withdrawal(const son_wallet_withdraw_object &swwo); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete); + bool send_sidechain_transaction(const sidechain_transaction_object &sto); private: std::string ip; @@ -99,17 +100,24 @@ private: std::unique_ptr bitcoin_client; std::unique_ptr listener; - std::string create_multisignature_wallet(const std::vector public_keys); - std::string transfer(const std::string &from, const std::string &to, const uint64_t amount); - std::string sign_transaction(const std::string &transaction); - std::string send_transaction(const std::string &transaction); - std::string sign_and_send_transaction_with_wallet(const std::string &tx_json); - std::string transfer_all_btc(const std::string &from_address, const std::string &to_address); - std::string transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo); - std::string transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo); + fc::future on_changed_objects_task; + + std::string create_transaction(const std::vector &inputs, const fc::flat_map outputs); + std::string sign_transaction(const std::string &tx, bool &complete); + bool send_transaction(const std::string &tx); + + std::string create_transaction_raw(const std::vector &inputs, const fc::flat_map outputs); + std::string create_transaction_psbt(const std::vector &inputs, const fc::flat_map outputs); + std::string create_transaction_standalone(const std::vector &inputs, const fc::flat_map outputs); + + std::string sign_transaction_raw(const std::string &tx, bool &complete); + std::string sign_transaction_psbt(const std::string &tx, bool &complete); + std::string sign_transaction_standalone(const std::string &tx, bool &complete); void handle_event(const std::string &event_data); std::vector extract_info_from_block(const std::string &_block); + void on_changed_objects(const vector &ids, const flat_set &accounts); + void on_changed_objects_cb(const vector &ids, const flat_set &accounts); }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp index 943c3a90..157dc421 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp @@ -14,15 +14,12 @@ public: virtual ~sidechain_net_handler_peerplays(); void recreate_primary_wallet(); - void process_deposit(const son_wallet_deposit_object &swdo); - void process_withdrawal(const son_wallet_withdraw_object &swwo); + bool process_deposit(const son_wallet_deposit_object &swdo); + bool process_withdrawal(const son_wallet_withdraw_object &swwo); + std::string process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete); + bool send_sidechain_transaction(const sidechain_transaction_object &sto); private: - std::string create_multisignature_wallet(const std::vector public_keys); - std::string transfer(const std::string &from, const std::string &to, const uint64_t amount); - std::string sign_transaction(const std::string &transaction); - std::string send_transaction(const std::string &transaction); - void on_applied_block(const signed_block &b); }; 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 8e246bce..29d9c7f1 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 @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -15,10 +15,12 @@ public: 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); + bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options); void recreate_primary_wallet(); void process_deposits(); void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); private: peerplays_sidechain_plugin &plugin; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 88089580..deda02b3 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -33,8 +33,9 @@ public: void plugin_startup(); std::set &get_sons(); - son_id_type &get_current_son_id(); - son_object get_son_object(son_id_type son_id); + const son_id_type get_current_son_id(); + const son_object get_current_son_object(); + const son_object get_son_object(son_id_type son_id); bool is_active_son(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); @@ -49,6 +50,8 @@ public: void recreate_primary_wallet(); void process_deposits(); void process_withdrawals(); + void process_sidechain_transactions(); + void send_sidechain_transactions(); private: peerplays_sidechain_plugin &plugin; @@ -67,6 +70,7 @@ private: fc::future _heartbeat_task; fc::future _son_processing_task; + bool first_block_skipped; void on_applied_block(const signed_block &b); }; @@ -76,7 +80,8 @@ peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidec config_ready_bitcoin(false), config_ready_peerplays(false), current_son_id(son_id_type(std::numeric_limits().max())), - net_manager(nullptr) { + net_manager(nullptr), + first_block_skipped(false) { } peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() { @@ -110,9 +115,9 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("son-ids", bpo::value(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str()); cli.add_options()("peerplays-private-key", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))), "Tuple of [PublicKey, WIF private key] (may specify multiple times)"); - cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("99.79.189.95"), "IP address of Bitcoin node"); + cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); - cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(22222), "RPC port of Bitcoin node"); + cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); cli.add_options()("bitcoin-node-rpc-password", bpo::value()->default_value("1"), "Bitcoin RPC password"); cli.add_options()("bitcoin-wallet", bpo::value(), "Bitcoin wallet"); @@ -222,11 +227,15 @@ std::set &peerplays_sidechain_plugin_impl::get_sons() { return sons; } -son_id_type &peerplays_sidechain_plugin_impl::get_current_son_id() { +const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() { return current_son_id; } -son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) { +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_son_object(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()) @@ -285,7 +294,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { for (son_id_type son_id : sons) { if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) { - ilog("peerplays_sidechain_plugin: sending heartbeat for SON ${son}", ("son", son_id)); + ilog("Sending heartbeat for SON ${son}", ("son", son_id)); chain::son_heartbeat_operation op; op.owner_account = get_son_object(son_id).son_account; op.son_id = son_id; @@ -298,7 +307,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending heartbeat failed with exception ${e}", ("e", e.what())); + elog("Sending heartbeat failed with exception ${e}", ("e", e.what())); return false; } }); @@ -324,34 +333,56 @@ void peerplays_sidechain_plugin_impl::son_processing() { return; } - chain::son_id_type next_son_id = plugin.database().get_scheduled_son(1); - ilog("peerplays_sidechain_plugin_impl: Scheduled SON ${son}", ("son", next_son_id)); + fc::time_point now_fine = fc::time_point::now(); + fc::time_point_sec now = now_fine + fc::microseconds(500000); + if (plugin.database().get_slot_time(1) < now) { + return; // Not synced + } - // Tasks that are executed by all active SONs, no matter if scheduled - // E.g. sending approvals and signing - approve_proposals(); + 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)); - // Tasks that are executed by scheduled and active SON - if (sons.find(next_son_id) != sons.end()) { + for (son_id_type son_id : plugin.get_sons()) { - current_son_id = next_son_id; + if (plugin.is_active_son(son_id)) { - create_son_down_proposals(); + current_son_id = son_id; - create_son_deregister_proposals(); + // Tasks that are executed by all active SONs, no matter if scheduled + // E.g. sending approvals and signing (only signing that can be done in parallel) + approve_proposals(); - recreate_primary_wallet(); + // Tasks that are executed by scheduled and active SON + if (current_son_id == scheduled_son_id) { - process_deposits(); + create_son_down_proposals(); - process_withdrawals(); + create_son_deregister_proposals(); + + recreate_primary_wallet(); + + process_deposits(); + + process_withdrawals(); + + process_sidechain_transactions(); + + send_sidechain_transactions(); + } + } else { + // Tasks that are executed by previously active SONs + // E.g. sending approvals and signing that SON was required to do while it was active + //approve_leftover_proposals(); ??? + //process_leftover_sidechain_transactions(); ??? + } } } void peerplays_sidechain_plugin_impl::approve_proposals() { auto approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_id_type &proposal_id) { - ilog("peerplays_sidechain_plugin: sending approval for ${p} from ${s}", ("p", proposal_id)("s", son_id)); + ilog("Sending approval for ${p} from ${s}", ("p", proposal_id)("s", son_id)); chain::proposal_update_operation puo; puo.fee_paying_account = get_son_object(son_id).son_account; puo.proposal = proposal_id; @@ -364,7 +395,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending approval failed with exception ${e}", ("e", e.what())); + elog("Sending approval failed with exception ${e}", ("e", e.what())); return false; } }); @@ -378,57 +409,71 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { } for (const auto proposal_id : proposals) { - for (son_id_type son_id : sons) { - if (!is_active_son(son_id)) { + + const object *obj = plugin.database().find_object(proposal_id); + const chain::proposal_object *proposal_ptr = dynamic_cast(obj); + if (proposal_ptr == nullptr) { + continue; + } + const proposal_object proposal = *proposal_ptr; + + if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) { + continue; + } + + if (proposal.proposed_transaction.operations.size() == 1) { + int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which(); + + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - const object *obj = plugin.database().find_object(proposal_id); - const chain::proposal_object *proposal_ptr = dynamic_cast(obj); - if (proposal_ptr == nullptr) { - continue; - } - const proposal_object proposal = *proposal_ptr; - - if (proposal.available_active_approvals.find(get_son_object(son_id).son_account) != proposal.available_active_approvals.end()) { + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); - continue; - } - - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); - continue; - } - - if (proposal.proposed_transaction.operations.size() == 1 && proposal.proposed_transaction.operations[0].which() == chain::operation::tag::value) { - approve_proposal(son_id, proposal.id); + if (op_idx_0 == chain::operation::tag::value) { + approve_proposal(get_current_son_id(), proposal.id); continue; } } + + if (proposal.proposed_transaction.operations.size() == 2) { + int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which(); + int32_t op_idx_1 = proposal.proposed_transaction.operations[1].which(); + + if ((op_idx_0 == chain::operation::tag::value) && + (op_idx_1 == chain::operation::tag::value)) { + approve_proposal(get_current_son_id(), proposal.id); + continue; + } + } + + ilog("=================================================="); + ilog("Proposal not approved ${proposal}", ("proposal", proposal)); + ilog("=================================================="); } } @@ -443,8 +488,8 @@ 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_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.push_back(op_wrapper(son_down_op)); + proposal_op.fee_paying_account = get_current_son_object().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); return proposal_op; @@ -467,7 +512,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { 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))) { - ilog("peerplays_sidechain_plugin: 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)))); + 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([&]() { @@ -477,7 +522,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending son down proposal failed with exception ${e}", ("e", e.what())); + elog("Sending son down proposal failed with exception ${e}", ("e", e.what())); return false; } }); @@ -502,7 +547,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account); if (op.valid()) { // Signing and pushing into the txs to be included in the block - ilog("peerplays_sidechain_plugin: sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id)); + ilog("Sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id)); 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 { @@ -511,7 +556,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { - ilog("peerplays_sidechain_plugin_impl: sending son dereg proposal failed with exception ${e}", ("e", e.what())); + elog("Sending son deregister proposal failed with exception ${e}", ("e", e.what())); return false; } }); @@ -534,11 +579,23 @@ void peerplays_sidechain_plugin_impl::process_withdrawals() { net_manager->process_withdrawals(); } -void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { - schedule_son_processing(); +void peerplays_sidechain_plugin_impl::process_sidechain_transactions() { + net_manager->process_sidechain_transactions(); } -} // end namespace detail +void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { + net_manager->send_sidechain_transactions(); +} + +void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { + if (first_block_skipped) { + schedule_son_processing(); + } else { + first_block_skipped = true; + } +} + +} // namespace detail peerplays_sidechain_plugin::peerplays_sidechain_plugin() : my(new detail::peerplays_sidechain_plugin_impl(*this)) { @@ -559,24 +616,30 @@ void peerplays_sidechain_plugin::plugin_set_program_options( } void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map &options) { - ilog("peerplays sidechain plugin: plugin_initialize()"); + ilog("peerplays sidechain plugin: plugin_initialize() begin"); my->plugin_initialize(options); + ilog("peerplays sidechain plugin: plugin_initialize() end"); } void peerplays_sidechain_plugin::plugin_startup() { - ilog("peerplays sidechain plugin: plugin_startup()"); + ilog("peerplays sidechain plugin: plugin_startup() begin"); my->plugin_startup(); + ilog("peerplays sidechain plugin: plugin_startup() end"); } std::set &peerplays_sidechain_plugin::get_sons() { return my->get_sons(); } -son_id_type &peerplays_sidechain_plugin::get_current_son_id() { +const son_id_type peerplays_sidechain_plugin::get_current_son_id() { return my->get_current_son_id(); } -son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { +const son_object peerplays_sidechain_plugin::get_current_son_object() { + return my->get_current_son_object(); +} + +const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) { return my->get_son_object(son_id); } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index 9d4329b0..c6eccd12 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -1,10 +1,7 @@ #include -#include -#include -#include - #include +#include namespace graphene { namespace peerplays_sidechain { @@ -16,7 +13,7 @@ sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin sidechain_net_handler::~sidechain_net_handler() { } -graphene::peerplays_sidechain::sidechain_type sidechain_net_handler::get_sidechain() { +sidechain_type sidechain_net_handler::get_sidechain() { return sidechain; } @@ -94,17 +91,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + proposal_op.proposed_ops.emplace_back(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 = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { 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)); } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); + elog("Sending proposal for son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } } @@ -140,17 +137,17 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ proposal_create_operation proposal_op; proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + proposal_op.proposed_ops.emplace_back(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 = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), proposal_op); try { 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)); } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); + elog("Sending proposal for son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what())); } } } @@ -161,83 +158,170 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } void sidechain_net_handler::process_deposits() { - const auto &idx = plugin.database().get_index_type().indices().get(); + const auto &idx = database.get_index_type().indices().get(); const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); - std::for_each(idx_range.first, idx_range.second, - [&](const son_wallet_deposit_object &swdo) { - ilog("Deposit to process: ${swdo}", ("swdo", swdo)); + std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { + ilog("Deposit to process: ${swdo}", ("swdo", swdo)); - process_deposit(swdo); + bool process_deposit_result = process_deposit(swdo); - const chain::global_property_object &gpo = plugin.database().get_global_properties(); + if (!process_deposit_result) { + wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); + return; + } - son_wallet_deposit_process_operation p_op; - p_op.payer = gpo.parameters.son_account(); - p_op.son_wallet_deposit_id = swdo.id; + const chain::global_property_object &gpo = database.get_global_properties(); - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(p_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(plugin.database().head_block_time().sec_since_epoch() + lifetime); + son_wallet_deposit_process_operation swdp_op; + swdp_op.payer = gpo.parameters.son_account(); + swdp_op.son_wallet_deposit_id = swdo.id; - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - trx.validate(); - try { - 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)); - } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}", ("e", e.what())); - } - }); + transfer_operation t_op; + t_op.fee = asset(2000000); + t_op.from = swdo.peerplays_to; // gpo.parameters.son_account() + t_op.to = swdo.peerplays_from; + t_op.amount = swdo.peerplays_asset; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(swdp_op); + proposal_op.proposed_ops.emplace_back(t_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); + trx.validate(); + try { + 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)); + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + } + }); } void sidechain_net_handler::process_withdrawals() { - const auto &idx = plugin.database().get_index_type().indices().get(); + const auto &idx = database.get_index_type().indices().get(); const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); - std::for_each(idx_range.first, idx_range.second, - [&](const son_wallet_withdraw_object &swwo) { - ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); + std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { + ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); - process_withdrawal(swwo); + bool process_withdrawal_result = process_withdrawal(swwo); - const chain::global_property_object &gpo = plugin.database().get_global_properties(); + if (!process_withdrawal_result) { + wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); + return; + } - son_wallet_withdraw_process_operation p_op; - p_op.payer = gpo.parameters.son_account(); - p_op.son_wallet_withdraw_id = swwo.id; + const chain::global_property_object &gpo = database.get_global_properties(); - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(p_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(plugin.database().head_block_time().sec_since_epoch() + lifetime); + son_wallet_withdraw_process_operation swwp_op; + swwp_op.payer = gpo.parameters.son_account(); + swwp_op.son_wallet_withdraw_id = swwo.id; - signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - trx.validate(); - try { - 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)); - } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}", ("e", e.what())); - } - }); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(swwp_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); + trx.validate(); + try { + 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)); + } catch (fc::exception e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::process_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + ilog("Sidechain transaction to process: ${sto}", ("sto", sto)); + + bool complete = false; + std::string processed_sidechain_tx = process_sidechain_transaction(sto, complete); + + if (processed_sidechain_tx.empty()) { + wlog("Sidechain transaction not processed: ${sto}", ("sto", sto)); + return; + } + + sidechain_transaction_sign_operation sts_op; + sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.sidechain_transaction_id = sto.id; + sts_op.transaction = processed_sidechain_tx; + sts_op.block = sto.block; + sts_op.complete = complete; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + trx.validate(); + try { + 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)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction sign operation failed with exception ${e}", ("e", e.what())); + } + }); +} + +void sidechain_net_handler::send_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + ilog("Sidechain transaction to send: ${sto}", ("sto", sto)); + + bool sent = send_sidechain_transaction(sto); + + if (!sent) { + wlog("Sidechain transaction not sent: ${sto}", ("sto", sto)); + return; + } + + sidechain_transaction_send_operation sts_op; + sts_op.payer = plugin.get_current_son_object().son_account; + sts_op.sidechain_transaction_id = sto.id; + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); + trx.validate(); + try { + 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)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction send operation failed with exception ${e}", ("e", e.what())); + } + }); } void sidechain_net_handler::recreate_primary_wallet() { FC_ASSERT(false, "recreate_primary_wallet not implemented"); } -void sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) { +bool sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) { FC_ASSERT(false, "process_deposit not implemented"); } -void sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) { +bool sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) { FC_ASSERT(false, "process_withdrawal not implemented"); } +std::string sidechain_net_handler::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) { + FC_ASSERT(false, "process_sidechain_transaction not implemented"); +} + +bool sidechain_net_handler::send_sidechain_transaction(const sidechain_transaction_object &sto) { + FC_ASSERT(false, "send_sidechain_transaction not implemented"); +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index e7b9a70c..a788b04f 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -32,14 +31,10 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::stri authorization.val = "Basic " + fc::base64_encode(user + ":" + password); } -bool bitcoin_rpc_client::connection_is_not_defined() const { - return ip.empty() || rpc_port == 0 || user.empty() || password.empty(); -} - -std::string bitcoin_rpc_client::addmultisigaddress(const std::vector public_keys) { +std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, const std::vector public_keys) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", " "\"method\": \"addmultisigaddress\", \"params\": ["); - std::string params = "2, ["; + std::string params = std::to_string(nrequired) + ", ["; std::string pubkeys = ""; for (std::string pubkey : public_keys) { if (!pubkeys.empty()) { @@ -50,11 +45,11 @@ std::string bitcoin_rpc_client::addmultisigaddress(const std::vector &ins, const fc::flat_map outs) { + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", " + "\"method\": \"createpsbt\", \"params\": ["); + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); + + const auto reply = send_post_request(body, true); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + return json.get("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " "\"method\": \"createrawtransaction\", \"params\": ["); @@ -92,11 +131,11 @@ std::string bitcoin_rpc_client::createrawtransaction(const std::vector("result"); + } + } + + if (json.count("error") && !json.get_child("error").empty()) { wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } - return std::string(); + return ""; } std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { @@ -121,7 +163,63 @@ std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::decodepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decodepsbt\", \"method\": " + "\"decodepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::decoderawtransaction(std::string const &tx_hex) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"decoderawtransaction\", \"method\": " + "\"decoderawtransaction\", \"params\": [\"" + + tx_hex + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -149,7 +247,7 @@ std::string bitcoin_rpc_client::encryptwallet(const std::string &passphrase) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -175,7 +273,7 @@ uint64_t bitcoin_rpc_client::estimatesmartfee() { "\"method\": \"estimatesmartfee\", \"params\": [") + std::to_string(confirmation_target_blocks) + std::string("] }"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -186,13 +284,79 @@ uint64_t bitcoin_rpc_client::estimatesmartfee() { boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - if (json.count("result")) - if (json.get_child("result").count("feerate")) { - auto feerate_str = json.get_child("result").get_child("feerate").get_value(); - feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); - return std::stoll(feerate_str); + if (reply.status == 200) { + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.find("feerate") != json_result.not_found()) { + auto feerate_str = json_result.get("feerate"); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); + } + + if (json_result.find("errors") != json_result.not_found()) { + wlog("Bitcoin RPC call ${function} with body ${body} executed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } } - return 0; + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return 20000; +} + +std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": " + "\"finalizepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body, true); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + +std::string bitcoin_rpc_client::getaddressinfo(const std::string &address) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getaddressinfo\", \"method\": " + "\"getaddressinfo\", \"params\": [\"" + + address + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, json.get_child("result")); + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; } std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { @@ -204,7 +368,7 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -228,7 +392,7 @@ void bitcoin_rpc_client::importaddress(const std::string &address_or_script) { "\"method\": \"importaddress\", \"params\": [") + std::string("\"") + address_or_script + std::string("\"") + std::string("] }"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); @@ -240,7 +404,6 @@ void bitcoin_rpc_client::importaddress(const std::string &address_or_script) { boost::property_tree::read_json(ss, json); if (reply.status == 200) { - idump((address_or_script)(ss.str())); return; } else if (json.count("error") && !json.get_child("error").empty()) { wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); @@ -326,7 +489,7 @@ std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -345,16 +508,16 @@ std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { return ""; } -void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { +bool bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", " "\"method\": \"sendrawtransaction\", \"params\": [") + std::string("\"") + tx_hex + std::string("\"") + std::string("] }"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return; + return false; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -362,18 +525,14 @@ void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { boost::property_tree::read_json(ss, json); if (reply.status == 200) { - return; + return true; } else if (json.count("error") && !json.get_child("error").empty()) { const auto error_code = json.get_child("error").get_child("code").get_value(); if (error_code == -27) // transaction already in block chain - return; - + return true; wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); } -} - -std::string bitcoin_rpc_client::signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key) { - return ""; + return false; } std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) { @@ -382,11 +541,11 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string & std::string params = "\"" + tx_hash + "\""; body = body + params + std::string("]}"); - const auto reply = send_post_request(body); + const auto reply = send_post_request(body, true); if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -412,7 +571,7 @@ std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -439,7 +598,7 @@ std::string bitcoin_rpc_client::walletlock() { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -458,6 +617,32 @@ std::string bitcoin_rpc_client::walletlock() { return ""; } +std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": " + "\"walletprocesspsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body, true); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " "\"walletpassphrase\", \"params\": [\"" + @@ -497,10 +682,10 @@ fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool sho fc::http::reply reply = conn.request("POST", url, body, fc::http::headers{authorization}); if (show_log) { - ilog("Request URL: ${url}", ("url", url)); - ilog("Request: ${body}", ("body", body)); + ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request: ${body}", ("body", body)); std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); - ilog("Response: ${ss}", ("ss", ss.str())); + ilog("### Response: ${ss}", ("ss", ss.str())); } return reply; @@ -603,9 +788,22 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain listener->event_received.connect([this](const std::string &event_data) { std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); }); + + database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { + on_changed_objects(ids, accounts); + }); } sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() { + try { + if (on_changed_objects_task.valid()) { + on_changed_objects_task.cancel_and_wait(__FUNCTION__); + } + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } } void sidechain_net_handler_bitcoin::recreate_primary_wallet() { @@ -623,7 +821,12 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { 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); + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1; + string reply_str = bitcoin_client->addmultisigaddress(nrequired, son_pubkeys_bitcoin); std::stringstream active_pw_ss(reply_str); boost::property_tree::ptree active_pw_pt; @@ -635,13 +838,13 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { son_wallet_update_operation op; op.payer = gpo.parameters.son_account(); - op.son_wallet_id = (*active_sw).id; + op.son_wallet_id = active_sw->id; op.sidechain = sidechain_type::bitcoin; op.address = res.str(); proposal_create_operation proposal_op; - proposal_op.fee_paying_account = plugin.get_son_object(plugin.get_current_son_id()).son_account; - proposal_op.proposed_ops.emplace_back(op_wrapper(op)); + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + proposal_op.proposed_ops.emplace_back(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); @@ -651,7 +854,7 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); } catch (fc::exception e) { - ilog("sidechain_net_handler: sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); + elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); return; } @@ -664,109 +867,80 @@ void sidechain_net_handler_bitcoin::recreate_primary_wallet() { std::string active_pw_address = active_pw_pt.get_child("result").get("address"); std::string prev_pw_address = prev_sw_pt.get("address"); - transfer_all_btc(prev_pw_address, active_pw_address); + if (prev_pw_address == active_pw_address) { + elog("BTC previous and new primary wallet addresses are same. No funds moving needed [from ${prev_sw} to ${active_sw}]", ("prev_sw", prev_sw->id)("active_sw", active_sw->id)); + return; + } + + uint64_t fee_rate = bitcoin_client->estimatesmartfee(); + uint64_t min_fee_rate = 1000; + fee_rate = std::max(fee_rate, min_fee_rate); + + double min_amount = ((double)fee_rate / 100000000.0); // Account only for relay fee for now + double total_amount = 0.0; + std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); + + if (inputs.size() == 0) { + elog("Failed to find UTXOs to spend for ${pw}", ("pw", prev_pw_address)); + return; + } else { + for (const auto &utx : inputs) { + total_amount += utx.amount_; + } + + if (min_amount >= total_amount) { + elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address)); + return; + } + } + + fc::flat_map outputs; + outputs[active_pw_address] = total_amount - min_amount; + + std::string tx_str = create_transaction(inputs, outputs); + + if (!tx_str.empty()) { + + auto signer_sons = prev_sw->sons; + std::vector signers; + for (const son_info &si : signer_sons) { + signers.push_back(si.son_id); + } + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = prev_sw->id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = signers; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().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); + trx.validate(); + try { + 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)); + } catch (fc::exception e) { + elog("Sending proposal for withdrawal sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + } + } } } } } } -void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) { - transfer_deposit_to_primary_wallet(swdo); -} - -void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) { - transfer_withdrawal_from_primary_wallet(swwo); -} - -std::string sidechain_net_handler_bitcoin::create_multisignature_wallet(const std::vector public_keys) { - return bitcoin_client->addmultisigaddress(public_keys); -} - -std::string sidechain_net_handler_bitcoin::transfer(const std::string &from, const std::string &to, const uint64_t amount) { - return ""; -} - -std::string sidechain_net_handler_bitcoin::sign_transaction(const std::string &transaction) { - return ""; -} - -std::string sidechain_net_handler_bitcoin::send_transaction(const std::string &transaction) { - return ""; -} - -std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet(const std::string &tx_json) { - std::string reply_str = tx_json; - - std::stringstream ss_utx(reply_str); - boost::property_tree::ptree pt; - boost::property_tree::read_json(ss_utx, pt); - - if (!(pt.count("error") && pt.get_child("error").empty()) || !pt.count("result")) { - return ""; - } - - if (!wallet_password.empty()) { - bitcoin_client->walletpassphrase(wallet_password, 60); - } - - std::string unsigned_tx_hex = pt.get("result"); - - reply_str = bitcoin_client->signrawtransactionwithwallet(unsigned_tx_hex); - std::stringstream ss_stx(reply_str); - boost::property_tree::ptree stx_json; - boost::property_tree::read_json(ss_stx, stx_json); - - //if (!wallet_password.empty()) { - // bitcoin_client->walletlock(); - //} - - if (!(stx_json.count("error") && stx_json.get_child("error").empty()) || !stx_json.count("result") || !stx_json.get_child("result").count("hex")) { - return ""; - } - - std::string signed_tx_hex = stx_json.get("result.hex"); - - bitcoin_client->sendrawtransaction(signed_tx_hex); - - return reply_str; -} - -std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string &from_address, const std::string &to_address) { - uint64_t fee_rate = bitcoin_client->estimatesmartfee(); - uint64_t min_fee_rate = 1000; - fee_rate = std::max(fee_rate, min_fee_rate); - - double min_amount = ((double)fee_rate / 100000000.0); // Account only for relay fee for now - double total_amount = 0.0; - std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(from_address, 0); - - if (unspent_utxo.size() == 0) { - wlog("Failed to find UTXOs to spend for ${pw}", ("pw", from_address)); - return ""; - } else { - for (const auto &utx : unspent_utxo) { - total_amount += utx.amount_; - } - - if (min_amount >= total_amount) { - wlog("Failed not enough BTC to transfer from ${fa}", ("fa", from_address)); - return ""; - } - } - - fc::flat_map outs; - outs[to_address] = total_amount - min_amount; - - std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); - return sign_and_send_transaction_with_wallet(reply_str); -} - -std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo) { +bool sidechain_net_handler_bitcoin::process_deposit(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()) { - return ""; + return false; } std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -787,26 +961,61 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(co deposit_amount -= fee_rate; // Deduct minimum relay fee double transfer_amount = (double)deposit_amount / 100000000.0; - std::vector ins; - fc::flat_map outs; + std::vector inputs; + fc::flat_map outputs; btc_txout utxo; utxo.txid_ = txid; utxo.out_num_ = std::stoul(nvout); - ins.push_back(utxo); + inputs.push_back(utxo); - outs[pw_address] = transfer_amount; + outputs[pw_address] = transfer_amount; - std::string reply_str = bitcoin_client->createrawtransaction(ins, outs); - return sign_and_send_transaction_with_wallet(reply_str); + std::string tx_str = create_transaction(inputs, outputs); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + std::vector signers; + for (const son_info &si : active_sons) { + signers.push_back(si.son_id); + } + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swdo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = signers; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().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); + trx.validate(); + try { + 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)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; } -std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo) { +bool sidechain_net_handler_bitcoin::process_withdrawal(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()) { - return ""; + return false; } std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second; @@ -823,30 +1032,267 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall double min_amount = ((double)(swwo.withdraw_amount.value + fee_rate) / 100000000.0); // Account only for relay fee for now double total_amount = 0.0; - std::vector unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); + std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); - if (unspent_utxo.size() == 0) { - wlog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); + if (inputs.size() == 0) { + elog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); return ""; } else { - for (const auto &utx : unspent_utxo) { + for (const auto &utx : inputs) { total_amount += utx.amount_; } if (min_amount > total_amount) { - wlog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); + elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address)); return ""; } } - fc::flat_map outs; - outs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0; + fc::flat_map outputs; + outputs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0; if ((total_amount - min_amount) > 0.0) { - outs[pw_address] = total_amount - min_amount; + outputs[pw_address] = total_amount - min_amount; } - std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); - return sign_and_send_transaction_with_wallet(reply_str); + std::string tx_str = create_transaction(inputs, outputs); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + auto active_sons = gpo.active_sons; + std::vector signers; + for (const son_info &si : active_sons) { + signers.push_back(si.son_id); + } + + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = swwo.id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = signers; + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().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); + trx.validate(); + try { + 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)); + return true; + } catch (fc::exception e) { + elog("Sending proposal for withdraw sidechain transaction create operation failed with exception ${e}", ("e", e.what())); + return false; + } + } + return false; +} + +std::string sidechain_net_handler_bitcoin::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) { + + //// Uncomment to get signing in order from sto.signers + //son_id_type invalid_signer = son_id_type(0xFFFFFFFF); + //son_id_type next_signer = invalid_signer; + //for (auto &signer : sto.signers) { + // if (signer.second == false) { + // next_signer = signer.first; + // break; + // } + //} + // + //if ((next_signer == invalid_signer) || (next_signer != plugin.get_current_son_id())) { + // return ""; + //} + + return sign_transaction(sto.transaction, complete); +} + +bool sidechain_net_handler_bitcoin::send_sidechain_transaction(const sidechain_transaction_object &sto) { + return send_transaction(sto.transaction); +} + +// Creates transaction in any format +// Function to actually create transaction should return transaction string, or empty string in case of failure +std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector &inputs, const fc::flat_map outputs) { + std::string new_tx = ""; + //new_tx = create_transaction_raw(inputs, outputs); + new_tx = create_transaction_psbt(inputs, outputs); + //new_tx = create_transaction_standalone(inputs, outputs); + return new_tx; +} + +// Adds signature to transaction +// Function to actually add signature should return transaction with added signature string, or empty string in case of failure +std::string sidechain_net_handler_bitcoin::sign_transaction(const std::string &tx_str, bool &complete) { + std::string new_tx = ""; + //new_tx = sign_transaction_raw(tx, complete); + new_tx = sign_transaction_psbt(tx_str, complete); + //new_tx = sign_transaction_standalone(tx, complete); + return new_tx; +} + +bool sidechain_net_handler_bitcoin::send_transaction(const std::string &tx_str) { + return bitcoin_client->sendrawtransaction(tx_str); +} + +std::string sidechain_net_handler_bitcoin::create_transaction_raw(const std::vector &inputs, const fc::flat_map outputs) { + return bitcoin_client->createrawtransaction(inputs, outputs); +} + +std::string sidechain_net_handler_bitcoin::create_transaction_psbt(const std::vector &inputs, const fc::flat_map outputs) { + return bitcoin_client->createpsbt(inputs, outputs); +} + +std::string sidechain_net_handler_bitcoin::create_transaction_standalone(const std::vector &inputs, const fc::flat_map outputs) { + // Examples + + // Transaction with no inputs and outputs + //bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" createrawtransaction '[]' '[]' + //02000000000000000000 + //bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" decoderawtransaction 02000000000000000000 + //{ + // "txid": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", + // "hash": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a", + // "version": 2, + // "size": 10, + // "vsize": 10, + // "weight": 40, + // "locktime": 0, + // "vin": [ + // ], + // "vout": [ + // ] + //} + + // Transaction with input and output + //{ + // "txid": "ff60f48f767bbf70d79efc1347b5554b481f14fda68709839091286e035e669b", + // "hash": "ff60f48f767bbf70d79efc1347b5554b481f14fda68709839091286e035e669b", + // "version": 2, + // "size": 83, + // "vsize": 83, + // "weight": 332, + // "locktime": 0, + // "vin": [ + // { + // "txid": "3d322dc2640239a2e68e182b254d19c88e5172a61947f94a105c3f57618092ff", + // "vout": 0, + // "scriptSig": { + // "asm": "", + // "hex": "" + // }, + // "sequence": 4294967295 + // } + // ], + // "vout": [ + // { + // "value": 1.00000000, + // "n": 0, + // "scriptPubKey": { + // "asm": "OP_HASH160 b87c323018cae236eb03a1f63000c85b672270f6 OP_EQUAL", + // "hex": "a914b87c323018cae236eb03a1f63000c85b672270f687", + // "reqSigs": 1, + // "type": "scripthash", + // "addresses": [ + // "2NA4h6sc9oZ4ogfNKU9Wp6fkqPZLZPqqpgf" + // ] + // } + // } + // ] + //} + + return ""; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction_raw(const std::string &tx_str, bool &complete) { + if (tx_str.empty()) { + elog("Signing failed, tx string is empty"); + return ""; + } + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + + std::string reply_str = bitcoin_client->signrawtransactionwithwallet(tx_str); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) { + elog("Failed to process raw transaction ${tx}", ("tx", tx_str)); + return ""; + } + + std::string new_tx_raw = json_res.get("hex"); + bool complete_raw = json_res.get("complete"); + + if (complete_raw) { + complete = true; + return new_tx_raw; + } + return new_tx_raw; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction_psbt(const std::string &tx_str, bool &complete) { + if (tx_str.empty()) { + elog("Signing failed, tx string is empty"); + return ""; + } + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + + std::string reply_str = bitcoin_client->walletprocesspsbt(tx_str); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("psbt") == 0) || (json_res.count("complete") == 0)) { + elog("Failed to process psbt transaction ${tx}", ("tx", tx_str)); + return ""; + } + + std::string new_tx_psbt = json_res.get("psbt"); + bool complete_psbt = json_res.get("complete"); + + if (complete_psbt) { + std::string reply_str = bitcoin_client->finalizepsbt(new_tx_psbt); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) { + elog("Failed to finalize psbt transaction ${tx}", ("tx", tx_str)); + return ""; + } + + std::string new_tx_raw = json_res.get("hex"); + bool complete_raw = json_res.get("complete"); + + if (complete_raw) { + complete = true; + return new_tx_raw; + } + } + return new_tx_psbt; +} + +std::string sidechain_net_handler_bitcoin::sign_transaction_standalone(const std::string &tx_str, bool &complete) { + complete = true; + return ""; } void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { @@ -867,7 +1313,7 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) std::string sidechain_uid = ss.str(); sidechain_event_data sed; - sed.timestamp = plugin.database().head_block_time(); + sed.timestamp = database.head_block_time(); sed.sidechain = addr_itr->sidechain; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = v.out.hash_tx; @@ -916,6 +1362,55 @@ std::vector sidechain_net_handler_bitcoin::extract_info_from_block return result; } +void sidechain_net_handler_bitcoin::on_changed_objects(const vector &ids, const flat_set &accounts) { + fc::time_point now = fc::time_point::now(); + int64_t time_to_next_changed_objects_processing = 5000; + + fc::time_point next_wakeup(now + fc::microseconds(time_to_next_changed_objects_processing)); + + on_changed_objects_task = fc::schedule([this, ids, accounts] { + on_changed_objects_cb(ids, accounts); + }, + next_wakeup, "SON Processing"); +} + +void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector &ids, const flat_set &accounts) { + for (auto id : ids) { + if (id.is()) { + const auto &swi = database.get_index_type().indices().get(); + auto swo = swi.find(id); + if (swo != swi.end()) { + std::stringstream pw_ss(swo->addresses.at(sidechain)); + boost::property_tree::ptree pw_pt; + boost::property_tree::read_json(pw_ss, pw_pt); + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 5); + } + + std::string pw_address = ""; + if (pw_pt.count("address")) { + pw_address = pw_pt.get("address"); + bitcoin_client->importaddress(pw_address); + } + + std::string pw_redeem_script = ""; + if (pw_pt.count("redeemScript")) { + pw_redeem_script = pw_pt.get("redeemScript"); + bitcoin_client->importaddress(pw_redeem_script); + } + + vector son_pubkeys_bitcoin; + for (const son_info &si : swo->sons) { + son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin)); + } + uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1; + bitcoin_client->addmultisigaddress(nrequired, son_pubkeys_bitcoin); + } + } + } +} + // ============================================================================= }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index 18f60b7a..8dd39b22 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include @@ -22,7 +21,7 @@ namespace graphene { namespace peerplays_sidechain { sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::peerplays; - plugin.database().applied_block.connect([&](const signed_block &b) { + database.applied_block.connect([&](const signed_block &b) { on_applied_block(b); }); } @@ -31,28 +30,24 @@ sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() { } void sidechain_net_handler_peerplays::recreate_primary_wallet() { + return; } -void sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { +bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { + return true; } -void sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { +bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { + return true; } -std::string sidechain_net_handler_peerplays::create_multisignature_wallet(const std::vector public_keys) { - return ""; +std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto, bool &complete) { + complete = true; + return sto.transaction; } -std::string sidechain_net_handler_peerplays::transfer(const std::string &from, const std::string &to, const uint64_t amount) { - return ""; -} - -std::string sidechain_net_handler_peerplays::sign_transaction(const std::string &transaction) { - return ""; -} - -std::string sidechain_net_handler_peerplays::send_transaction(const std::string &transaction) { - return ""; +bool sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) { + return true; } void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { @@ -72,18 +67,18 @@ void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { std::string sidechain_uid = ss.str(); sidechain_event_data sed; - sed.timestamp = plugin.database().head_block_time(); + sed.timestamp = database.head_block_time(); sed.sidechain = sidechain_type::peerplays; sed.sidechain_uid = sidechain_uid; sed.sidechain_transaction_id = trx.id().str(); sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance); sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance); - sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(plugin.database()).symbol; + sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance); //transfer_op.amount.asset_id(database).symbol; sed.sidechain_amount = transfer_op.amount.amount; sed.peerplays_from = transfer_op.from; sed.peerplays_to = transfer_op.to; // We should calculate exchange rate between CORE/TEST and other Peerplays asset - sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(plugin.database()).options.core_exchange_rate.quote.amount); + sed.peerplays_asset = asset(transfer_op.amount.amount / transfer_op.amount.asset_id(database).options.core_exchange_rate.quote.amount); sidechain_event_data_received(sed); } } diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index 05b6b5b8..826e5d91 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -15,7 +15,7 @@ sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin sidechain_net_manager::~sidechain_net_manager() { } -bool sidechain_net_manager::create_handler(peerplays_sidechain::sidechain_type sidechain, const boost::program_options::variables_map &options) { +bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) { bool ret_val = false; @@ -57,4 +57,16 @@ void sidechain_net_manager::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(); + } +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index aca8c04f..ea2434f6 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -73,7 +73,7 @@ #include #include -#include +#include #include #include diff --git a/tests/tests/sidechain_addresses_test.cpp b/tests/tests/sidechain_addresses_test.cpp index bee7cec8..e65cf11e 100644 --- a/tests/tests/sidechain_addresses_test.cpp +++ b/tests/tests/sidechain_addresses_test.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using namespace graphene::chain; using namespace graphene::chain::test; diff --git a/tests/tests/sidechain_transaction_tests.cpp b/tests/tests/sidechain_transaction_tests.cpp deleted file mode 100644 index d1132605..00000000 --- a/tests/tests/sidechain_transaction_tests.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include - -#include "../common/database_fixture.hpp" - -#include -#include -#include -#include -#include - -using namespace graphene; -using namespace graphene::chain; -using namespace graphene::chain::test; - -BOOST_FIXTURE_TEST_SUITE(sidechain_transaction_tests, database_fixture) - -BOOST_AUTO_TEST_CASE(bitcoin_transaction_send_test) -{ - - try - { - BOOST_TEST_MESSAGE("bitcoin_transaction_send_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(); - - generate_block(); - - const auto& acc_idx = db.get_index_type().indices().get(); - auto acc_itr = acc_idx.find(db.get_global_properties().parameters.son_account()); - BOOST_REQUIRE(acc_itr != acc_idx.end()); - db.modify(*acc_itr, [&](account_object &obj) { - obj.active.account_auths.clear(); - obj.active.add_authority(bob_id, 1); - obj.active.add_authority(alice_id, 1); - obj.active.weight_threshold = 2; - obj.owner.account_auths.clear(); - obj.owner.add_authority(bob_id, 1); - obj.owner.add_authority(alice_id, 1); - obj.owner.weight_threshold = 2; - }); - - /*const auto &son_btc_account = db.create([&](account_object &obj) { - obj.name = "son_btc_account"; - obj.statistics = db.create([&](account_statistics_object &acc_stat) { acc_stat.owner = obj.id; }).id; - obj.membership_expiration_date = time_point_sec::maximum(); - obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; - - obj.owner.add_authority(bob_id, 1); - obj.active.add_authority(bob_id, 1); - obj.owner.add_authority(alice_id, 1); - obj.active.add_authority(alice_id, 1); - obj.active.weight_threshold = 2; - obj.owner.weight_threshold = 2; - }); - - db.modify( db.get_global_properties(), [&]( global_property_object& _gpo ) - { - _gpo.parameters.extensions.value.son_btc_account = son_btc_account.get_id(); - if( _gpo.pending_parameters ) - _gpo.pending_parameters->extensions.value.son_btc_account = son_btc_account.get_id(); - });*/ - - generate_block(); - - const global_property_object &gpo = db.get_global_properties(); - - { - BOOST_TEST_MESSAGE("Send bitcoin_transaction_send_operation"); - - bitcoin_transaction_send_operation send_op; - - send_op.payer = gpo.parameters.son_account(); - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = alice_id; - proposal_op.proposed_ops.push_back(op_wrapper(send_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime); - - trx.operations.push_back(proposal_op); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - - BOOST_TEST_MESSAGE("Check proposal results"); - - const auto &idx = db.get_index_type().indices().get(); - BOOST_REQUIRE(idx.size() == 1); - auto obj = idx.find(proposal_id_type(0)); - BOOST_REQUIRE(obj != idx.end()); - - const auto& btidx = db.get_index_type().indices().get(); - BOOST_REQUIRE(btidx.size() == 0); - - std::vector a1 = {'a', 'l', 'i', 'c', 'e', '1'}; - std::vector a2 = {'a', 'l', 'i', 'c', 'e', '2'}; - std::vector a3 = {'a', 'l', 'i', 'c', 'e', '3'}; - - { - BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation"); - - bitcoin_transaction_sign_operation sign_op; - - sign_op.payer = alice_id; - sign_op.proposal_id = proposal_id_type(0); - sign_op.signatures.push_back(a1); - sign_op.signatures.push_back(a2); - sign_op.signatures.push_back(a3); - - trx.operations.push_back(sign_op); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - - BOOST_REQUIRE(idx.size() == 1); - BOOST_REQUIRE(btidx.size() == 0); - auto pobj = idx.find(proposal_id_type(0)); - BOOST_REQUIRE(pobj != idx.end()); - - const auto& sidx = db.get_index_type().indices().get(); - const auto son_obj1 = sidx.find( alice_id ); - BOOST_REQUIRE(son_obj1 != sidx.end()); - - auto bitcoin_transaction_send_op = pobj->proposed_transaction.operations[0].get(); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures.size() == 1); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][0] == a1); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][1] == a2); - BOOST_REQUIRE(bitcoin_transaction_send_op.signatures[son_obj1->id][2] == a3); - - std::vector b1 = {'b', 'o', 'b', '1'}; - std::vector b2 = {'b', 'o', 'b', '2'}; - std::vector b3 = {'b', 'o', 'b', '3'}; - - { - BOOST_TEST_MESSAGE("Send bitcoin_transaction_sign_operation"); - - bitcoin_transaction_sign_operation sign_op; - - sign_op.payer = bob_id; - sign_op.proposal_id = proposal_id_type(0); - sign_op.signatures.push_back(b1); - sign_op.signatures.push_back(b2); - sign_op.signatures.push_back(b3); - - trx.operations.push_back(sign_op); - set_expiration(db, trx); - sign(trx, bob_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - - BOOST_REQUIRE(idx.size() == 0); - - const auto son_obj2 = sidx.find( bob_id ); - BOOST_REQUIRE(son_obj2 != sidx.end()); - - BOOST_REQUIRE(btidx.size() == 1); - - const auto btobj = btidx.find(bitcoin_transaction_id_type(0)); - BOOST_REQUIRE(btobj != btidx.end()); - - BOOST_REQUIRE(btobj->processed == false); - - auto stats1 = son_obj1->statistics( db ); - auto stats2 = son_obj2->statistics( db ); - - BOOST_REQUIRE(stats1.txs_signed == 1); - BOOST_REQUIRE(stats2.txs_signed == 1); - - auto sigs = btobj->signatures; - - BOOST_REQUIRE(sigs[son_obj1->id][0] == a1); - BOOST_REQUIRE(sigs[son_obj1->id][1] == a2); - BOOST_REQUIRE(sigs[son_obj1->id][2] == a3); - - BOOST_REQUIRE(sigs[son_obj2->id][0] == b1); - BOOST_REQUIRE(sigs[son_obj2->id][1] == b2); - BOOST_REQUIRE(sigs[son_obj2->id][2] == b3); - - { - BOOST_TEST_MESSAGE("Send bitcoin_send_transaction_process_operation"); - - bitcoin_send_transaction_process_operation process_op; - process_op.bitcoin_transaction_id = bitcoin_transaction_id_type(0); - process_op.payer = db.get_global_properties().parameters.son_account(); - - proposal_create_operation proposal_op; - proposal_op.fee_paying_account = alice_id; - proposal_op.proposed_ops.push_back(op_wrapper(process_op)); - uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3; - proposal_op.expiration_time = time_point_sec(db.head_block_time().sec_since_epoch() + lifetime); - - trx.operations.push_back(proposal_op); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - - generate_block(); - BOOST_REQUIRE(idx.size() == 1); - obj = idx.find(proposal_id_type(1)); - BOOST_REQUIRE(obj != idx.end()); - - - { - BOOST_TEST_MESSAGE("Send proposal_update_operation"); - - proposal_update_operation puo; - puo.fee_paying_account = bob_id; - puo.proposal = obj->id; - puo.active_approvals_to_add = { bob_id }; - - trx.operations.push_back(puo); - set_expiration(db, trx); - sign(trx, bob_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - BOOST_REQUIRE(idx.size() == 1); - obj = idx.find(proposal_id_type(1)); - BOOST_REQUIRE(obj != idx.end()); - - BOOST_REQUIRE(btobj != btidx.end()); - BOOST_REQUIRE(btobj->processed == false); - - { - BOOST_TEST_MESSAGE("Send proposal_update_operation"); - - proposal_update_operation puo; - puo.fee_paying_account = alice_id; - puo.proposal = obj->id; - puo.active_approvals_to_add = { alice_id }; - - trx.operations.push_back(puo); - set_expiration(db, trx); - sign(trx, alice_private_key); - PUSH_TX(db, trx, ~0); - trx.clear(); - } - generate_block(); - BOOST_REQUIRE(idx.size() == 0); - - BOOST_REQUIRE(btobj != btidx.end()); - BOOST_REQUIRE(btobj->processed == true); - } - FC_LOG_AND_RETHROW() -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/son_wallet_tests.cpp b/tests/tests/son_wallet_tests.cpp index 39e53269..7ef3abc0 100644 --- a/tests/tests/son_wallet_tests.cpp +++ b/tests/tests/son_wallet_tests.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include using namespace graphene; using namespace graphene::chain;