Merge branch 'feature/478-estimate-transaction-fee' into 'develop'

#478 estimate transaction fee

See merge request PBSA/peerplays!179
This commit is contained in:
serkixenos 2022-11-22 20:44:07 +00:00
commit 1b340345f3
15 changed files with 177 additions and 1 deletions

View file

@ -54,6 +54,7 @@ public:
void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); 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); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id);
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -26,9 +26,10 @@ public:
std::shared_ptr<detail::sidechain_api_impl> my; std::shared_ptr<detail::sidechain_api_impl> my;
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
}; };
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain
FC_API(graphene::peerplays_sidechain::sidechain_api, FC_API(graphene::peerplays_sidechain::sidechain_api,
(get_son_listener_log)) (get_son_listener_log)(estimate_withdrawal_transaction_fee))

View file

@ -50,6 +50,7 @@ public:
void add_to_son_listener_log(std::string trx_id); void add_to_son_listener_log(std::string trx_id);
std::vector<std::string> get_son_listener_log(); std::vector<std::string> get_son_listener_log();
virtual optional<asset> estimate_withdrawal_transaction_fee() const = 0;
protected: protected:
peerplays_sidechain_plugin &plugin; peerplays_sidechain_plugin &plugin;

View file

@ -106,6 +106,7 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_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); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string ip; std::string ip;

View file

@ -22,12 +22,14 @@ public:
std::string net_version(); std::string net_version();
std::string eth_get_transaction_count(const std::string &params); std::string eth_get_transaction_count(const std::string &params);
std::string eth_gas_price(); std::string eth_gas_price();
std::string eth_estimateGas(const std::string &params);
std::string get_chain_id(); std::string get_chain_id();
std::string get_network_id(); std::string get_network_id();
std::string get_nonce(const std::string &address); std::string get_nonce(const std::string &address);
std::string get_gas_price(); std::string get_gas_price();
std::string get_gas_limit(); std::string get_gas_limit();
std::string get_estimate_gas(const std::string &params);
std::string eth_send_transaction(const std::string &params); std::string eth_send_transaction(const std::string &params);
std::string eth_send_raw_transaction(const std::string &params); std::string eth_send_raw_transaction(const std::string &params);
@ -47,6 +49,7 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_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); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string rpc_url; std::string rpc_url;

View file

@ -45,6 +45,7 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_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); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
std::string rpc_url; std::string rpc_url;

View file

@ -19,6 +19,7 @@ public:
std::string process_sidechain_transaction(const sidechain_transaction_object &sto); std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
std::string send_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); bool settle_sidechain_transaction(const sidechain_transaction_object &sto, asset &settle_amount);
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private: private:
}; };

View file

