diff --git a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp index ac526715..be050544 100644 --- a/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/common/rpc_client.cpp @@ -18,10 +18,40 @@ namespace graphene { namespace peerplays_sidechain { -rpc_client::rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : - url(_url), - user(_user), - password(_password), +struct rpc_reply { + uint16_t status; + std::string body; +}; + +class rpc_connection { +public: + rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls); + + std::string send_post_request(std::string method, std::string params, bool show_log); + std::string get_url() const; + +protected: + rpc_credentials credentials; + bool debug_rpc_calls; + + std::string protocol; + std::string host; + std::string port; + std::string target; + std::string authorization; + + uint32_t request_id; + +private: + rpc_reply send_post_request(std::string body, bool show_log); + + boost::beast::net::io_context ioc; + boost::beast::net::ip::tcp::resolver resolver; + boost::asio::ip::basic_resolver_results results; +}; + +rpc_connection::rpc_connection(const rpc_credentials &_credentials, bool _debug_rpc_calls) : + credentials(_credentials), debug_rpc_calls(_debug_rpc_calls), request_id(0), resolver(ioc) { @@ -31,7 +61,7 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor boost::xpressive::smatch sm; - if (boost::xpressive::regex_search(url, sm, sr)) { + if (boost::xpressive::regex_search(credentials.url, sm, sr)) { protocol = sm["Protocol"]; if (protocol.empty()) { protocol = "http"; @@ -52,15 +82,19 @@ rpc_client::rpc_client(std::string _url, std::string _user, std::string _passwor target = "/"; } - authorization = "Basic " + base64_encode(user + ":" + password); + authorization = "Basic " + base64_encode(credentials.user + ":" + credentials.password); results = resolver.resolve(host, port); } else { - elog("Invalid URL: ${url}", ("url", url)); + elog("Invalid URL: ${url}", ("url", credentials.url)); } } +std::string rpc_connection::get_url() const { + return credentials.url; +} + std::string rpc_client::retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx) { if (reply_str.empty()) { wlog("RPC call ${function}, empty reply string", ("function", __FUNCTION__)); @@ -125,7 +159,7 @@ std::string rpc_client::retrieve_value_from_reply(std::string reply_str, std::st return ""; } -std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { +std::string rpc_connection::send_post_request(std::string method, std::string params, bool show_log) { std::stringstream body; request_id = request_id + 1; @@ -164,7 +198,7 @@ std::string rpc_client::send_post_request(std::string method, std::string params return ""; } -rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { +rpc_reply rpc_connection::send_post_request(std::string body, bool show_log) { // These object is used as a context for ssl connection boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12_client); @@ -239,7 +273,7 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { reply.body = rbody; if (show_log) { - ilog("### Request URL: ${url}", ("url", url)); + ilog("### Request URL: ${url}", ("url", credentials.url)); ilog("### Request: ${body}", ("body", body)); ilog("### Response: ${rbody}", ("rbody", rbody)); } @@ -247,4 +281,113 @@ rpc_reply rpc_client::send_post_request(std::string body, bool show_log) { return reply; } +rpc_client::rpc_client(sidechain_type _sidechain, const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection) : + sidechain(_sidechain), + debug_rpc_calls(_debug_rpc_calls), + simulate_connection_reselection(_simulate_connection_reselection) { + FC_ASSERT(_credentials.size()); + for (size_t i = 0; i < _credentials.size(); i++) + connections.push_back(new rpc_connection(_credentials[i], _debug_rpc_calls)); + n_active_conn = 0; + if (connections.size() > 1) + schedule_connection_selection(); +} + +void rpc_client::schedule_connection_selection() { + fc::time_point now = fc::time_point::now(); + static const int64_t time_to_next_conn_selection = 10 * 1000 * 1000; // 10 sec + fc::time_point next_wakeup = now + fc::microseconds(time_to_next_conn_selection); + connection_selection_task = fc::schedule([this] { + select_connection(); + }, + next_wakeup, "SON RPC connection selection"); +} + +void rpc_client::select_connection() { + FC_ASSERT(connections.size() > 1); + + const std::lock_guard lock(conn_mutex); + + static const int t_limit = 5 * 1000 * 1000, // 5 sec + quality_diff_threshold = 10 * 1000; // 10 ms + + int best_n = -1; + int best_quality = -1; + + std::vector head_block_numbers; + head_block_numbers.resize(connections.size()); + + std::vector qualities; + qualities.resize(connections.size()); + + for (size_t n = 0; n < connections.size(); n++) { + rpc_connection &conn = *connections[n]; + int quality = 0; + head_block_numbers[n] = std::numeric_limits::max(); + + // ping n'th node + if (debug_rpc_calls) + ilog("### Ping ${sidechain} node #${n}, ${url}", ("sidechain", fc::reflector::to_string(sidechain))("n", n)("url", conn.get_url())); + fc::time_point t_sent = fc::time_point::now(); + uint64_t head_block_number = ping(conn); + fc::time_point t_received = fc::time_point::now(); + int t = (t_received - t_sent).count(); + + // evaluate n'th node reply quality and switch to it if it's better + if (head_block_number != std::numeric_limits::max()) { + if (simulate_connection_reselection) + t += rand() % 10; + FC_ASSERT(t != -1); + head_block_numbers[n] = head_block_number; + if (t < t_limit) + quality = t_limit - t; // the less time, the higher quality + + // look for the best quality + if (quality > best_quality) { + best_n = n; + best_quality = quality; + } + } + qualities[n] = quality; + } + + FC_ASSERT(best_n != -1 && best_quality != -1); + if (best_n != n_active_conn) { // if the best client is not the current one, ... + uint64_t active_head_block_number = head_block_numbers[n_active_conn]; + if ((active_head_block_number == std::numeric_limits::max() // ...and the current one has no known head block... + || head_block_numbers[best_n] >= active_head_block_number) // ...or the best client's head is more recent than the current, ... + && best_quality > qualities[n_active_conn] + quality_diff_threshold) { // ...and the new client's quality exceeds current more than by threshold + n_active_conn = best_n; // ...then select new one + if (debug_rpc_calls) + ilog("### Reselected ${sidechain} node to #${n}, ${url}", ("sidechain", fc::reflector::to_string(sidechain))("n", n_active_conn)("url", connections[n_active_conn]->get_url())); + } + } + + schedule_connection_selection(); +} + +rpc_connection &rpc_client::get_active_connection() const { + return *connections[n_active_conn]; +} + +std::string rpc_client::send_post_request(std::string method, std::string params, bool show_log) { + const std::lock_guard lock(conn_mutex); + return send_post_request(get_active_connection(), method, params, show_log); +} + +std::string rpc_client::send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log) { + return conn.send_post_request(method, params, show_log); +} + +rpc_client::~rpc_client() { + try { + if (connection_selection_task.valid()) + connection_selection_task.cancel_and_wait(__FUNCTION__); + } catch (fc::canceled_exception &) { + //Expected exception. Move along. + } catch (fc::exception &e) { + edump((e.to_detail_string())); + } +} + }} // namespace graphene::peerplays_sidechain diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp index eb8eac0c..bdec60d1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/common/rpc_client.hpp @@ -3,44 +3,52 @@ #include #include +#include +#include + #include #include +#include + namespace graphene { namespace peerplays_sidechain { -struct rpc_reply { - uint16_t status; - std::string body; +class rpc_connection; + +struct rpc_credentials { + std::string url; + std::string user; + std::string password; }; class rpc_client { public: - rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); + const sidechain_type sidechain; + + rpc_client(sidechain_type _sidechain, const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection); + ~rpc_client(); protected: - std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx); - std::string retrieve_value_from_reply(std::string reply_str, std::string value_path); + bool debug_rpc_calls; + bool simulate_connection_reselection; std::string send_post_request(std::string method, std::string params, bool show_log); - std::string url; - std::string user; - std::string password; - bool debug_rpc_calls; + static std::string send_post_request(rpc_connection &conn, std::string method, std::string params, bool show_log); - std::string protocol; - std::string host; - std::string port; - std::string target; - std::string authorization; - - uint32_t request_id; + static std::string retrieve_array_value_from_reply(std::string reply_str, std::string array_path, uint32_t idx); + static std::string retrieve_value_from_reply(std::string reply_str, std::string value_path); private: - rpc_reply send_post_request(std::string body, bool show_log); + std::vector connections; + int n_active_conn; + fc::future connection_selection_task; + std::mutex conn_mutex; - boost::beast::net::io_context ioc; - boost::beast::net::ip::tcp::resolver resolver; - boost::asio::ip::basic_resolver_results results; + rpc_connection &get_active_connection() const; + + void select_connection(); + void schedule_connection_selection(); + virtual uint64_t ping(rpc_connection &conn) const = 0; }; }} // namespace graphene::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 836a1f05..85dbc1f6 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 @@ -16,8 +16,10 @@ namespace graphene { namespace peerplays_sidechain { class sidechain_net_handler { +protected: + sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); + public: - sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options); virtual ~sidechain_net_handler(); sidechain_type get_sidechain() const; @@ -54,9 +56,9 @@ public: virtual optional estimate_withdrawal_transaction_fee() const = 0; protected: + const sidechain_type sidechain; peerplays_sidechain_plugin &plugin; graphene::chain::database &database; - sidechain_type sidechain; bool debug_rpc_calls; bool use_bitcoind_client; 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 bfb93805..96e8ec9c 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 @@ -98,7 +98,7 @@ protected: class bitcoin_rpc_client : public bitcoin_client_base, public rpc_client { public: public: - bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls); + bitcoin_rpc_client(const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection); uint64_t estimatesmartfee(uint16_t conf_target = 1); std::vector getblock(const block_data &block, int32_t verbosity = 2); @@ -113,6 +113,8 @@ public: std::string walletlock(); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); + virtual uint64_t ping(rpc_connection &conn) const override; + private: std::string ip; std::string user; @@ -213,14 +215,11 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - std::string bitcoin_node_ip; + std::vector _rpc_credentials; std::string libbitcoin_server_ip; uint32_t libbitcoin_block_zmq_port; uint32_t libbitcoin_trx_zmq_port; uint32_t bitcoin_node_zmq_port; - uint32_t rpc_port; - std::string rpc_user; - std::string rpc_password; std::string wallet_name; std::string wallet_password; 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 0cd22f96..fcabccf5 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 @@ -14,7 +14,7 @@ namespace graphene { namespace peerplays_sidechain { class ethereum_rpc_client : public rpc_client { public: - ethereum_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + ethereum_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection); std::string eth_blockNumber(); std::string eth_get_block_by_number(std::string block_number, bool full_block); @@ -36,6 +36,8 @@ public: std::string eth_send_raw_transaction(const std::string ¶ms); std::string eth_get_transaction_receipt(const std::string ¶ms); std::string eth_get_transaction_by_hash(const std::string ¶ms); + + virtual uint64_t ping(rpc_connection &conn) const override; }; class sidechain_net_handler_ethereum : public sidechain_net_handler { @@ -54,13 +56,9 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - using bimap_type = boost::bimap; - -private: - std::string rpc_url; - std::string rpc_user; - std::string rpc_password; + std::vector _rpc_credentials; std::string wallet_contract_address; + using bimap_type = boost::bimap; bimap_type erc20_addresses; ethereum_rpc_client *rpc_client; 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 440a2520..baa95bbf 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 @@ -13,7 +13,7 @@ namespace graphene { namespace peerplays_sidechain { class hive_rpc_client : public rpc_client { public: - hive_rpc_client(const std::string &url, const std::string &user_name, const std::string &password, bool debug_rpc_calls); + hive_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection); std::string account_history_api_get_transaction(std::string transaction_id); std::string block_api_get_block(uint32_t block_number); @@ -30,6 +30,8 @@ public: std::string get_head_block_time(); std::string get_is_test_net(); std::string get_last_irreversible_block_num(); + + virtual uint64_t ping(rpc_connection &conn) const override; }; class sidechain_net_handler_hive : public sidechain_net_handler { @@ -48,9 +50,8 @@ public: virtual optional estimate_withdrawal_transaction_fee() const override; private: - std::string rpc_url; - std::string rpc_user; - std::string rpc_password; + std::vector _rpc_credentials; + std::string wallet_account_name; hive_rpc_client *rpc_client; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index e4a2b8ea..6f6747db 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -175,13 +175,14 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("sidechain-retry-threshold", bpo::value()->default_value(150), "Sidechain retry throttling threshold"); cli.add_options()("debug-rpc-calls", bpo::value()->default_value(false), "Outputs RPC calls to console"); + cli.add_options()("simulate-rpc-connection-reselection", bpo::value()->default_value(false), "Simulate RPC connection reselection by altering their response times by a random value"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value()->default_value(false), "Bitcoin sidechain handler enabled"); + cli.add_options()("bitcoin-node-ip", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("use-bitcoind-client", bpo::value()->default_value(false), "Use bitcoind client instead of libbitcoin client"); cli.add_options()("libbitcoin-server-ip", bpo::value()->default_value("127.0.0.1"), "Libbitcoin server IP address"); cli.add_options()("libbitcoin-server-block-zmq-port", bpo::value()->default_value(9093), "Block ZMQ port of libbitcoin server"); cli.add_options()("libbitcoin-server-trx-zmq-port", bpo::value()->default_value(9094), "Trx ZMQ port of libbitcoin server"); - cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); cli.add_options()("bitcoin-node-zmq-port", bpo::value()->default_value(11111), "ZMQ port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-port", bpo::value()->default_value(8332), "RPC port of Bitcoin node"); cli.add_options()("bitcoin-node-rpc-user", bpo::value()->default_value("1"), "Bitcoin RPC user"); @@ -192,7 +193,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( "Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)"); cli.add_options()("ethereum-sidechain-enabled", bpo::value()->default_value(false), "Ethereum sidechain handler enabled"); - cli.add_options()("ethereum-node-rpc-url", bpo::value()->default_value("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); + cli.add_options()("ethereum-node-rpc-url", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:8545"), "Ethereum node RPC URL [http[s]://]host[:port]"); cli.add_options()("ethereum-node-rpc-user", bpo::value(), "Ethereum RPC user"); cli.add_options()("ethereum-node-rpc-password", bpo::value(), "Ethereum RPC password"); cli.add_options()("ethereum-wallet-contract-address", bpo::value(), "Ethereum wallet contract address"); @@ -202,7 +203,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( "Tuple of [Ethereum public key, Ethereum private key] (may specify multiple times)"); cli.add_options()("hive-sidechain-enabled", bpo::value()->default_value(false), "Hive sidechain handler enabled"); - cli.add_options()("hive-node-rpc-url", bpo::value()->default_value("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); + cli.add_options()("hive-node-rpc-url", bpo::value>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR("127.0.0.1:28090"), "Hive node RPC URL [http[s]://]host[:port]"); cli.add_options()("hive-node-rpc-user", bpo::value(), "Hive node RPC user"); cli.add_options()("hive-node-rpc-password", bpo::value(), "Hive node RPC password"); cli.add_options()("hive-wallet-account-name", bpo::value(), "Hive wallet account name"); @@ -291,6 +292,9 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt if (sidechain_enabled_peerplays && !config_ready_peerplays) { wlog("Haven't set up Peerplays sidechain parameters"); } + + if (options.at("simulate-rpc-connection-reselection").as()) + ilog("### RPC connection reselection will be simulated"); } void peerplays_sidechain_plugin_impl::plugin_startup() { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp index a4fe1252..f762da49 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp @@ -9,7 +9,8 @@ namespace graphene { namespace peerplays_sidechain { -sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : +sidechain_net_handler::sidechain_net_handler(sidechain_type _sidechain, peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : + sidechain(_sidechain), plugin(_plugin), database(_plugin.database()) { diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index c528d8a4..76e23ab0 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -25,8 +25,8 @@ namespace graphene { namespace peerplays_sidechain { // ============================================================================= -bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls) : - rpc_client(_url, _user, _password, _debug_rpc_calls) { +bitcoin_rpc_client::bitcoin_rpc_client(const std::vector &_credentials, bool _debug_rpc_calls, bool _simulate_connection_reselection) : + rpc_client(sidechain_type::bitcoin, _credentials, _debug_rpc_calls, _simulate_connection_reselection) { } uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { @@ -498,6 +498,13 @@ std::string bitcoin_libbitcoin_client::sendrawtransaction(const std::string &tx_ return res; } +uint64_t bitcoin_rpc_client::ping(rpc_connection &conn) const { + std::string str = send_post_request(conn, "getblockcount", "[]", debug_rpc_calls); + if (str.length() > 0) + return std::stoll(str); + return std::numeric_limits::max(); +} + // ============================================================================= zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq_block_port, uint32_t _zmq_trx_port) : @@ -655,13 +662,19 @@ void zmq_listener_libbitcoin::handle_block() { // ============================================================================= sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::bitcoin; + sidechain_net_handler(sidechain_type::bitcoin, _plugin, options) { if (options.count("debug-rpc-calls")) { debug_rpc_calls = options.at("debug-rpc-calls").as(); } + bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as(); + std::vector ips = options.at("bitcoin-node-ip").as>(); + bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as(); + uint32_t rpc_port = options.at("bitcoin-node-rpc-port").as(); + std::string rpc_user = options.at("bitcoin-node-rpc-user").as(); + std::string rpc_password = options.at("bitcoin-node-rpc-password").as(); + if (options.count("use-bitcoind-client")) { use_bitcoind_client = options.at("use-bitcoind-client").as(); } @@ -670,11 +683,6 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain libbitcoin_block_zmq_port = options.at("libbitcoin-server-block-zmq-port").as(); libbitcoin_trx_zmq_port = options.at("libbitcoin-server-trx-zmq-port").as(); - bitcoin_node_ip = options.at("bitcoin-node-ip").as(); - bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as(); - rpc_port = options.at("bitcoin-node-rpc-port").as(); - rpc_user = options.at("bitcoin-node-rpc-user").as(); - rpc_password = options.at("bitcoin-node-rpc-password").as(); wallet_name = ""; if (options.count("bitcoin-wallet-name")) { wallet_name = options.at("bitcoin-wallet-name").as(); @@ -697,17 +705,27 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } if (use_bitcoind_client) { - std::string url = bitcoin_node_ip + ":" + std::to_string(rpc_port); - if (!wallet_name.empty()) { - url = url + "/wallet/" + wallet_name; + + for (size_t i = 0; i < ips.size(); i++) { + std::string ip = ips[i]; + std::string url = ip + ":" + std::to_string(rpc_port); + if (!wallet_name.empty()) { + url = url + "/wallet/" + wallet_name; + } + rpc_credentials creds; + creds.url = url; + creds.user = rpc_user; + creds.password = rpc_password; + _rpc_credentials.push_back(creds); } - bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); + FC_ASSERT(!_rpc_credentials.empty()); + + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection)); if (!wallet_name.empty()) { bitcoin_client->loadwallet(wallet_name); } - listener = std::unique_ptr(new zmq_listener(bitcoin_node_ip, bitcoin_node_zmq_port)); - + listener = std::unique_ptr(new zmq_listener(ips[0], bitcoin_node_zmq_port)); } else { bitcoin_client = std::unique_ptr(new bitcoin_libbitcoin_client(libbitcoin_server_ip)); @@ -727,7 +745,6 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain bitcoin_client->getnetworkinfo(); - listener->start(); listener->block_event_received.connect([this](const block_data &block_event_data) { std::thread(&sidechain_net_handler_bitcoin::block_handle_event, this, block_event_data).detach(); }); @@ -736,6 +753,8 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain std::thread(&sidechain_net_handler_bitcoin::trx_handle_event, this, trx_event_data).detach(); }); + listener->start(); + database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { on_changed_objects(ids, accounts); }); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp index bb6a9821..c4db3266 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_ethereum.cpp @@ -25,8 +25,8 @@ namespace graphene { namespace peerplays_sidechain { -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) { +ethereum_rpc_client::ethereum_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection) : + rpc_client(sidechain_type::ethereum, credentials, debug_rpc_calls, simulate_connection_reselection) { } std::string ethereum_rpc_client::eth_blockNumber() { @@ -126,20 +126,29 @@ std::string ethereum_rpc_client::eth_get_transaction_by_hash(const std::string & return send_post_request("eth_getTransactionByHash", "[\"" + params + "\"]", debug_rpc_calls); } +uint64_t ethereum_rpc_client::ping(rpc_connection &conn) const { + std::string reply = send_post_request(conn, "eth_blockNumber", "", debug_rpc_calls); + if (!reply.empty()) + return ethereum::from_hex(retrieve_value_from_reply(reply, "")); + return std::numeric_limits::max(); +} + 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; + sidechain_net_handler(sidechain_type::ethereum, _plugin, options) { if (options.count("debug-rpc-calls")) { debug_rpc_calls = options.at("debug-rpc-calls").as(); } + bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as(); - rpc_url = options.at("ethereum-node-rpc-url").as(); + std::vector rpc_urls = options.at("ethereum-node-rpc-url").as>(); + std::string rpc_user; if (options.count("ethereum-node-rpc-user")) { rpc_user = options.at("ethereum-node-rpc-user").as(); } else { rpc_user = ""; } + std::string rpc_password; if (options.count("ethereum-node-rpc-password")) { rpc_password = options.at("ethereum-node-rpc-password").as(); } else { @@ -175,18 +184,27 @@ sidechain_net_handler_ethereum::sidechain_net_handler_ethereum(peerplays_sidecha } } - rpc_client = new ethereum_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); + for (size_t i = 0; i < rpc_urls.size(); i++) { + rpc_credentials creds; + creds.url = rpc_urls[i]; + creds.user = rpc_user; + creds.password = rpc_password; + _rpc_credentials.push_back(creds); + } + FC_ASSERT(!_rpc_credentials.empty()); + + rpc_client = new ethereum_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection); const std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { - elog("No Ethereum node running at ${url}", ("url", rpc_url)); + elog("No Ethereum node running at ${url}", ("url", _rpc_credentials[0].url)); FC_ASSERT(false); } chain_id = std::stoll(chain_id_str); const std::string network_id_str = rpc_client->get_network_id(); if (network_id_str.empty()) { - elog("No Ethereum node running at ${url}", ("url", rpc_url)); + elog("No Ethereum node running at ${url}", ("url", _rpc_credentials[0].url)); FC_ASSERT(false); } network_id = std::stoll(network_id_str); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp index 6a54b7f5..eb2f332f 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_hive.cpp @@ -30,8 +30,8 @@ namespace graphene { namespace peerplays_sidechain { -hive_rpc_client::hive_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) { +hive_rpc_client::hive_rpc_client(const std::vector &credentials, bool debug_rpc_calls, bool simulate_connection_reselection) : + rpc_client(sidechain_type::hive, credentials, debug_rpc_calls, simulate_connection_reselection) { } std::string hive_rpc_client::account_history_api_get_transaction(std::string transaction_id) { @@ -112,20 +112,34 @@ std::string hive_rpc_client::get_last_irreversible_block_num() { return retrieve_value_from_reply(reply_str, "last_irreversible_block_num"); } +uint64_t hive_rpc_client::ping(rpc_connection &conn) const { + const std::string reply = send_post_request(conn, "database_api.get_dynamic_global_properties", "", debug_rpc_calls); + if (!reply.empty()) { + std::stringstream ss(reply); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + if (json.count("result")) + return json.get("result.head_block_number"); + } + return std::numeric_limits::max(); +} + sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::hive; + sidechain_net_handler(sidechain_type::hive, _plugin, options) { if (options.count("debug-rpc-calls")) { debug_rpc_calls = options.at("debug-rpc-calls").as(); } + bool simulate_connection_reselection = options.at("simulate-rpc-connection-reselection").as(); - rpc_url = options.at("hive-node-rpc-url").as(); + std::vector rpc_urls = options.at("hive-node-rpc-url").as>(); + std::string rpc_user; if (options.count("hive-rpc-user")) { rpc_user = options.at("hive-rpc-user").as(); } else { rpc_user = ""; } + std::string rpc_password; if (options.count("hive-rpc-password")) { rpc_password = options.at("hive-rpc-password").as(); } else { @@ -146,11 +160,20 @@ sidechain_net_handler_hive::sidechain_net_handler_hive(peerplays_sidechain_plugi } } - rpc_client = new hive_rpc_client(rpc_url, rpc_user, rpc_password, debug_rpc_calls); + for (size_t i = 0; i < rpc_urls.size(); i++) { + rpc_credentials creds; + creds.url = rpc_urls[i]; + creds.user = rpc_user; + creds.password = rpc_password; + _rpc_credentials.push_back(creds); + } + FC_ASSERT(!_rpc_credentials.empty()); + + rpc_client = new hive_rpc_client(_rpc_credentials, debug_rpc_calls, simulate_connection_reselection); const std::string chain_id_str = rpc_client->get_chain_id(); if (chain_id_str.empty()) { - elog("No Hive node running at ${url}", ("url", rpc_url)); + elog("No Hive node running at ${url}", ("url", _rpc_credentials[0].url)); FC_ASSERT(false); } chain_id = chain_id_type(chain_id_str); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp index 0f6d06eb..edf92e80 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_peerplays.cpp @@ -23,8 +23,7 @@ namespace graphene { namespace peerplays_sidechain { sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) : - sidechain_net_handler(_plugin, options) { - sidechain = sidechain_type::peerplays; + sidechain_net_handler(sidechain_type::peerplays, _plugin, options) { //const auto &assets_by_symbol = database.get_index_type().indices().get(); //const auto get_asset_id = [&assets_by_symbol](const string &symbol) { // auto asset_itr = assets_by_symbol.find(symbol);