From 022fdeb40a3344c0e5e5d47d74369e1b7bb21f94 Mon Sep 17 00:00:00 2001 From: Vlad Dobromyslov Date: Tue, 22 Nov 2022 20:44:07 +0000 Subject: [PATCH] #478 estimate transaction fee --- .../peerplays_sidechain_plugin.hpp | 1 + .../peerplays_sidechain/sidechain_api.hpp | 3 +- .../sidechain_net_handler.hpp | 1 + .../sidechain_net_handler_bitcoin.hpp | 1 + .../sidechain_net_handler_ethereum.hpp | 3 + .../sidechain_net_handler_hive.hpp | 1 + .../sidechain_net_handler_peerplays.hpp | 1 + .../peerplays_sidechain_plugin.cpp | 14 ++++ .../peerplays_sidechain/sidechain_api.cpp | 9 +++ .../sidechain_net_handler_bitcoin.cpp | 5 ++ .../sidechain_net_handler_ethereum.cpp | 47 +++++++++++++ .../sidechain_net_handler_hive.cpp | 5 ++ .../sidechain_net_handler_peerplays.cpp | 5 ++ .../wallet/include/graphene/wallet/wallet.hpp | 15 +++++ libraries/wallet/wallet.cpp | 67 +++++++++++++++++++ 15 files changed, 177 insertions(+), 1 deletion(-) 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 114f9bc7..d890ae68 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 @@ -54,6 +54,7 @@ public: void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); }; }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp index b4636537..5d6df6af 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_api.hpp @@ -26,9 +26,10 @@ public: std::shared_ptr my; std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); }; }} // namespace graphene::peerplays_sidechain FC_API(graphene::peerplays_sidechain::sidechain_api, - (get_son_listener_log)) + (get_son_listener_log)(estimate_withdrawal_transaction_fee)) 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 a9257e54..fc295b4c 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 @@ -50,6 +50,7 @@ public: void add_to_son_listener_log(std::string trx_id); std::vector get_son_listener_log(); + virtual optional estimate_withdrawal_transaction_fee() const = 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 62eeeab6..eaa48526 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 @@ -106,6 +106,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: std::string ip; 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 ccc1aad4..e6e8f298 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,12 +22,14 @@ public: std::string net_version(); std::string eth_get_transaction_count(const std::string ¶ms); std::string eth_gas_price(); + std::string eth_estimateGas(const std::string ¶ms); std::string get_chain_id(); std::string get_network_id(); std::string get_nonce(const std::string &address); std::string get_gas_price(); std::string get_gas_limit(); + std::string get_estimate_gas(const std::string ¶ms); std::string eth_send_transaction(const std::string ¶ms); std::string eth_send_raw_transaction(const std::string ¶ms); @@ -47,6 +49,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: std::string rpc_url; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp index 72539db2..440a2520 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_hive.hpp @@ -45,6 +45,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: std::string rpc_url; 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 69eea1a9..139db7bf 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 @@ -19,6 +19,7 @@ public: std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string send_sidechain_transaction(const sidechain_transaction_object &sto); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount); + virtual optional estimate_withdrawal_transaction_fee() const override; private: }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 27784720..c8da6ce6 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -48,6 +48,7 @@ public: void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id); std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); void schedule_heartbeat_loop(); void heartbeat_loop(); @@ -626,6 +627,15 @@ std::map> peerplays_sidechain_plugin_im return result; } +optional peerplays_sidechain_plugin_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + if (!net_handlers.at(sidechain)) { + wlog("Net handler is null for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional(); + } + + return net_handlers.at(sidechain)->estimate_withdrawal_transaction_fee(); +} + void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) { // prevent approving duplicate proposals with lock for parallel execution. // We can have the same propsals, but in the case of parallel execution we can run @@ -922,4 +932,8 @@ std::map> peerplays_sidechain_plugin::g return my->get_son_listener_log(); } +optional peerplays_sidechain_plugin::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + return my->estimate_withdrawal_transaction_fee(sidechain); +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/sidechain_api.cpp b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp index 2a85d034..833d91ff 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_api.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_api.cpp @@ -12,6 +12,7 @@ public: std::shared_ptr get_plugin(); std::map> get_son_listener_log(); + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain); private: app::application &app; @@ -32,6 +33,10 @@ std::map> sidechain_api_impl::get_son_l return get_plugin()->get_son_listener_log(); } +optional sidechain_api_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + return get_plugin()->estimate_withdrawal_transaction_fee(sidechain); +} + } // namespace detail sidechain_api::sidechain_api(graphene::app::application &_app) : @@ -45,4 +50,8 @@ std::map> sidechain_api::get_son_listen return my->get_son_listener_log(); } +optional sidechain_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) { + return my->estimate_withdrawal_transaction_fee(sidechain); +} + }} // 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 ea4621c0..862f991a 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -1002,6 +1002,11 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain return false; } +optional sidechain_net_handler_bitcoin::estimate_withdrawal_transaction_fee() const { + wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; +} + std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector &son_pubkeys) { using namespace bitcoin; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index ce0e29e9..c827a721 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -60,6 +60,10 @@ std::string ethereum_rpc_client::eth_gas_price() { return send_post_request("eth_gasPrice", "", debug_rpc_calls); } +std::string ethereum_rpc_client::eth_estimateGas(const std::string ¶ms) { + return send_post_request("eth_estimateGas", params, debug_rpc_calls); +} + std::string ethereum_rpc_client::get_chain_id() { const std::string reply_str = eth_chainId(); const auto chain_id_string = retrieve_value_from_reply(reply_str, ""); @@ -100,6 +104,11 @@ std::string ethereum_rpc_client::get_gas_limit() { return std::string{}; } +std::string ethereum_rpc_client::get_estimate_gas(const std::string ¶ms) { + const std::string reply_str = eth_estimateGas(params); + return retrieve_value_from_reply(reply_str, ""); +} + std::string ethereum_rpc_client::eth_send_transaction(const std::string ¶ms) { return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls); } @@ -651,6 +660,44 @@ bool sidechain_net_handler_ethereum::settle_sidechain_transaction(const sidechai return false; } +optional sidechain_net_handler_ethereum::estimate_withdrawal_transaction_fee() const { + const auto &gpo = database.get_global_properties(); + if (gpo.active_sons.at(sidechain).empty()) { + wlog("No active sons for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; + } + + const auto &active_son = gpo.active_sons.at(sidechain).at(0); + const auto &s_idx = database.get_index_type().indices().get(); + const auto son = s_idx.find(active_son.son_id); + if (son == s_idx.end()) { + wlog("Can't find son for id: ${son_id}", ("son_id", active_son.son_id)); + return optional{}; + } + + if (!son->sidechain_public_keys.contains(sidechain)) { + wlog("No public keys for current son: ${account_id}", ("account_id", son->son_account)); + return optional{}; + } + + const auto &assets_by_symbol = database.get_index_type().indices().get(); + auto asset_itr = assets_by_symbol.find("ETH"); + if (asset_itr == assets_by_symbol.end()) { + wlog("Could not find asset matching ETH"); + return optional{}; + } + + const auto &public_key = son->sidechain_public_keys.at(sidechain); + const ethereum::withdrawal_encoder encoder; + const auto data = encoder.encode(public_key, 1 * 10000000000, son_wallet_withdraw_id_type{0}.operator object_id_type().operator std::string()); + const std::string params = "[{\"from\":\"" + ethereum::add_0x(public_key) + "\", \"to\":\"" + wallet_contract_address + "\", \"data\":\"" + data + "\"}]"; + + const auto estimate_gas = ethereum::from_hex(rpc_client->get_estimate_gas(params)); + const auto gas_price = ethereum::from_hex(rpc_client->get_gas_price()); + const auto eth_gas_fee = double(estimate_gas * gas_price) / double{1000000000000000000}; + return asset_itr->amount_from_string(std::to_string(eth_gas_fee)); +} + 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) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index a4c06175..63ae22c6 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -806,6 +806,11 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr return false; } +optional sidechain_net_handler_hive::estimate_withdrawal_transaction_fee() const { + wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; +} + void sidechain_net_handler_hive::schedule_hive_listener() { fc::time_point now = fc::time_point::now(); int64_t time_to_next = 1000; diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index b2945251..76fbbb3e 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -290,4 +290,9 @@ bool sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidecha return true; } +optional sidechain_net_handler_peerplays::estimate_withdrawal_transaction_fee() const { + wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain)); + return optional{}; +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index aac79252..6a0b5ee5 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -2676,6 +2676,19 @@ class wallet_api */ std::map> get_son_listener_log() const; + /** + * @brief Estimate transaction fee for withdrawal + * @param sidechain Sidechain type (bitcoin, HIVE, etc) + * @return Transaction fee + */ + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain) const; + + /** + * @brief Estimate gas fee for withdrawal transaction for ETH + * @return Gas fee in ETH + */ + std::string eth_estimate_withdrawal_transaction_fee() const; + fc::signal lock_changed; std::shared_ptr my; void encrypt_keys(); @@ -2976,4 +2989,6 @@ FC_API( graphene::wallet::wallet_api, (get_voters_by_id) (get_voters) (get_son_listener_log) + (estimate_withdrawal_transaction_fee) + (eth_estimate_withdrawal_transaction_fee) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index d8d79c4b..6b1b2eb1 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -3291,6 +3291,25 @@ public: return sign_transaction(trx, broadcast); } FC_CAPTURE_AND_RETHROW((order_id)) } + sidechain_type get_sidechain_type_from_asset(asset_id_type asset_id) const + { + const auto& gpo = _remote_db->get_global_properties(); + + if(asset_id == gpo.parameters.btc_asset()) + return sidechain_type::bitcoin; + + if(asset_id == gpo.parameters.eth_asset()) + return sidechain_type::ethereum; + + if(asset_id == gpo.parameters.hbd_asset()) + return sidechain_type::hive; + + if(asset_id == gpo.parameters.hive_asset()) + return sidechain_type::hive; + + return sidechain_type::unknown; + } + signed_transaction transfer(string from, string to, string amount, string asset_symbol, string memo, bool broadcast = false) { try { @@ -3323,6 +3342,19 @@ public: set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); tx.validate(); + //! For sidechain withdrawal check if amount is greater than fee + if(to_id == _remote_db->get_global_properties().parameters.son_account()) { + const auto sidechain = get_sidechain_type_from_asset(asset_obj->id); + const auto transaction_fee = estimate_withdrawal_transaction_fee(sidechain); + + if(transaction_fee) { + if (*transaction_fee >= xfer_op.amount) { + FC_THROW("Transaction fee: ${sidechain_fee}, would be grater than transfer amount ${amount}", + ("sidechain_fee", get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount))("amount", get_asset(xfer_op.amount.asset_id).amount_to_pretty_string(xfer_op.amount.amount))); + } + } + } + return sign_transaction(tx, broadcast); } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } @@ -4222,6 +4254,31 @@ public: FC_CAPTURE_AND_RETHROW() } + optional estimate_withdrawal_transaction_fee(sidechain_type sidechain) + { + use_sidechain_api(); + try + { + return (*_remote_sidechain)->estimate_withdrawal_transaction_fee(sidechain); + } + FC_CAPTURE_AND_RETHROW() + } + + std::string eth_estimate_withdrawal_transaction_fee() + { + try + { + const auto transaction_fee = estimate_withdrawal_transaction_fee(sidechain_type::ethereum); + if(transaction_fee) + { + return get_asset(transaction_fee->asset_id).amount_to_pretty_string(transaction_fee->amount); + } + + return "Can't get fee value"; + } + FC_CAPTURE_AND_RETHROW() + } + string _wallet_filename; wallet_data _wallet; @@ -7273,6 +7330,16 @@ std::map> wallet_api::get_son_listener_ return my->get_son_listener_log(); } +optional wallet_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) const +{ + return my->estimate_withdrawal_transaction_fee(sidechain); +} + +std::string wallet_api::eth_estimate_withdrawal_transaction_fee() const +{ + return my->eth_estimate_withdrawal_transaction_fee(); +} + // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info( const signed_block& block ) : signed_block( block )