From 6b67fe76a8c551b317a0c93d0a7a40ab34560666 Mon Sep 17 00:00:00 2001 From: hirunda Date: Tue, 20 Sep 2022 01:06:33 +0200 Subject: [PATCH] libbitcoin client and bitcoind --- .../bitcoin/libbitcoin_client.cpp | 113 ++++- .../bitcoin/libbitcoin_client.hpp | 31 +- .../sidechain_net_handler.hpp | 2 +- .../sidechain_net_handler_bitcoin.hpp | 81 +++- .../peerplays_sidechain_plugin.cpp | 2 +- .../sidechain_net_handler_bitcoin.cpp | 385 ++++++++++++------ 6 files changed, 444 insertions(+), 170 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp index 5adb1228..07049781 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/libbitcoin_client.cpp @@ -3,28 +3,62 @@ #include -#include #include +#include + +#include -#include #include +#include namespace graphene { namespace peerplays_sidechain { -libbitcoin_client::libbitcoin_client() : - obelisk_client(10, 100) { +libbitcoin_client::libbitcoin_client(std::string url) : + obelisk_client(LIBBITCOIN_SERVER_TIMEOUT, LIBBITCOIN_SERVER_RETRIES) { + + std::string reg_expr = "^((?Phttps|http|tcp):\\/\\/)?(?P[a-zA-Z0-9\\-\\.]+)(:(?P\\d{1,5}))?(?P\\/.+)?"; + boost::xpressive::sregex sr = boost::xpressive::sregex::compile(reg_expr); + + boost::xpressive::smatch sm; + + if (boost::xpressive::regex_search(url, sm, sr)) { + protocol = sm["Protocol"]; + if (protocol.empty()) { + protocol = "tcp"; + } + + host = sm["Host"]; + if (host.empty()) { + host + "localhost"; + } + + port = sm["Port"]; + if (port.empty()) { + port = "9091"; + } + } + + uint16_t port_num = std::stoi(port); + std::string final_url = protocol + "://" + host; + + std::cout << "Final URL is: " << final_url << std::endl; + + libbitcoin::config::endpoint address(final_url, port_num); + libbitcoin::client::connection_type connection; - connection.retries = 100; - connection.server = libbitcoin::config::endpoint("tcp://localhost", 9091); + connection.retries = LIBBITCOIN_SERVER_RETRIES; + connection.server = address; if (!obelisk_client.connect(connection)) { - //display_connection_failure(error, connection.server); - //return console_result::failure; - std::cout << "Can't connect" << std::endl; + elog("Can't connect libbitcoin for url: ${url}", ("url", final_url)); } + + is_connected = true; } -void libbitcoin_client::send_transaction(std::string tx) { +std::string libbitcoin_client::send_transaction(std::string tx) { + + std::string res; auto error_handler = [&](const std::error_code &ec) { elog("error on sending bitcoin transaction ${error_code}", ("error_code", ec.message())); @@ -32,35 +66,46 @@ void libbitcoin_client::send_transaction(std::string tx) { auto result_handler = [&](libbitcoin::code result_code) { ilog("result code on sending transaction ${result_code}", ("result_code", result_code.message())); + res = std::to_string(result_code.value()); }; libbitcoin::explorer::config::transaction transaction(tx); libbitcoin::chain::transaction trx; - // This validates the tx, submits it to local tx pool, and notifies peers. - obelisk_client.transaction_pool_broadcast(error_handler, result_handler, transaction); - obelisk_client.wait(); + // This validates the tx, submits it to local tx pool, and notifies peers. + obelisk_client.transaction_pool_broadcast(error_handler, result_handler, transaction); + obelisk_client.wait(); + + return res; } -void libbitcoin_client::get_transaction(std::string tx_id) { +libbitcoin::chain::output::list libbitcoin_client::get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions) { + + libbitcoin::chain::output::list outs; auto error_handler = [&](const std::error_code &ec) { - elog("error on fetch_trx_by_address ${error_code}", ("error_code", ec.message())); + elog("error on fetch_trx_by_hash: ${hash} ${error_code}", ("hash", tx_id)("error_code", ec.message())); }; - auto transaction_handler = [&] (const libbitcoin::chain::transaction& tx_handler) { - + auto transaction_handler = [&](const libbitcoin::chain::transaction &tx_handler) { + tx_hash = libbitcoin::config::hash256(tx_handler.hash(false)).to_string(); + // TODO try to find this value (confirmitions) + confirmitions = 1; + outs = tx_handler.outputs(); }; - libbitcoin::hash_digest tx_hash = libbitcoin::config::hash256(tx_id); + libbitcoin::hash_digest hash = libbitcoin::config::hash256(tx_id); - obelisk_client.blockchain_fetch_transaction (error_handler, transaction_handler,tx_hash); + // obelisk_client.blockchain_fetch_transaction (error_handler, transaction_handler,hash); + obelisk_client.blockchain_fetch_transaction2(error_handler, transaction_handler, hash); obelisk_client.wait(); + + return outs; } -std::vector libbitcoin_client::list_unspent(std::string address, double amount) { +std::vector libbitcoin_client::listunspent(std::string address, double amount) { std::vector result; auto error_handler = [&](const std::error_code &ec) { @@ -78,12 +123,36 @@ std::vector libbitcoin_client::list_unspent(std::string add }; libbitcoin::wallet::payment_address payment_address(address); + uint64_t satoshi = 100000000 * amount; obelisk_client.blockchain_fetch_unspent_outputs(error_handler, - replay_handler, payment_address, 100000000, libbitcoin::wallet::select_outputs::algorithm::individual); + replay_handler, payment_address, satoshi, libbitcoin::wallet::select_outputs::algorithm::individual); obelisk_client.wait(); return result; } -}} // namespace graphene::peerplays_sidechain \ No newline at end of file + +bool libbitcoin_client::get_is_test_net() { + + bool result = false; + + auto error_handler = [&](const std::error_code &ec) { + elog("error on fetching genesis block ${error_code}", ("error_code", ec.message())); + }; + + auto block_header_handler = [&](const libbitcoin::chain::header &block_header) { + std::string hash_str = libbitcoin::config::hash256(block_header.hash()).to_string(); + if (hash_str == GENESIS_TESTNET_HASH || hash_str == GENESIS_REGTEST_HASH) { + result = true; + } + }; + + obelisk_client.blockchain_fetch_block_header(error_handler, block_header_handler, 0); + + obelisk_client.wait(); + + return result; +} +} +} // namespace graphene::peerplays_sidechain \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp index 2d0260de..37831224 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp @@ -1,9 +1,23 @@ #pragma once #include +#include +#include + +#include + +#define LIBBITCOIN_SERVER_TIMEOUT (10) +#define LIBBITCOIN_SERVER_RETRIES (100) + +#define GENESIS_MAINNET_HASH "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" +#define GENESIS_TESTNET_HASH "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" +#define GENESIS_REGTEST_HASH "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" namespace graphene { namespace peerplays_sidechain { +typedef std::function + block_update_handler; + struct list_unspent_replay { std::string hash; uint64_t value; @@ -12,13 +26,22 @@ struct list_unspent_replay { class libbitcoin_client { public: - libbitcoin_client(); - void send_transaction(std::string tx); - void get_transaction (std::string tx_id); - std::vector list_unspent(std::string address, double amount); + libbitcoin_client(std::string url); + std::string send_transaction(const std::string tx); + libbitcoin::chain::output::list get_transaction(std::string tx_id, std::string &tx_hash, uint32_t &confirmitions); + std::vector listunspent(std::string address, double amount); + bool get_is_test_net(); private: libbitcoin::client::obelisk_client obelisk_client; + libbitcoin::protocol::zmq::identifier id; + + std::string protocol; + std::string host; + std::string port; + std::string url; + + bool is_connected = false; }; }} // namespace graphene::peerplays_sidechain \ No newline at end of file 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 7e6030ed..edff1b45 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 @@ -57,7 +57,7 @@ protected: sidechain_type sidechain; bool debug_rpc_calls; - bool use_p2wsh_address; + bool use_bitcoind_client; std::map private_keys; 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 fc9bbf2e..2d1d62f2 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 @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include #include #include @@ -38,7 +38,13 @@ public: std::vector tx_in_list; }; -class bitcoin_client { +class block_data { +public: + std::string block_hash; + libbitcoin::chain::block block; +}; + +class bitcoin_client_base { public: enum class multi_type { script, @@ -57,21 +63,30 @@ public: }; virtual uint64_t estimatesmartfee(uint16_t conf_target = 128) = 0; - virtual std::string getblock(const std::string &block_hash, int32_t verbosity = 2) = 0; + virtual std::vector getblock(const block_data &block, int32_t verbosity = 2) = 0; virtual btc_tx getrawtransaction(const std::string &txid, const bool verbose = false) = 0; virtual void getnetworkinfo() = 0; virtual std::string getblockchaininfo() = 0; virtual std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999) = 0; virtual std::string sendrawtransaction(const std::string &tx_hex) = 0; + virtual std::string loadwallet(const std::string &filename) { + return ""; + }; + virtual std::string walletlock() { + return ""; + }; + virtual bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60) { + return false; + }; }; -class bitcoin_rpc_client : public bitcoin_client, public rpc_client { +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); uint64_t estimatesmartfee(uint16_t conf_target = 128); - std::string getblock(const std::string &block_hash, int32_t verbosity = 2); + std::vector getblock(const block_data &block, int32_t verbosity = 2); btc_tx getrawtransaction(const std::string &txid, const bool verbose = false); void getnetworkinfo(); std::string getblockchaininfo(); @@ -92,39 +107,67 @@ private: uint32_t bitcoin_major_version; }; -class bitcoin_libbitcoin_client : public bitcoin_client, public libbitcoin_client { +class bitcoin_libbitcoin_client : public bitcoin_client_base, public libbitcoin_client { public: + bitcoin_libbitcoin_client(std::string url); uint64_t estimatesmartfee(uint16_t conf_target = 128); - std::string getblock(const std::string &block_hash, int32_t verbosity = 2); + std::vector getblock(const block_data &block, int32_t verbosity = 2); btc_tx getrawtransaction(const std::string &txid, const bool verbose = false); void getnetworkinfo(); std::string getblockchaininfo(); std::vector listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999); std::string sendrawtransaction(const std::string &tx_hex); + +private: + bool is_test_net = false; }; // ============================================================================= -class zmq_listener { +class zmq_listener_base { +public: + virtual ~zmq_listener_base(){}; + zmq_listener_base(std::string _ip, uint32_t _zmq) { + ip = _ip; + zmq_port = _zmq; + stopped = false; + }; + virtual void start() = 0; + boost::signals2::signal event_received; + +protected: + std::string ip; + uint32_t zmq_port; + std::atomic_bool stopped; + std::thread thr; +}; + +class zmq_listener : public zmq_listener_base { public: zmq_listener(std::string _ip, uint32_t _zmq); virtual ~zmq_listener(); - void start(); - boost::signals2::signal event_received; private: void handle_zmq(); std::vector receive_multipart(); - std::string ip; - uint32_t zmq_port; - zmq::context_t ctx; zmq::socket_t socket; +}; - std::atomic_bool stopped; - std::thread thr; +class zmq_listener_libbitcoin : public zmq_listener_base { +public: + zmq_listener_libbitcoin(std::string _ip, uint32_t _zmq); + virtual ~zmq_listener_libbitcoin(); + void start(); + +private: + void handle_block(); + + libbitcoin::protocol::zmq::context context; + libbitcoin::protocol::zmq::socket socket; + libbitcoin::protocol::zmq::poller poller; }; // ============================================================================= @@ -152,9 +195,8 @@ private: std::string wallet; std::string wallet_password; - std::unique_ptr bitcoin_client; - std::unique_ptr lib_bitcoin_client; - std::unique_ptr listener; + std::unique_ptr bitcoin_client; + std::unique_ptr listener; fc::future on_changed_objects_task; bitcoin::bitcoin_address::network network_type; @@ -172,9 +214,8 @@ private: std::string sign_transaction(const sidechain_transaction_object &sto); std::string send_transaction(const sidechain_transaction_object &sto); - void handle_event(const std::string &event_data); + void handle_event(const block_data &event_data); std::string get_redeemscript_for_userdeposit(const std::string &user_address); - std::vector extract_info_from_block(const std::string &_block); void on_changed_objects(const vector &ids, const flat_set &accounts); void on_changed_objects_cb(const vector &ids, const flat_set &accounts); }; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 3592dac0..8cd7e6f3 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -165,7 +165,7 @@ 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()("use-p2wsh-address", bpo::value()->default_value(false), "Use p2wsh address instead of p2sh address"); + cli.add_options()("use-bitcoind-client", bpo::value()->default_value(false), "Use bitcoind client instead of libbitcoin client"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value()->default_value(false), "Bitcoin sidechain handler enabled"); cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index e5b094b4..093319c5 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -53,10 +53,53 @@ uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) { return 20000; } -std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { - std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); +std::vector bitcoin_rpc_client::getblock(const block_data &block, int32_t verbosity) { + std::string params = std::string("[\"") + block.block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]"); std::string str = send_post_request("getblock", params, debug_rpc_calls); - return str; + std::vector result; + + if (str.empty()) { + return result; + } + + std::stringstream ss(str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + auto json_result = json.get_child_optional("result"); + + for (const auto &tx_child : json_result.get().get_child("tx")) { + const auto &tx = tx_child.second; + + for (const auto &o : tx.get_child("vout")) { + const auto script = o.second.get_child("scriptPubKey"); + std::vector address_list; + + if (script.count("address")) { + address_list.emplace_back(script.get("address")); + } else if (script.count("addresses")) { + for (const auto &addr : script.get_child("addresses")) { + address_list.emplace_back(addr.second.get_value()); + } + } else { + continue; + } + + for (auto &address : address_list) { + const auto address_base58 = address; + info_for_vin vin; + vin.out.hash_tx = tx.get_child("txid").get_value(); + string amount = o.second.get_child("value").get_value(); + amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + vin.out.amount = std::stoll(amount); + vin.out.n_vout = o.second.get_child("n").get_value(); + vin.address = address_base58; + result.push_back(vin); + } + } + } + + return result; } void bitcoin_rpc_client::getnetworkinfo() { @@ -117,17 +160,23 @@ std::string bitcoin_rpc_client::getblockchaininfo() { std::string params = std::string("[]"); std::string str = send_post_request("getblockchaininfo", params, debug_rpc_calls); + std::string result; + if (str.length() > 0) { std::stringstream ss(str); boost::property_tree::ptree json; boost::property_tree::read_json(ss, json); - boost::property_tree::json_parser::write_json(ss, json.get_child("result")); - return ss.str(); + if (json.find("result") != json.not_found()) { + auto json_result = json.get_child("result"); + if (json_result.count("chain")) { + result = json_result.get("chain"); + } + } } - return str; + return result; } void bitcoin_rpc_client::importmulti(const std::vector &address_or_script_array, const bool rescan) { @@ -255,8 +304,6 @@ std::string bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) { } return json.get("result"); - std::cout << "Result of transaction: " << std::endl; - std::cout << ss.str() << std::endl; } return str; @@ -288,37 +335,126 @@ bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_ else return true; } - -uint64_t bitcoin_libbitcoin_client::estimatesmartfee(uint16_t conf_target) { - // TODO - return 0; +bitcoin_libbitcoin_client::bitcoin_libbitcoin_client(std::string url) : + libbitcoin_client(url) { } -std::string bitcoin_libbitcoin_client::getblock(const std::string &block_hash, int32_t verbosity) { - // TODO - return std::string(""); +uint64_t bitcoin_libbitcoin_client::estimatesmartfee(uint16_t conf_target) { + // TODO for now it's hard-coded value until final implementation + return 20000; +} + +std::vector bitcoin_libbitcoin_client::getblock(const block_data &block, int32_t verbosity) { + std::vector result; + + const libbitcoin::chain::transaction::list trx_list = block.block.transactions(); + + uint32_t vout_seq = 0; + + for (const auto &tx : trx_list) { + for (const auto &o : tx.outputs()) { + std::vector address_list; + + libbitcoin::wallet::payment_address::list addresses; + if (is_test_net) { + addresses = o.addresses(libbitcoin::wallet::payment_address::testnet_p2kh, + libbitcoin::wallet::payment_address::testnet_p2sh); + } else { + addresses = o.addresses(); + } + + for (auto &payment_address : addresses) { + std::stringstream ss; + ss << payment_address; + address_list.emplace_back(ss.str()); + } + + // addres list consists usual of one element + for (auto &address : address_list) { + const auto address_base58 = address; + info_for_vin vin; + vin.out.hash_tx = libbitcoin::config::hash256(tx.hash()).to_string(); + // TODO check this condition here + // amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); + // vin.out.amount = std::stoll(amount); + vin.out.amount = o.value(); + vin.out.n_vout = vout_seq++; + vin.address = address_base58; + result.push_back(vin); + } + } + } + + return result; } btc_tx bitcoin_libbitcoin_client::getrawtransaction(const std::string &txid, const bool verbose) { - // TODO btc_tx tx; + + std::string tx_hash; + uint32_t confirmitions; + + libbitcoin::chain::output::list outs = get_transaction(txid, tx_hash, confirmitions); + + if (tx_hash.empty()) { + return tx; + } + + tx.tx_txid = tx_hash; + tx.tx_confirmations = confirmitions; + + uint64_t tx_vout_index = 0; + + for (auto &out : outs) { + btc_txin tx_in; + tx_in.tx_vout = tx_vout_index++; + + libbitcoin::wallet::payment_address::list addresses; + if (is_test_net) { + addresses = out.addresses(libbitcoin::wallet::payment_address::testnet_p2kh, + libbitcoin::wallet::payment_address::testnet_p2sh); + } else { + addresses = out.addresses(); + } + + for (auto &address : addresses) { + + std::stringstream ss; + ss << address; + tx_in.tx_address.emplace_back(ss.str()); + } + + // TODO check this condition here + // std::string tx_amount_s = input.second.get("value"); + // tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end()); + // tx_in.tx_amount = std::stoll(tx_amount_s); + + tx_in.tx_amount = out.value(); + tx.tx_in_list.emplace_back(tx_in); + } + return tx; } void bitcoin_libbitcoin_client::getnetworkinfo() { - // TODO + // This function is only used for bitcoind client in order of getting + // version of bitcoin client. Version is used for extracting addresses or address + // which is not important for libbitcoin client } std::string bitcoin_libbitcoin_client::getblockchaininfo() { - // TODO - return std::string(""); + if (get_is_test_net()) { + is_test_net = true; + return "regtest"; + } + + return ""; } std::vector bitcoin_libbitcoin_client::listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf, const uint32_t maxconf) { std::vector result; - std::vector outputs = list_unspent(address, 0); - uint8_t counter{0}; + std::vector outputs = listunspent(address, transfer_amount); for (auto &output : outputs) { btc_txout txo; txo.txid_ = output.hash; @@ -327,28 +463,20 @@ std::vector bitcoin_libbitcoin_client::listunspent_by_address_and_amo result.push_back(txo); } - std::cout << "libbitcoin: ===>" << std::endl; - for (auto &res : result) { - std::cout << "Hash: " << res.txid_ << " out_num: " << res.out_num_ << " amount: " << res.amount_ << std::endl; - } - std::cout << "==================================>" << std::endl; - return result; } std::string bitcoin_libbitcoin_client::sendrawtransaction(const std::string &tx_hex) { - // TODO - return std::string(""); + std::string res = send_transaction(tx_hex); + return res; } // ============================================================================= zmq_listener::zmq_listener(std::string _ip, uint32_t _zmq) : - ip(_ip), - zmq_port(_zmq), + zmq_listener_base(_ip, _zmq), ctx(1), - socket(ctx, ZMQ_SUB), - stopped(false) { + socket(ctx, ZMQ_SUB) { } void zmq_listener::start() { @@ -399,7 +527,9 @@ void zmq_listener::handle_zmq() { } const auto header = std::string(static_cast(msg[0].data()), msg[0].size()); const auto block_hash = boost::algorithm::hex(std::string(static_cast(msg[1].data()), msg[1].size())); - event_received(block_hash); + block_data event_data; + event_data.block_hash = block_hash; + event_received(event_data); } } catch (zmq::error_t &e) { elog("handle_zmq recv_multipart exception ${str}", ("str", e.what())); @@ -411,6 +541,58 @@ void zmq_listener::handle_zmq() { // ============================================================================= +// ============================================================================= + +zmq_listener_libbitcoin::zmq_listener_libbitcoin(std::string _ip, uint32_t _zmq_port) : + zmq_listener_base(_ip, _zmq_port), + socket(context, libbitcoin::protocol::zmq::socket::role::subscriber) { +} + +zmq_listener_libbitcoin::~zmq_listener_libbitcoin() { + stopped.store(true); + thr.join(); +} + +void zmq_listener_libbitcoin::start() { + + libbitcoin::config::endpoint address(ip, zmq_port); + socket.connect(address); + thr = std::thread(&zmq_listener_libbitcoin::handle_block, this); +} + +void zmq_listener_libbitcoin::handle_block() { + poller.add(socket); + + while (!stopped.load()) { + // TODO change the hard-coded value + const auto identifiers = poller.wait(500); + + if (identifiers.contains(socket.id())) { + libbitcoin::protocol::zmq::message message; + + socket.receive(message); + + std::vector data; + for (int i = 0; i < 3; i++) { + data.clear(); + message.dequeue(data); + } + + libbitcoin::chain::block block; + block.from_data(data, true); + + block_data event_data; + event_data.block_hash = libbitcoin::config::hash256(block.hash()).to_string(); + event_data.block = std::move(block); + event_received(event_data); + } + } + + ilog("zmq_listener_libbitcoin thread finished"); +} + +// ============================================================================= + 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; @@ -419,8 +601,8 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain debug_rpc_calls = options.at("debug-rpc-calls").as(); } - if (options.count("use-p2wsh-address")) { - use_p2wsh_address = options.at("use-p2wsh-address").as(); + if (options.count("use-bitcoind-client")) { + use_bitcoind_client = options.at("use-bitcoind-client").as(); } ip = options.at("bitcoin-node-ip").as(); @@ -449,44 +631,47 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain } } - std::string url = ip + ":" + std::to_string(rpc_port); - if (wallet.length() > 0) { - url = url + "/wallet/" + wallet; + std::string url = ip; + + if (use_bitcoind_client) { + url = ip + ":" + std::to_string(rpc_port); + if (wallet.length() > 0) { + url = url + "/wallet/" + wallet; + } + bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); + if (!wallet.empty()) { + bitcoin_client->loadwallet(wallet); + } + + listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); + listener->start(); + listener->event_received.connect([this](const block_data &event_data) { + std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); + }); + + } else { + bitcoin_client = std::unique_ptr(new bitcoin_libbitcoin_client("tcp://localhost")); + + listener = std::unique_ptr(new zmq_listener_libbitcoin("tcp://localhost", 9093)); + listener->start(); + listener->event_received.connect([this](const block_data &event_data) { + std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); + }); } - bitcoin_client = std::unique_ptr(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls)); - lib_bitcoin_client = std::unique_ptr(new bitcoin_libbitcoin_client()); - if (!wallet.empty()) { - bitcoin_client->loadwallet(wallet); - } - - std::string blockchain_info = bitcoin_client->getblockchaininfo(); - std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end())); - boost::property_tree::ptree bci_json; - boost::property_tree::read_json(bci_ss, bci_json); + std::string chain_info = bitcoin_client->getblockchaininfo(); using namespace bitcoin; network_type = bitcoin_address::network::mainnet; - if (bci_json.count("chain")) { - std::string chain = bci_json.get("chain"); - if (chain.length() > 0) { - if (chain == "test") { - network_type = bitcoin_address::network::testnet; - } else if (chain == "regtest") { - network_type = bitcoin_address::network::regtest; - } - } + if (chain_info == "test") { + network_type = bitcoin_address::network::testnet; + } else if (chain_info == "regtest") { + network_type = bitcoin_address::network::regtest; } bitcoin_client->getnetworkinfo(); - listener = std::unique_ptr(new zmq_listener(ip, zmq_port)); - listener->start(); - listener->event_received.connect([this](const std::string &event_data) { - std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach(); - }); - database.changed_objects.connect([this](const vector &ids, const flat_set &accounts) { on_changed_objects(ids, accounts); }); @@ -616,7 +801,6 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1)); btc_tx tx = bitcoin_client->getrawtransaction(swdo_txid, true); - // lib_bitcoin_client->get_transaction(swdo_txid); if (!tx.tx_in_list.empty()) { @@ -872,7 +1056,7 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key))); payment_type payment_type_address = payment_type::P2SH_WSH; - if (use_p2wsh_address) { + if (use_bitcoind_client) { payment_type_address = payment_type::P2WSH; } @@ -1022,6 +1206,8 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain btc_tx tx = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true); if (tx.tx_in_list.empty()) { + // This case will result with segmentation fault. + // TODO check if that happened before introducing libbitcoin return false; } @@ -1035,7 +1221,7 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain } payment_type payment_type_address = payment_type::P2SH_WSH; - if (use_p2wsh_address) { + if (use_bitcoind_client) { payment_type_address = payment_type::P2WSH; } @@ -1079,7 +1265,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s } payment_type payment_type_address = payment_type::P2SH_WSH; - if (use_p2wsh_address) { + if (use_bitcoind_client) { payment_type_address = payment_type::P2WSH; } btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address); @@ -1122,7 +1308,6 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con uint64_t total_amount = 0.0; std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); - inputs = lib_bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0); if (inputs.size() == 0) { elog("Failed to find UTXOs to spend for ${pw}", ("pw", prev_pw_address)); @@ -1213,7 +1398,6 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s int64_t total_amount = 0; std::vector inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); - inputs = lib_bitcoin_client->listunspent_by_address_and_amount(pw_address, 0); if (inputs.size() == 0) { elog("Failed to find UTXOs to spend for ${pw}", ("pw", pw_address)); @@ -1321,7 +1505,7 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran // Add redeemscripts to vins and make tx ready for sending sign_witness_transaction_finalize(tx, redeem_scripts, false); - if (!use_p2wsh_address) { + if (!use_bitcoind_client) { // get witness script from redeem script bitcoin::bytes redeem_bytes = parse_hex(redeem_script); fc::sha256 sha = fc::sha256::hash(&redeem_bytes[0], redeem_bytes.size()); @@ -1334,20 +1518,20 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran std::string final_tx_hex = fc::to_hex(pack(tx)); std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); - // lib_bitcoin_client->send_transaction(final_tx_hex); + + if (res.empty()) { + return res; + } return tx.get_txid().str(); } -void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) { +void sidechain_net_handler_bitcoin::handle_event(const block_data &event_data) { - std::string block = bitcoin_client->getblock(event_data); - if (block.empty()) - return; + auto vins = bitcoin_client->getblock(event_data); - add_to_son_listener_log("BLOCK : " + event_data); + add_to_son_listener_log("BLOCK : " + event_data.block_hash); - auto vins = extract_info_from_block(block); scoped_lock interlock(event_handler_mutex); const auto &sidechain_addresses_idx = database.get_index_type().indices().get(); @@ -1405,7 +1589,7 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); payment_type payment_type_address = payment_type::P2SH_WSH; - if (use_p2wsh_address) { + if (use_bitcoind_client) { payment_type_address = payment_type::P2WSH; } btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type, payment_type_address); @@ -1413,49 +1597,6 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons return fc::to_hex(deposit_addr.get_redeem_script()); } -std::vector sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) { - std::stringstream ss(_block); - boost::property_tree::ptree json; - boost::property_tree::read_json(ss, json); - - auto json_result = json.get_child_optional("result"); - - std::vector result; - - for (const auto &tx_child : json_result.get().get_child("tx")) { - const auto &tx = tx_child.second; - - for (const auto &o : tx.get_child("vout")) { - const auto script = o.second.get_child("scriptPubKey"); - std::vector address_list; - - if (script.count("address")) { - address_list.emplace_back(script.get("address")); - } else if (script.count("addresses")) { - for (const auto &addr : script.get_child("addresses")) { - address_list.emplace_back(addr.second.get_value()); - } - } else { - continue; - } - - for (auto &address : address_list) { - const auto address_base58 = address; - info_for_vin vin; - vin.out.hash_tx = tx.get_child("txid").get_value(); - string amount = o.second.get_child("value").get_value(); - amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end()); - vin.out.amount = std::stoll(amount); - vin.out.n_vout = o.second.get_child("n").get_value(); - vin.address = address_base58; - result.push_back(vin); - } - } - } - - return result; -} - void sidechain_net_handler_bitcoin::on_changed_objects(const vector &ids, const flat_set &accounts) { fc::time_point now = fc::time_point::now(); int64_t time_to_next_changed_objects_processing = 5000;