@ -48,6 +48,7 @@ public:
void log_son_proposal_retry(sidechain_type sidechain, int op_type, object_id_type object_id); 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); bool can_son_participate(sidechain_type sidechain, int op_type, object_id_type object_id);
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
void schedule_heartbeat_loop(); void schedule_heartbeat_loop();
void heartbeat_loop(); void heartbeat_loop();
@ -626,6 +627,15 @@ std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin_im
return result; return result;
} }
optional<asset> 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<asset>();
}
return net_handlers.at(sidechain)->estimate_withdrawal_transaction_fee();
}
void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) { void peerplays_sidechain_plugin_impl::approve_proposals(sidechain_type sidechain) {
// prevent approving duplicate proposals with lock for parallel execution. // 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 // We can have the same propsals, but in the case of parallel execution we can run
@ -922,4 +932,8 @@ std::map<sidechain_type, std::vector<std::string>> peerplays_sidechain_plugin::g
return my->get_son_listener_log(); return my->get_son_listener_log();
} }
optional<asset> peerplays_sidechain_plugin::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
return my->estimate_withdrawal_transaction_fee(sidechain);
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -12,6 +12,7 @@ public:
std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> get_plugin(); std::shared_ptr<graphene::peerplays_sidechain::peerplays_sidechain_plugin> get_plugin();
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log(); std::map<sidechain_type, std::vector<std::string>> get_son_listener_log();
optional<asset> estimate_withdrawal_transaction_fee(sidechain_type sidechain);
private: private:
app::application &app; app::application &app;
@ -32,6 +33,10 @@ std::map<sidechain_type, std::vector<std::string>> sidechain_api_impl::get_son_l
return get_plugin()->get_son_listener_log(); return get_plugin()->get_son_listener_log();
} }
optional<asset> sidechain_api_impl::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
return get_plugin()->estimate_withdrawal_transaction_fee(sidechain);
}
} // namespace detail } // namespace detail
sidechain_api::sidechain_api(graphene::app::application &_app) : sidechain_api::sidechain_api(graphene::app::application &_app) :
@ -45,4 +50,8 @@ std::map<sidechain_type, std::vector<std::string>> sidechain_api::get_son_listen
return my->get_son_listener_log(); return my->get_son_listener_log();
} }
optional<asset> sidechain_api::estimate_withdrawal_transaction_fee(sidechain_type sidechain) {
return my->estimate_withdrawal_transaction_fee(sidechain);
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -1002,6 +1002,11 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
return false; return false;
} }
optional<asset> sidechain_net_handler_bitcoin::estimate_withdrawal_transaction_fee() const {
wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>{};
}
std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector<son_info> &son_pubkeys) { std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector<son_info> &son_pubkeys) {
using namespace bitcoin; using namespace bitcoin;

View file

@ -60,6 +60,10 @@ std::string ethereum_rpc_client::eth_gas_price() {
return send_post_request("eth_gasPrice", "", debug_rpc_calls); return send_post_request("eth_gasPrice", "", debug_rpc_calls);
} }
std::string ethereum_rpc_client::eth_estimateGas(const std::string &params) {
return send_post_request("eth_estimateGas", params, debug_rpc_calls);
}
std::string ethereum_rpc_client::get_chain_id() { std::string ethereum_rpc_client::get_chain_id() {
const std::string reply_str = eth_chainId(); const std::string reply_str = eth_chainId();
const auto chain_id_string = retrieve_value_from_reply(reply_str, ""); 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{}; return std::string{};
} }
std::string ethereum_rpc_client::get_estimate_gas(const std::string &params) {
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 &params) { std::string ethereum_rpc_client::eth_send_transaction(const std::string &params) {
return send_post_request("eth_sendTransaction", "[" + params + "]", debug_rpc_calls); 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; return false;
} }
optional<asset> 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<asset>{};
}
const auto &active_son = gpo.active_sons.at(sidechain).at(0);
const auto &s_idx = database.get_index_type<son_index>().indices().get<by_id>();
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<asset>{};
}
if (!son->sidechain_public_keys.contains(sidechain)) {
wlog("No public keys for current son: ${account_id}", ("account_id", son->son_account));
return optional<asset>{};
}
const auto &assets_by_symbol = database.get_index_type<asset_index>().indices().get<by_symbol>();
auto asset_itr = assets_by_symbol.find("ETH");
if (asset_itr == assets_by_symbol.end()) {
wlog("Could not find asset matching ETH");
return optional<asset>{};
}
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<int64_t>(rpc_client->get_estimate_gas(params));
const auto gas_price = ethereum::from_hex<int64_t>(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_info> &son_pubkeys, const std::string &object_id) { std::string sidechain_net_handler_ethereum::create_primary_wallet_transaction(const std::vector<son_info> &son_pubkeys, const std::string &object_id) {
std::vector<std::pair<std::string, uint16_t>> owners_weights; std::vector<std::pair<std::string, uint16_t>> owners_weights;
for (auto &son : son_pubkeys) { for (auto &son : son_pubkeys) {

View file

@ -806,6 +806,11 @@ bool sidechain_net_handler_hive::settle_sidechain_transaction(const sidechain_tr
return false; return false;
} }
optional<asset> sidechain_net_handler_hive::estimate_withdrawal_transaction_fee() const {
wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>{};
}
void sidechain_net_handler_hive::schedule_hive_listener() { void sidechain_net_handler_hive::schedule_hive_listener() {
fc::time_point now = fc::time_point::now(); fc::time_point now = fc::time_point::now();
int64_t time_to_next = 1000; int64_t time_to_next = 1000;

View file

@ -290,4 +290,9 @@ bool sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidecha
return true; return true;
} }
optional<asset> sidechain_net_handler_peerplays::estimate_withdrawal_transaction_fee() const {
wlog("estimate_withdrawal_transaction_fee not implemented for sidechain: ${sidechain}", ("sidechain", sidechain));
return optional<asset>{};
}
}} // namespace graphene::peerplays_sidechain }} // namespace graphene::peerplays_sidechain

View file

@ -2676,6 +2676,19 @@ class wallet_api
*/ */
std::map<sidechain_type, std::vector<std::string>> get_son_listener_log() const; std::map<sidechain_type, std::vector<std::string>> get_son_listener_log() const;
/**
* @brief Estimate transaction fee for withdrawal
* @param sidechain Sidechain type (bitcoin, HIVE, etc)
* @return Transaction fee
*/
optional<asset> 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<void(bool)> lock_changed; fc::signal<void(bool)> lock_changed;
std::shared_ptr<detail::wallet_api_impl> my; std::shared_ptr<detail::wallet_api_impl> my;
void encrypt_keys(); void encrypt_keys();
@ -2976,4 +2989,6 @@ FC_API( graphene::wallet::wallet_api,
(get_voters_by_id) (get_voters_by_id)
(get_voters) (get_voters)
(get_son_listener_log) (get_son_listener_log)
(estimate_withdrawal_transaction_fee)
(eth_estimate_withdrawal_transaction_fee)
) )

View file

@ -3291,6 +3291,25 @@ public:
return sign_transaction(trx, broadcast); return sign_transaction(trx, broadcast);
} FC_CAPTURE_AND_RETHROW((order_id)) } } 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, signed_transaction transfer(string from, string to, string amount,
string asset_symbol, string memo, bool broadcast = false) string asset_symbol, string memo, bool broadcast = false)
{ try { { try {
@ -3323,6 +3342,19 @@ public:
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
tx.validate(); 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); return sign_transaction(tx, broadcast);
} FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) } } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) }
@ -4222,6 +4254,31 @@ public:
FC_CAPTURE_AND_RETHROW() FC_CAPTURE_AND_RETHROW()
} }
optional<asset> 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; string _wallet_filename;
wallet_data _wallet; wallet_data _wallet;
@ -7273,6 +7330,16 @@ std::map<sidechain_type, std::vector<std::string>> wallet_api::get_son_listener_
return my->get_son_listener_log(); return my->get_son_listener_log();
} }
optional<asset> 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 // default ctor necessary for FC_REFLECT
signed_block_with_info::signed_block_with_info( const signed_block& block ) signed_block_with_info::signed_block_with_info( const signed_block& block )
: signed_block( block ) : signed_block( block )