From 776dc729b5679b0d401d0821c14b26f0373ec66c Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 2 Aug 2022 11:17:16 +0300 Subject: [PATCH] UpdateOwners: 1) process_proposal 2) sign_transaction 3) send_sidechain_transaction 4) settle_sidechain_transaction --- .../sidechain_net_handler_ethereum.hpp | 10 +- .../sidechain_net_handler_ethereum.cpp | 392 +++++++++++------- 2 files changed, 243 insertions(+), 159 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp index 681ceaed..1af1ab10 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_ethereum.hpp @@ -22,6 +22,11 @@ public: std::string get_chain_id(); std::string get_network_id(); + + std::string send_transaction(const std::string& wallet_contract_address, const std::string& owner_address, const std::string& data); + std::string get_transaction_receipt(const std::string& params); + //std::string update_owners(const std::string& wallet_contract_address, const std::string& owner_address, const std::vector>& owners_weights, const std::string& object_id); + //std::string withdraw(); }; class sidechain_net_handler_ethereum : public sidechain_net_handler { @@ -49,14 +54,11 @@ private: ethereum::chain_id_type chain_id; ethereum::network_id_type network_id; - std::string create_primary_wallet_address(const std::vector &son_pubkeys); - - std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address); + std::string create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string& object_id); std::string create_deposit_transaction(const son_wallet_deposit_object &swdo); std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo); std::string sign_transaction(const sidechain_transaction_object &sto); - std::string send_transaction(const sidechain_transaction_object &sto); uint64_t last_block_received; fc::future _listener_task; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index 58d22b00..c4f3dd87 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,18 @@ namespace graphene { namespace peerplays_sidechain { +std::string string_to_hex(const std::string& in) +{ + std::stringstream ss; + + ss << std::hex << std::setfill('0'); + for (size_t i = 0; in.length() > i; ++i) { + ss << std::setw(2) << static_cast(static_cast(in[i])); + } + + return ss.str(); +} + ethereum_rpc_client::ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls) : rpc_client(url, user_name, password, debug_rpc_calls) { } @@ -55,6 +68,43 @@ std::string ethereum_rpc_client::get_network_id() { return retrieve_value_from_reply(reply_str, "protocols.eth.network"); } +std::string ethereum_rpc_client::send_transaction(const std::string& wallet_contract_address, const std::string& owner_address, const std::string& data) { + const std::string params = "[ { \"from\": \"" + owner_address + "\", \"to\": \"" + wallet_contract_address + "\", \"data\": \"" + data + "\" } ]"; + return send_post_request("eth_sendTransaction", params, debug_rpc_calls); +} + +std::string ethereum_rpc_client::get_transaction_receipt(const std::string& params) { + return send_post_request("eth_getTransactionReceipt", "[ \"" + params + "\"]", debug_rpc_calls); +} + +/*std::string ethereum_rpc_client::update_owners(const std::string& wallet_contract_address, const std::string& owner_address, const std::vector>& owners_weights, const std::string& object_id) { + const std::string function_hash = "23ab6adf"; //! updateOwners((address,uint256)[],string) + + const std::string data = [&function_hash, &owners_weights, &object_id] { + std::string data = "0x" + function_hash; + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), 64)).str(); + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), ((owners_weights.size() * 2 + 3) * 32))).str(); + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), owners_weights.size())).str(); + for(const auto& owner : owners_weights) + { + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), owner.first)).str(); + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), owner.second)).str(); + } + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), object_id.size())).str(); + data += string_to_hex(object_id) + std::string( (64 - object_id.size() * 2 % 64), '0' ); + + return data; + }(); + + const std::string params = "[ { \"from\": \"" + owner_address + "\", \"to\": \"" + wallet_contract_address + "\", \"data\": \"" + data + "\" } ]"; + return send_post_request("eth_sendTransaction", params, debug_rpc_calls); +}*/ + +/*std::string ethereum_rpc_client::withdraw() { + const std::string params = "[ { \"from\": \"0x5FbBb31BE52608D2F52247E8400B7fCaA9E0bC12\", \"to\": \"0x3E84f248Cd00A2FDaaDfa0dC5c3ff64D8767Fb01\", \"data\": \"0xe088747b00000000000000000000000009ee460834498a4ee361beb819470061b7381b490000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000006312e33362e330000000000000000000000000000000000000000000000000000\" } ]"; + return send_post_request("eth_sendTransaction", params, debug_rpc_calls); +}*/ + sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : sidechain_net_handler(_plugin, options) { sidechain = sidechain_type::ethereum; @@ -146,80 +196,45 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) const auto swo = idx.find(swo_id); if (swo != idx.end()) { - //auto active_sons = gpo.active_sons; - //vector wallet_sons = swo->sons; - // - //bool son_sets_equal = (active_sons.size() == wallet_sons.size()); - // - //if (son_sets_equal) { - // for (size_t i = 0; i < active_sons.size(); i++) { - // son_sets_equal = son_sets_equal && active_sons.at(i) == wallet_sons.at(i); - // } - //} - // - //if (son_sets_equal) { - // address_ok = (op_obj_idx_0.get().address == wallet_account_name); - //} - // - //if (po.proposed_transaction.operations.size() >= 2) { - // 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 swo = idx.find(object_id); - // if (swo != idx.end()) { - // - // std::stringstream ss_trx(boost::algorithm::unhex(op_tx_str)); - // hive::signed_transaction op_trx; - // fc::raw::unpack(ss_trx, op_trx, 1000); - // - // fc::flat_map account_auths; - // uint32_t total_weight = 0; - // for (const auto &wallet_son : wallet_sons) { - // total_weight = total_weight + wallet_son.weight; - // account_auths[wallet_son.sidechain_public_keys.at(sidechain)] = wallet_son.weight; - // } - // - // std::string memo_key = rpc_client->get_account_memo_key(wallet_account_name); - // - // hive::authority active; - // active.weight_threshold = total_weight * 2 / 3 + 1; - // active.account_auths = account_auths; - // - // hive::account_update_operation auo; - // auo.account = wallet_account_name; - // auo.active = active; - // auo.memo_key = op_trx.operations[0].get().memo_key; - // - // hive::signed_transaction htrx; - // htrx.ref_block_num = op_trx.ref_block_num; - // htrx.ref_block_prefix = op_trx.ref_block_prefix; - // htrx.set_expiration(op_trx.expiration); - // - // htrx.operations.push_back(auo); - // - // std::stringstream ss; - // fc::raw::pack(ss, htrx, 1000); - // tx_str = boost::algorithm::hex(ss.str()); - // } - // } - // - // transaction_ok = (op_tx_str == tx_str); - // } - //} else { - // transaction_ok = true; - //} - } + auto active_sons = gpo.active_sons; + vector wallet_sons = swo->sons; - address_ok = true; - transaction_ok = true; + bool son_sets_equal = (active_sons.size() == wallet_sons.size()); + + if (son_sets_equal) { + for (size_t i = 0; i < active_sons.size(); i++) { + son_sets_equal = son_sets_equal && active_sons.at(i) == wallet_sons.at(i); + } + } + + if (son_sets_equal) { + address_ok = (op_obj_idx_0.get().address == wallet_contract_address); + } + + if (po.proposed_transaction.operations.size() >= 2) { + 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 swo = idx.find(object_id); + if (swo != idx.end()) { + tx_str = create_primary_wallet_transaction(gpo.active_sons, object_id.operator std::string()); + } + } + + transaction_ok = (op_tx_str == tx_str); + } + } else { + transaction_ok = true; + } + } should_approve = address_ok && transaction_ok; @@ -416,79 +431,62 @@ bool sidechain_net_handler_ethereum::process_proposal(const proposal_object &po) } void sidechain_net_handler_ethereum::process_primary_wallet() { - //const auto &swi = database.get_index_type().indices().get(); - //const auto &active_sw = swi.rbegin(); - //if (active_sw != swi.rend()) { - // - // if ((active_sw->addresses.find(sidechain_type::ethereum) == active_sw->addresses.end()) || - // (active_sw->addresses.at(sidechain_type::ethereum).empty())) { - // - // if (proposal_exists(chain::operation::tag::value, active_sw->id)) { - // return; - // } - // - // const chain::global_property_object &gpo = database.get_global_properties(); - // - // auto active_sons = gpo.active_sons; - // string reply_str = create_primary_wallet_address(active_sons); - // - // std::stringstream active_pw_ss(reply_str); - // - // boost::property_tree::ptree active_pw_pt; - // boost::property_tree::read_json(active_pw_ss, active_pw_pt); - // if (active_pw_pt.count("error") && active_pw_pt.get_child("error").empty()) { - // if (!plugin.can_son_participate(chain::operation::tag::value, active_sw->id)) { - // return; - // } - // - // 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); - // - // std::stringstream res; - // boost::property_tree::json_parser::write_json(res, active_pw_pt.get_child("result")); - // - // son_wallet_update_operation swu_op; - // swu_op.payer = gpo.parameters.son_account(); - // swu_op.son_wallet_id = active_sw->id; - // swu_op.sidechain = sidechain_type::ethereum; - // swu_op.address = res.str(); - // - // proposal_op.proposed_ops.emplace_back(swu_op); - // - // const auto &prev_sw = std::next(active_sw); - // if (prev_sw != swi.rend()) { - // std::string new_pw_address = active_pw_pt.get("result.address"); - // std::string tx_str = create_primary_wallet_transaction(*prev_sw, new_pw_address); - // if (!tx_str.empty()) { - // 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 = prev_sw->sons; - // proposal_op.proposed_ops.emplace_back(stc_op); - // } - // } - // - // signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); - // try { - // trx.validate(); - // database.push_transaction(trx, database::validation_steps::skip_block_size_check); - // if (plugin.app().p2p_node()) - // plugin.app().p2p_node()->broadcast(net::trx_message(trx)); - // plugin.log_son_proposal_retry(chain::operation::tag::value, active_sw->id); - // } catch (fc::exception &e) { - // elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); - // return; - // } - // } - // } - //} + const auto &swi = database.get_index_type().indices().get(); + const auto &active_sw = swi.rbegin(); + if (active_sw != swi.rend()) { + + if ((active_sw->addresses.find(sidechain_type::ethereum) == active_sw->addresses.end()) || + (active_sw->addresses.at(sidechain_type::ethereum).empty())) { + + if (proposal_exists(chain::operation::tag::value, active_sw->id)) { + return; + } + + if (!plugin.can_son_participate(chain::operation::tag::value, active_sw->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); + + son_wallet_update_operation swu_op; + swu_op.payer = gpo.parameters.son_account(); + swu_op.son_wallet_id = active_sw->id; + swu_op.sidechain = sidechain_type::ethereum; + swu_op.address = wallet_contract_address; + proposal_op.proposed_ops.emplace_back(swu_op); + + std::string tx_str = create_primary_wallet_transaction(gpo.active_sons, active_sw->id.operator std::string()); + if (!tx_str.empty()) { + sidechain_transaction_create_operation stc_op; + stc_op.payer = gpo.parameters.son_account(); + stc_op.object_id = active_sw->id; + stc_op.sidechain = sidechain; + stc_op.transaction = tx_str; + stc_op.signers = gpo.active_sons; + proposal_op.proposed_ops.emplace_back(stc_op); + } + + signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op); + try { + trx.validate(); + database.push_transaction(trx, database::validation_steps::skip_block_size_check); + if (plugin.app().p2p_node()) + plugin.app().p2p_node()->broadcast(net::trx_message(trx)); + plugin.log_son_proposal_retry(chain::operation::tag::value, active_sw->id); + } catch (fc::exception &e) { + elog("Sending proposal for son wallet update operation failed with exception ${e}", ("e", e.what())); + return; + } + } + } } void sidechain_net_handler_ethereum::process_sidechain_addresses() { + int temp = 0; } bool sidechain_net_handler_ethereum::process_deposit(const son_wallet_deposit_object &swdo) { @@ -578,19 +576,104 @@ std::string sidechain_net_handler_ethereum::process_sidechain_transaction(const } std::string sidechain_net_handler_ethereum::send_sidechain_transaction(const sidechain_transaction_object &sto) { - return send_transaction(sto); + boost::property_tree::ptree pt; + boost::property_tree::ptree pt_array; + for(const auto& signature : sto.signatures) { + const std::string sidechain_transaction = rpc_client->send_transaction(wallet_contract_address, signature.second, sto.transaction); + + std::stringstream ss_tx(sidechain_transaction); + boost::property_tree::ptree tx_json; + boost::property_tree::read_json(ss_tx, tx_json); + if( tx_json.count("result") && !tx_json.count("error") ) { + boost::property_tree::ptree node; + node.put("son", signature.second); + node.put("result", tx_json.get("result")); + pt_array.push_back(std::make_pair("", node)); + } + else { + //! Fixme + //! How should we proceed with error in send_transaction + elog("Error in send_transaction for transaction ${id}, signature ${signature}", ("id", sto.id) ("signature", signature.second)); + } + } + pt.add_child("result_array", pt_array); + + std::stringstream ss; + boost::property_tree::json_parser::write_json(ss, pt); + return ss.str(); } bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount) { - return true; + std::stringstream ss(sto.sidechain_transaction); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if( !json.count("result_array") ) { + return false; + } + + size_t count = 0; + for(const auto &entry : json.get_child("result_array")) { + const std::string receipt = rpc_client->get_transaction_receipt( entry.second.get("result") ); + + std::stringstream ss_receipt(receipt); + boost::property_tree::ptree json_receipt; + boost::property_tree::read_json(ss_receipt, json_receipt); + + if( json_receipt.get("result") == "null" ) { + wlog("Block is not minted yet for transaction ${id}", ("id", sto.id)); + return false; + } + + for(const auto &entry_receipt : json_receipt.get_child("result.logs")) { + if( boost::algorithm::to_lower_copy(wallet_contract_address) == entry_receipt.second.get("address") ) { + count += 1; + //! Fixme - compare data somehow? + //if( sto.transaction == entry_receipt.second.get("data") ) { + //} + } + } + } + + //! Check that we have all transactions + if(count != json.count("result_array")) { + wlog("Not all receipts received for transaction ${id}", ("id", sto.id)); + return false; + } + else + return true; + + return false; } -std::string sidechain_net_handler_ethereum::create_primary_wallet_address(const std::vector &son_pubkeys) { - return "Primary Wallet Address"; -} +std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector &son_pubkeys, const std::string& object_id) { + std::vector> owners_weights; + for (auto &son : son_pubkeys) { + FC_ASSERT(son.sidechain_public_keys.contains(sidechain_type::ethereum), "No public keys for son: ${son_id}", ("son_id", son.son_id)); + const std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::ethereum); + owners_weights.emplace_back(std::make_pair(pub_key_str, son.weight)); + } -std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address) { - return "Primary-Wallet-Transaction"; + //! Create data of transaction + const std::string function_hash = "23ab6adf"; //! updateOwners((address,uint256)[],string) + + const std::string data = [&function_hash, &owners_weights, &object_id] { + std::string data = "0x" + function_hash; + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), 64)).str(); + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), ((owners_weights.size() * 2 + 3) * 32))).str(); + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), owners_weights.size())).str(); + for(const auto& owner : owners_weights) + { + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), owner.first)).str(); + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), owner.second)).str(); + } + data += (boost::format("%x") % boost::io::group(std::setw(64), std::setfill('0'), object_id.size())).str(); + data += string_to_hex(object_id) + std::string( (64 - object_id.size() * 2 % 64), '0' ); + + return data; + }(); + + return data; } std::string sidechain_net_handler_ethereum::create_deposit_transaction(const son_wallet_deposit_object &swdo) { @@ -602,12 +685,11 @@ std::string sidechain_net_handler_ethereum::create_withdrawal_transaction(const } std::string sidechain_net_handler_ethereum::sign_transaction(const sidechain_transaction_object &sto) { - std::string key = get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain)); - return "Transaction-Signature-" + key; -} + //std::string key = get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain)); -std::string sidechain_net_handler_ethereum::send_transaction(const sidechain_transaction_object &sto) { - return "Transaction-ID"; + const auto& current_son = plugin.get_current_son_object(); + FC_ASSERT(current_son.sidechain_public_keys.contains(sidechain_type::ethereum), "No public keys for current son: ${account_id}", ("account_id", current_son.son_account)); + return "0x" + current_son.sidechain_public_keys.at(sidechain); } void sidechain_net_handler_ethereum::schedule_ethereum_listener() {