diff --git a/docs b/docs index 8d8b69d8..740407c2 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 8d8b69d82482101279460fa02f814d0e4030966f +Subproject commit 740407c22154634aa0881e6b85e19ad0191e8325 diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 13f4fd4f..7d3cb267 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -272,6 +272,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); } void database::initialize_indexes() diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index f38225e0..1950eaff 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -347,6 +347,9 @@ struct get_impacted_account_visitor void operator()( const sidechain_transaction_send_operation& op ) { _impacted.insert( op.payer ); } + void operator()( const sidechain_transaction_settle_operation& op ) { + _impacted.insert( op.payer ); + } }; void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index e76ef082..f2ced8f7 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -159,7 +159,8 @@ namespace graphene { namespace chain { sidechain_address_delete_operation, sidechain_transaction_create_operation, sidechain_transaction_sign_operation, - sidechain_transaction_send_operation + sidechain_transaction_send_operation, + sidechain_transaction_settle_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp index 3eb02623..dda3298f 100644 --- a/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/sidechain_transaction.hpp @@ -50,6 +50,19 @@ namespace graphene { namespace chain { share_type calculate_fee( const fee_parameters_type& k )const { return 0; } }; + struct sidechain_transaction_settle_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type payer; + + sidechain_transaction_id_type sidechain_transaction_id; + + account_id_type fee_payer()const { return payer; } + share_type calculate_fee( const fee_parameters_type& k )const { return 0; } + }; + } } // graphene::chain FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) ) @@ -68,3 +81,7 @@ FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameter FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer) (sidechain_transaction_id) (sidechain_transaction) ) + +FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation, (fee)(payer) + (sidechain_transaction_id) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp index 3bc64bfa..d0af68fb 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_evaluator.hpp @@ -31,4 +31,13 @@ public: object_id_type do_apply(const sidechain_transaction_send_operation& o); }; +class sidechain_transaction_settle_evaluator : public evaluator +{ +public: + typedef sidechain_transaction_settle_operation operation_type; + + void_result do_evaluate(const sidechain_transaction_settle_operation& o); + object_id_type do_apply(const sidechain_transaction_settle_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 984a25a6..e9011ffb 100644 --- a/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_transaction_object.hpp @@ -7,6 +7,14 @@ namespace graphene { namespace chain { using namespace graphene::db; + enum class sidechain_transaction_status { + invalid, + valid, + complete, + sent, + settled + }; + /** * @class sidechain_transaction_object * @brief tracks state of sidechain transaction during signing process. @@ -28,14 +36,12 @@ namespace graphene { namespace chain { uint32_t total_weight = 0; uint32_t current_weight = 0; uint32_t threshold = 0; - bool valid = false; - bool complete = false; - bool sent = false; + + sidechain_transaction_status status; }; struct by_object_id; - struct by_sidechain_and_complete; - struct by_sidechain_and_complete_and_sent; + struct by_sidechain_and_status; using sidechain_transaction_multi_index_type = multi_index_container< sidechain_transaction_object, indexed_by< @@ -45,17 +51,10 @@ namespace graphene { namespace chain { ordered_unique< tag, member >, - ordered_non_unique< tag, + ordered_non_unique< tag, composite_key, - member - > - >, - ordered_non_unique< tag, - composite_key, - member, - member + member > > > @@ -63,6 +62,13 @@ namespace graphene { namespace chain { using sidechain_transaction_index = generic_index; } } // graphene::chain +FC_REFLECT_ENUM( graphene::chain::sidechain_transaction_status, + (invalid) + (valid) + (complete) + (sent) + (settled) ) + FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ), (sidechain) (object_id) @@ -73,6 +79,4 @@ FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db (total_weight) (current_weight) (threshold) - (valid) - (complete) - (sent) ) + (status) ) diff --git a/libraries/chain/sidechain_transaction_evaluator.cpp b/libraries/chain/sidechain_transaction_evaluator.cpp index 570211dd..124b050d 100644 --- a/libraries/chain/sidechain_transaction_evaluator.cpp +++ b/libraries/chain/sidechain_transaction_evaluator.cpp @@ -41,9 +41,7 @@ object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_ sto.sidechain_transaction = ""; sto.current_weight = 0; sto.threshold = sto.total_weight * 2 / 3 + 1; - sto.valid = true; - sto.complete = false; - sto.sent = false; + sto.status = sidechain_transaction_status::valid; }); return new_sidechain_transaction_object.id; } FC_CAPTURE_AND_RETHROW( ( op ) ) } @@ -68,9 +66,7 @@ void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_tr } FC_ASSERT(expected, "Signer not expected"); - FC_ASSERT(sto_obj->valid, "Transaction not valid"); - FC_ASSERT(!sto_obj->complete, "Transaction signing completed"); - FC_ASSERT(!sto_obj->sent, "Transaction already sent"); + FC_ASSERT(sto_obj->status == sidechain_transaction_status::valid, "Invalid transaction status"); return void_result(); } FC_CAPTURE_AND_RETHROW( ( op ) ) } @@ -94,7 +90,9 @@ object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_tr sto.current_weight = sto.current_weight + sto.signers.at(i).weight; } } - sto.complete = (sto.current_weight >= sto.threshold); + if (sto.current_weight >= sto.threshold) { + sto.status = sidechain_transaction_status::complete; + } }); db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) { @@ -112,9 +110,7 @@ void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_tr 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"); + FC_ASSERT(sto_obj->status == sidechain_transaction_status::complete, "Invalid transaction status"); return void_result(); } FC_CAPTURE_AND_RETHROW( ( op ) ) } @@ -126,7 +122,33 @@ object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_tr db().modify(*sto_obj, [&](sidechain_transaction_object &sto) { sto.sidechain_transaction = op.sidechain_transaction; - sto.sent = true; + sto.status = sidechain_transaction_status::sent; + }); + + return op.sidechain_transaction_id; +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + + +void_result sidechain_transaction_settle_evaluator::do_evaluate(const sidechain_transaction_settle_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->status == sidechain_transaction_status::sent, "Invalid transaction status"); + + return void_result(); +} FC_CAPTURE_AND_RETHROW( ( op ) ) } + +object_id_type sidechain_transaction_settle_evaluator::do_apply(const sidechain_transaction_settle_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.status = sidechain_transaction_status::settled; }); return op.sidechain_transaction_id; diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index 61e27b94..3e37b6fe 100755 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -15,12 +15,26 @@ add_library( peerplays_sidechain bitcoin/sign_bitcoin_transaction.cpp ) -if (SUPPORT_MULTIPLE_SONS) - message ("Multiple SONs per software instance are supported") - target_compile_definitions(peerplays_sidechain PRIVATE SUPPORT_MULTIPLE_SONS) +if (ENABLE_DEV_FEATURES) + set(ENABLE_MULTIPLE_SONS 1) + set(ENABLE_PEERPLAYS_ASSET_DEPOSITS 1) endif() -unset(SUPPORT_MULTIPLE_SONS) -unset(SUPPORT_MULTIPLE_SONS CACHE) +unset(ENABLE_DEV_FEATURES) +unset(ENABLE_DEV_FEATURES CACHE) + +if (ENABLE_MULTIPLE_SONS) + message ("Multiple SONs per software instance are supported") + target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_MULTIPLE_SONS) +endif() +unset(ENABLE_MULTIPLE_SONS) +unset(ENABLE_MULTIPLE_SONS CACHE) + +if (ENABLE_PEERPLAYS_ASSET_DEPOSITS) + message ("Depositing Peerplays assets enabled") + target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_PEERPLAYS_ASSET_DEPOSITS) +endif() +unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS) +unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE) target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq ) target_include_directories( peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index 28e2dff9..dc47beda 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 @@ -35,6 +35,7 @@ public: void process_withdrawals(); void process_sidechain_transactions(); void send_sidechain_transactions(); + void settle_sidechain_transactions(); virtual bool process_proposal(const proposal_object &po) = 0; virtual void process_primary_wallet() = 0; @@ -43,6 +44,7 @@ public: virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0; virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto) = 0; virtual std::string send_sidechain_transaction(const sidechain_transaction_object &sto) = 0; + virtual int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto) = 0; protected: peerplays_sidechain_plugin &plugin; 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 03c6cb6c..60d0262a 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 @@ -92,6 +92,7 @@ public: bool process_withdrawal(const son_wallet_withdraw_object &swwo); std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); + int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto); private: std::string ip; 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 342d5094..aa094d95 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 @@ -20,6 +20,7 @@ public: bool process_withdrawal(const son_wallet_withdraw_object &swwo); std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); + int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto); private: 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 3e98c2b1..cbdc344f 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 @@ -22,6 +22,7 @@ public: void process_withdrawals(); void process_sidechain_transactions(); void send_sidechain_transactions(); + void settle_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 491d516b..24320845 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -58,6 +58,7 @@ public: void process_withdrawals(); void process_sidechain_transactions(); void send_sidechain_transactions(); + void settle_sidechain_transactions(); private: peerplays_sidechain_plugin &plugin; @@ -142,8 +143,10 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt boost::insert(sons, fc::json::from_string(options.at("son-ids").as()).as>(5)); config_ready_son = config_ready_son && !sons.empty(); -#ifndef SUPPORT_MULTIPLE_SONS - FC_ASSERT(sons.size() == 1, "Multiple SONs not supported"); +#ifndef ENABLE_MULTIPLE_SONS + if (sons.size() > 1) { + FC_THROW("Invalid configuration, multiple SON IDs provided"); + } #endif if (options.count("peerplays-private-key")) { @@ -343,6 +346,7 @@ void peerplays_sidechain_plugin_impl::heartbeat_loop() { chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op); fc::future fut = fc::async([&]() { try { + trx.validate(); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -414,6 +418,8 @@ void peerplays_sidechain_plugin_impl::son_processing() { process_sidechain_transactions(); send_sidechain_transactions(); + + settle_sidechain_transactions(); } } } @@ -450,6 +456,7 @@ void peerplays_sidechain_plugin_impl::approve_proposals() { chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo); fc::future fut = fc::async([&]() { try { + trx.validate(); 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)); @@ -525,6 +532,7 @@ void peerplays_sidechain_plugin_impl::create_son_down_proposals() { chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op); fc::future fut = fc::async([&]() { try { + trx.validate(); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -559,6 +567,7 @@ void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() { chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op); fc::future fut = fc::async([&]() { try { + trx.validate(); d.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -599,6 +608,10 @@ void peerplays_sidechain_plugin_impl::send_sidechain_transactions() { net_manager->send_sidechain_transactions(); } +void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() { + net_manager->settle_sidechain_transactions(); +} + void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) { if (first_block_skipped) { schedule_son_processing(); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index b7f7ba5d..b769ddfa 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -92,11 +92,6 @@ bool sidechain_net_handler::proposal_exists(int32_t operation_tag, const object_ break; } - case chain::operation::tag::value: { - result = (op_obj_idx_0.get().object_id == object_id); - break; - } - default: return false; } @@ -116,11 +111,12 @@ bool sidechain_net_handler::approve_proposal(const proposal_id_type &proposal_id op.proposal = proposal_id; op.active_approvals_to_add = {plugin.get_son_object(son_id).son_account}; - signed_transaction tx = database.create_signed_transaction(plugin.get_private_key(son_id), op); + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op); try { - database.push_transaction(tx, database::validation_steps::skip_block_size_check); + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) - plugin.app().p2p_node()->broadcast(net::trx_message(tx)); + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); return true; } catch (fc::exception e) { elog("Sending approval from ${son_id} for proposal ${proposal_id} failed with exception ${e}", @@ -151,8 +147,18 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ fc::to_string(btc_asset_id.type_id) + "." + fc::to_string((uint64_t)btc_asset_id.instance); +#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS + // Accepts BTC and peerplays asset deposits + bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) != 0)); + bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0)); +#else + // Accepts BTC deposits only + bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("BTC") == 0)); + bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0)); +#endif + // Deposit request - if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) != 0)) { + if (deposit_condition) { for (son_id_type son_id : plugin.get_sons()) { if (plugin.is_active_son(son_id)) { @@ -175,6 +181,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op); try { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -187,7 +194,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } // Withdrawal request - if ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0)) { + if (withdraw_condition) { // BTC Payout only (for now) const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin)); @@ -216,6 +223,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op); try { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -226,8 +234,6 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_ } return; } - - FC_ASSERT(false, "Invalid sidechain event"); } void sidechain_net_handler::process_proposals() { @@ -242,8 +248,6 @@ void sidechain_net_handler::process_proposals() { const auto po = idx.find(proposal_id); if (po != idx.end()) { - ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", (*po).id)("son_id", plugin.get_current_son_id())); - if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) { continue; } @@ -258,6 +262,14 @@ void sidechain_net_handler::process_proposals() { op_obj_idx_0 = po->proposed_transaction.operations[0]; } + int32_t op_idx_1 = -1; + chain::operation op_obj_idx_1; + + if (po->proposed_transaction.operations.size() >= 2) { + op_idx_1 = po->proposed_transaction.operations[1].which(); + op_obj_idx_1 = po->proposed_transaction.operations[1]; + } + switch (op_idx_0) { case chain::operation::tag::value: { should_process = (op_obj_idx_0.get().sidechain == sidechain); @@ -279,14 +291,18 @@ void sidechain_net_handler::process_proposals() { const auto &idx = database.get_index_type().indices().get(); const auto swwo = idx.find(swwo_id); if (swwo != idx.end()) { - should_process = (swwo->sidechain == sidechain); + should_process = (swwo->withdraw_sidechain == sidechain); } break; } - case chain::operation::tag::value: { - sidechain_type sc = op_obj_idx_0.get().sidechain; - should_process = (sc == sidechain); + case chain::operation::tag::value: { + sidechain_transaction_id_type st_id = op_obj_idx_0.get().sidechain_transaction_id; + const auto &idx = database.get_index_type().indices().get(); + const auto sto = idx.find(st_id); + if (sto != idx.end()) { + should_process = (sto->sidechain == sidechain); + } break; } @@ -298,16 +314,10 @@ void sidechain_net_handler::process_proposals() { } if (should_process) { - ilog("Proposal ${po} will be processed by sidechain handler ${sidechain}", ("po", (*po).id)("sidechain", sidechain)); bool should_approve = process_proposal(*po); if (should_approve) { - ilog("Proposal ${po} will be approved", ("po", *po)); approve_proposal(po->id, plugin.get_current_son_id()); - } else { - ilog("Proposal ${po} is not approved", ("po", (*po).id)); } - } else { - ilog("Proposal ${po} will not be processed by sidechain handler ${sidechain}", ("po", (*po).id)("sidechain", sidechain)); } } } @@ -327,8 +337,9 @@ void sidechain_net_handler::process_deposits() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) { - if (swdo.id == object_id_type(0, 0, 0)) + if (swdo.id == object_id_type(0, 0, 0)) { return; + } ilog("Deposit to process: ${swdo}", ("swdo", swdo)); @@ -338,36 +349,6 @@ void sidechain_net_handler::process_deposits() { wlog("Deposit not processed: ${swdo}", ("swdo", swdo)); return; } - - const chain::global_property_object &gpo = database.get_global_properties(); - - son_wallet_deposit_process_operation swdp_op; - swdp_op.payer = gpo.parameters.son_account(); - swdp_op.son_wallet_deposit_id = swdo.id; - - asset_issue_operation ai_op; - ai_op.fee = asset(2001000); - ai_op.issuer = gpo.parameters.son_account(); - price btc_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; - ai_op.asset_to_issue = asset(swdo.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount, database.get_global_properties().parameters.btc_asset()); - ai_op.issue_to_account = swdo.peerplays_from; - - 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(ai_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 son wallet deposit process operation failed with exception ${e}", ("e", e.what())); - } }); } @@ -380,8 +361,9 @@ void sidechain_net_handler::process_withdrawals() { const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false)); std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) { - if (swwo.id == object_id_type(0, 0, 0)) + if (swwo.id == object_id_type(0, 0, 0)) { return; + } ilog("Withdraw to process: ${swwo}", ("swwo", swwo)); @@ -391,48 +373,20 @@ void sidechain_net_handler::process_withdrawals() { wlog("Withdraw not processed: ${swwo}", ("swwo", swwo)); return; } - - const chain::global_property_object &gpo = database.get_global_properties(); - - son_wallet_withdraw_process_operation swwp_op; - swwp_op.payer = gpo.parameters.son_account(); - swwp_op.son_wallet_withdraw_id = swwo.id; - - asset_reserve_operation ar_op; - ar_op.fee = asset(2001000); - ar_op.payer = gpo.parameters.son_account(); - ar_op.amount_to_reserve = asset(swwo.withdraw_amount, database.get_global_properties().parameters.btc_asset()); - - 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); - proposal_op.proposed_ops.emplace_back(ar_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 son wallet withdraw process 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)); + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid)); std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { - if (sto.id == object_id_type(0, 0, 0)) + if (sto.id == object_id_type(0, 0, 0)) { return; + } ilog("Sidechain transaction to process: ${sto}", ("sto", sto.id)); - bool complete = false; std::string processed_sidechain_tx = process_sidechain_transaction(sto); if (processed_sidechain_tx.empty()) { @@ -446,8 +400,8 @@ void sidechain_net_handler::process_sidechain_transactions() { sts_op.signature = processed_sidechain_tx; signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); - trx.validate(); try { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -458,12 +412,13 @@ void sidechain_net_handler::process_sidechain_transactions() { } 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)); + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete)); std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { - if (sto.id == object_id_type(0, 0, 0)) + if (sto.id == object_id_type(0, 0, 0)) { return; + } ilog("Sidechain transaction to send: ${sto}", ("sto", sto.id)); @@ -480,8 +435,8 @@ void sidechain_net_handler::send_sidechain_transactions() { sts_op.sidechain_transaction = sidechain_transaction; signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op); - trx.validate(); try { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -491,4 +446,69 @@ void sidechain_net_handler::send_sidechain_transactions() { }); } +void sidechain_net_handler::settle_sidechain_transactions() { + const auto &idx = database.get_index_type().indices().get(); + const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent)); + + std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) { + if (sto.id == object_id_type(0, 0, 0)) { + return; + } + + if (proposal_exists(chain::operation::tag::value, sto.id)) { + return; + } + + ilog("Sidechain transaction to settle: ${sto}", ("sto", sto.id)); + + int64_t settle_amount = settle_sidechain_transaction(sto); + + if (settle_amount < 0) { + wlog("Sidechain transaction not settled: ${sto}", ("sto", sto.id)); + return; + } + + const chain::global_property_object &gpo = database.get_global_properties(); + + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + 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); + + sidechain_transaction_settle_operation sts_op; + sts_op.payer = gpo.parameters.son_account(); + sts_op.sidechain_transaction_id = sto.id; + proposal_op.proposed_ops.emplace_back(sts_op); + + if (settle_amount != 0) { + if (sto.object_id.is()) { + asset_issue_operation ai_op; + ai_op.fee = asset(2001000); + ai_op.issuer = gpo.parameters.son_account(); + ai_op.asset_to_issue = asset(settle_amount, database.get_global_properties().parameters.btc_asset()); + ai_op.issue_to_account = database.get(sto.object_id).peerplays_from; + proposal_op.proposed_ops.emplace_back(ai_op); + } + + if (sto.object_id.is()) { + asset_reserve_operation ar_op; + ar_op.fee = asset(2001000); + ar_op.payer = gpo.parameters.son_account(); + ar_op.amount_to_reserve = asset(settle_amount, database.get_global_properties().parameters.btc_asset()); + proposal_op.proposed_ops.emplace_back(ar_op); + } + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + } catch (fc::exception e) { + elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what())); + } + }); +} + }} // 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 ed410244..92c0b445 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1015,6 +1015,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) } case chain::operation::tag::value: { + bool process_ok = false; + bool transaction_ok = false; son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get().son_wallet_deposit_id; const auto &idx = database.get_index_type().indices().get(); const auto swdo = idx.find(swdo_id); @@ -1035,8 +1037,8 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) std::string tx_txid = tx_json.get("result.txid"); uint32_t tx_confirmations = tx_json.get("result.confirmations"); std::string tx_address = ""; - uint64_t tx_amount = 0; - uint64_t tx_vout = 0; + int64_t tx_amount = -1; + int64_t tx_vout = -1; for (auto &input : tx_json.get_child("result.details")) { tx_address = input.second.get("address"); @@ -1050,49 +1052,104 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) } } - should_approve = (swdo_txid == tx_txid) && - (swdo_address == tx_address) && - (swdo_amount == tx_amount) && - (swdo_vout == tx_vout) && - (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); + process_ok = (swdo_txid == tx_txid) && + (swdo_address == tx_address) && + (swdo_amount == tx_amount) && + (swdo_vout == tx_vout) && + (gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations); + } + + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swdo = idx.find(object_id); + if (swdo != idx.end()) { + tx_str = create_deposit_transaction(*swdo); + } + } + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(object_id); + if (swwo != idx.end()) { + tx_str = create_withdrawal_transaction(*swwo); + } + } + + transaction_ok = (op_tx_str == tx_str); } } + + should_approve = process_ok && + transaction_ok; break; } case chain::operation::tag::value: { - should_approve = false; + bool process_ok = false; + bool transaction_ok = false; + son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(swwo_id); + if (swwo != idx.end()) { + + uint32_t swwo_block_num = swwo->block_num; + std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; + uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); + + const auto &block = database.fetch_block_by_number(swwo_block_num); + + for (const auto &tx : block->transactions) { + if (tx.id().str() == swwo_peerplays_transaction_id) { + operation op = tx.operations[swwo_op_idx]; + transfer_operation t_op = op.get(); + + price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; + asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); + + process_ok = (t_op.to == gpo.parameters.son_account()) && + (swwo->peerplays_from == t_op.from) && + (swwo->peerplays_asset == peerplays_asset); + break; + } + } + + object_id_type object_id = op_obj_idx_1.get().object_id; + std::string op_tx_str = op_obj_idx_1.get().transaction; + + const auto &st_idx = database.get_index_type().indices().get(); + const auto st = st_idx.find(object_id); + if (st == st_idx.end()) { + + std::string tx_str = ""; + + if (object_id.is()) { + const auto &idx = database.get_index_type().indices().get(); + const auto swwo = idx.find(object_id); + if (swwo != idx.end()) { + tx_str = create_withdrawal_transaction(*swwo); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } + + should_approve = process_ok && + transaction_ok; break; } - case chain::operation::tag::value: { - object_id_type object_id = op_obj_idx_0.get().object_id; - std::string op_tx_str = op_obj_idx_0.get().transaction; - - const auto &st_idx = database.get_index_type().indices().get(); - const auto st = st_idx.find(object_id); - if (st == st_idx.end()) { - - std::string tx_str = ""; - - if (object_id.is()) { - const auto &idx = database.get_index_type().indices().get(); - const auto swdo = idx.find(object_id); - if (swdo != idx.end()) { - tx_str = create_deposit_transaction(*swdo); - } - } - - if (object_id.is()) { - const auto &idx = database.get_index_type().indices().get(); - const auto swwo = idx.find(object_id); - if (swwo != idx.end()) { - tx_str = create_withdrawal_transaction(*swwo); - } - } - - should_approve = (op_tx_str == tx_str); - } + case chain::operation::tag::value: { + should_approve = true; break; } @@ -1161,6 +1218,7 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() { signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); try { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -1204,8 +1262,8 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { op.withdraw_address = sao.withdraw_address; signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op); - trx.validate(); try { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -1228,22 +1286,27 @@ bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_obj if (!tx_str.empty()) { const chain::global_property_object &gpo = database.get_global_properties(); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + 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); + + son_wallet_deposit_process_operation swdp_op; + swdp_op.payer = gpo.parameters.son_account(); + swdp_op.son_wallet_deposit_id = swdo.id; + proposal_op.proposed_ops.emplace_back(swdp_op); + 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 = gpo.active_sons; - - 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 { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -1267,22 +1330,27 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw if (!tx_str.empty()) { const chain::global_property_object &gpo = database.get_global_properties(); + proposal_create_operation proposal_op; + proposal_op.fee_paying_account = plugin.get_current_son_object().son_account; + 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); + + son_wallet_withdraw_process_operation swwp_op; + swwp_op.payer = gpo.parameters.son_account(); + swwp_op.son_wallet_withdraw_id = swwo.id; + proposal_op.proposed_ops.emplace_back(swwp_op); + 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 = gpo.active_sons; - - 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 { + trx.validate(); database.push_transaction(trx, database::validation_steps::skip_block_size_check); if (plugin.app().p2p_node()) plugin.app().p2p_node()->broadcast(net::trx_message(trx)); @@ -1303,6 +1371,55 @@ std::string sidechain_net_handler_bitcoin::send_sidechain_transaction(const side return send_transaction(sto); } +int64_t sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain_transaction_object &sto) { + + if (sto.object_id.is()) { + return 0; + } + + int64_t settle_amount = -1; + + if (sto.sidechain_transaction.empty()) { + return settle_amount; + } + + std::string tx_str = bitcoin_client->gettransaction(sto.sidechain_transaction, true); + std::stringstream tx_ss(tx_str); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(tx_ss, tx_json); + + if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) { + return settle_amount; + } + + std::string tx_txid = tx_json.get("result.txid"); + uint32_t tx_confirmations = tx_json.get("result.confirmations"); + std::string tx_address = ""; + int64_t tx_amount = -1; + + const chain::global_property_object &gpo = database.get_global_properties(); + + if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) { + if (sto.object_id.is()) { + for (auto &input : tx_json.get_child("result.details")) { + if (input.second.get("category") == "receive") { + std::string tx_amount_s = input.second.get("amount"); + tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + tx_amount = std::stoll(tx_amount_s); + break; + } + } + settle_amount = tx_amount; + } + + if (sto.object_id.is()) { + auto swwo = database.get(sto.object_id); + settle_amount = swwo.withdraw_amount.value; + } + } + return settle_amount; +} + std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector &son_pubkeys) { using namespace bitcoin; @@ -1321,7 +1438,6 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s << "}, \"error\":null}"; std::string res = ss.str(); - ilog("Weighted Multisig Address = ${a}", ("a", res)); return res; } @@ -1452,7 +1568,6 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s return create_transaction(inputs, outputs, redeem_script); } -// 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 &redeem_script) { using namespace bitcoin; @@ -1474,12 +1589,9 @@ std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector< const auto tx = tb.get_transaction(); std::string hex_tx = fc::to_hex(pack(tx)); std::string tx_raw = write_transaction_data(hex_tx, in_amounts, redeem_script); - ilog("Raw transaction ${tx}", ("tx", tx_raw)); return tx_raw; } -// 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 sidechain_transaction_object &sto) { using namespace bitcoin; std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain); @@ -1498,15 +1610,11 @@ std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_tran read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script); - ilog("Sign transaction retreived: ${s}", ("s", tx_hex)); - bitcoin_transaction tx = unpack(parse_hex(tx_hex)); std::vector redeem_scripts(tx.vin.size(), parse_hex(redeem_script)); auto sigs = sign_witness_transaction_part(tx, redeem_scripts, in_amounts, privkey_signing, btc_context(), 1); std::string tx_signature = write_transaction_signatures(sigs); - ilog("Signatures: son-id = ${son}, pkey = ${prvkey}, tx_signature = ${s}", ("son", plugin.get_current_son_id())("prvkey", prvkey)("s", tx_signature)); - return tx_signature; } @@ -1518,8 +1626,6 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script); - ilog("Send transaction retreived: ${s}", ("s", tx_hex)); - bitcoin_transaction tx = unpack(parse_hex(tx_hex)); std::vector redeem_scripts(tx.vin.size(), parse_hex(redeem_script)); @@ -1547,8 +1653,6 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran std::string final_tx_hex = fc::to_hex(pack(tx)); std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); - ilog("Send transaction: ${tx}, [${res}]", ("tx", final_tx_hex)("res", res)); - return res; } @@ -1692,5 +1796,4 @@ void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector #include +#include #include #include @@ -15,12 +16,26 @@ #include #include #include +#include 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; + + if (options.count("peerplays-private-key")) { + const std::vector pub_priv_keys = options["peerplays-private-key"].as>(); + for (const std::string &itr_key_pair : pub_priv_keys) { + auto key_pair = graphene::app::dejsonify>(itr_key_pair, 5); + ilog("Peerplays Public Key: ${public}", ("public", key_pair.first)); + if (!key_pair.first.length() || !key_pair.second.length()) { + FC_THROW("Invalid public private key pair."); + } + private_keys[key_pair.first] = key_pair.second; + } + } + database.applied_block.connect([&](const signed_block &b) { on_applied_block(b); }); @@ -85,37 +100,12 @@ bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po } case chain::operation::tag::value: { - son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get().son_wallet_withdraw_id; - const auto &idx = database.get_index_type().indices().get(); - const auto swwo = idx.find(swwo_id); - if (swwo != idx.end()) { - - uint32_t swwo_block_num = swwo->block_num; - std::string swwo_peerplays_transaction_id = swwo->peerplays_transaction_id; - uint32_t swwo_op_idx = std::stoll(swwo->peerplays_uid.substr(swwo->peerplays_uid.find_last_of("-") + 1)); - - const auto &block = database.fetch_block_by_number(swwo_block_num); - - for (const auto &tx : block->transactions) { - if (tx.id().str() == swwo_peerplays_transaction_id) { - operation op = tx.operations[swwo_op_idx]; - transfer_operation t_op = op.get(); - - price asset_price = database.get(t_op.amount.asset_id).options.core_exchange_rate; - asset peerplays_asset = asset(t_op.amount.amount * asset_price.base.amount / asset_price.quote.amount); - - should_approve = (t_op.to == gpo.parameters.son_account()) && - (swwo->peerplays_from == t_op.from) && - (swwo->peerplays_asset == peerplays_asset); - break; - } - } - } + should_approve = false; break; } - case chain::operation::tag::value: { - should_approve = false; + case chain::operation::tag::value: { + should_approve = true; break; } @@ -138,7 +128,55 @@ void sidechain_net_handler_peerplays::process_sidechain_addresses() { } bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) { - return true; + + const chain::global_property_object &gpo = database.get_global_properties(); + + asset_issue_operation ai_op; + ai_op.issuer = gpo.parameters.son_account(); + price btc_price = database.get(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate; + ai_op.asset_to_issue = asset(swdo.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount, database.get_global_properties().parameters.btc_asset()); + ai_op.issue_to_account = swdo.peerplays_from; + + signed_transaction tx; + auto dyn_props = database.get_dynamic_global_properties(); + tx.set_reference_block(dyn_props.head_block_id); + tx.set_expiration(database.head_block_time() + gpo.parameters.maximum_time_until_expiration); + tx.operations.push_back(ai_op); + database.current_fee_schedule().set_fee(tx.operations.back()); + + std::stringstream ss; + fc::raw::pack(ss, tx, 1000); + std::string tx_str = boost::algorithm::hex(ss.str()); + + if (!tx_str.empty()) { + const chain::global_property_object &gpo = database.get_global_properties(); + + 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 = gpo.active_sons; + + 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); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + 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; } bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) { @@ -146,11 +184,55 @@ bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdr } std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto) { - return sto.transaction; + + std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction)); + signed_transaction trx; + fc::raw::unpack(ss_trx, trx, 1000); + + fc::optional privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain))); + signature_type st = trx.sign(*privkey, database.get_chain_id()); + + std::stringstream ss_st; + fc::raw::pack(ss_st, st, 1000); + std::string st_str = boost::algorithm::hex(ss_st.str()); + + return st_str; } std::string sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) { - return sto.transaction; + + std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction)); + signed_transaction trx; + fc::raw::unpack(ss_trx, trx, 1000); + + for (auto signature : sto.signatures) { + if (!signature.second.empty()) { + std::stringstream ss_st(boost::algorithm::unhex(signature.second)); + signature_type st; + fc::raw::unpack(ss_st, st, 1000); + + trx.signatures.push_back(st); + trx.signees.clear(); + } + } + + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + return trx.id().str(); + } catch (fc::exception e) { + elog("Sidechain transaction failed with exception ${e}", ("e", e.what())); + return ""; + } + + return ""; +} + +int64_t sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidechain_transaction_object &sto) { + int64_t settle_amount = 0; + return settle_amount; } void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp index ede60bd4..8de60981 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_manager.cpp @@ -75,4 +75,10 @@ void sidechain_net_manager::send_sidechain_transactions() { } } +void sidechain_net_manager::settle_sidechain_transactions() { + for (size_t i = 0; i < net_handlers.size(); i++) { + net_handlers.at(i)->settle_sidechain_transactions(); + } +} + }} // namespace graphene::peerplays_sidechain