libbitcoin client and bitcoind
This commit is contained in:
parent
6aee0c6f82
commit
6b67fe76a8
6 changed files with 444 additions and 170 deletions
|
|
@ -3,28 +3,62 @@
|
|||
|
||||
#include <system_error>
|
||||
|
||||
#include <bitcoin/system/config/hash256.hpp>
|
||||
#include <bitcoin/explorer/config/transaction.hpp>
|
||||
#include <bitcoin/system/config/hash256.hpp>
|
||||
|
||||
#include <boost/xpressive/xpressive.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
|
||||
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 = "^((?P<Protocol>https|http|tcp):\\/\\/)?(?P<Host>[a-zA-Z0-9\\-\\.]+)(:(?P<Port>\\d{1,5}))?(?P<Target>\\/.+)?";
|
||||
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<list_unspent_replay> libbitcoin_client::list_unspent(std::string address, double amount) {
|
||||
std::vector<list_unspent_replay> libbitcoin_client::listunspent(std::string address, double amount) {
|
||||
std::vector<list_unspent_replay> result;
|
||||
|
||||
auto error_handler = [&](const std::error_code &ec) {
|
||||
|
|
@ -78,12 +123,36 @@ std::vector<list_unspent_replay> 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
|
||||
|
||||
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
|
||||
|
|
@ -1,9 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <bitcoin/client/obelisk_client.hpp>
|
||||
#include <bitcoin/client/socket_stream.hpp>
|
||||
#include <bitcoin/system/chain/block.hpp>
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
#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<void(const libbitcoin::chain::block &)>
|
||||
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_replay> 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<list_unspent_replay> 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
|
||||
|
|
@ -57,7 +57,7 @@ protected:
|
|||
sidechain_type sidechain;
|
||||
|
||||
bool debug_rpc_calls;
|
||||
bool use_p2wsh_address;
|
||||
bool use_bitcoind_client;
|
||||
|
||||
std::map<std::string, std::string> private_keys;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
|
||||
#include <graphene/peerplays_sidechain/common/rpc_client.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
|
@ -38,7 +38,13 @@ public:
|
|||
std::vector<btc_txin> 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<info_for_vin> 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<btc_txout> 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<info_for_vin> 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<info_for_vin> 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<btc_txout> 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<void(const block_data &)> 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<void(const std::string &)> event_received;
|
||||
|
||||
private:
|
||||
void handle_zmq();
|
||||
std::vector<zmq::message_t> 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_rpc_client> bitcoin_client;
|
||||
std::unique_ptr<bitcoin_libbitcoin_client> lib_bitcoin_client;
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
std::unique_ptr<bitcoin_client_base> bitcoin_client;
|
||||
std::unique_ptr<zmq_listener_base> listener;
|
||||
|
||||
fc::future<void> 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<info_for_vin> extract_info_from_block(const std::string &_block);
|
||||
void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
|
|||
cli.add_options()("sidechain-retry-threshold", bpo::value<uint16_t>()->default_value(150), "Sidechain retry throttling threshold");
|
||||
|
||||
cli.add_options()("debug-rpc-calls", bpo::value<bool>()->default_value(false), "Outputs RPC calls to console");
|
||||
cli.add_options()("use-p2wsh-address", bpo::value<bool>()->default_value(false), "Use p2wsh address instead of p2sh address");
|
||||
cli.add_options()("use-bitcoind-client", bpo::value<bool>()->default_value(false), "Use bitcoind client instead of libbitcoin client");
|
||||
|
||||
cli.add_options()("bitcoin-sidechain-enabled", bpo::value<bool>()->default_value(false), "Bitcoin sidechain handler enabled");
|
||||
cli.add_options()("bitcoin-node-ip", bpo::value<string>()->default_value("127.0.0.1"), "IP address of Bitcoin node");
|
||||
|
|
|
|||
|
|
@ -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<info_for_vin> 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<info_for_vin> 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<std::string> address_list;
|
||||
|
||||
if (script.count("address")) {
|
||||
address_list.emplace_back(script.get<std::string>("address"));
|
||||
} else if (script.count("addresses")) {
|
||||
for (const auto &addr : script.get_child("addresses")) {
|
||||
address_list.emplace_back(addr.second.get_value<std::string>());
|
||||
}
|
||||
} 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<std::string>();
|
||||
string amount = o.second.get_child("value").get_value<std::string>();
|
||||
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<uint32_t>();
|
||||
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<std::string>("chain");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
return result;
|
||||
}
|
||||
|
||||
void bitcoin_rpc_client::importmulti(const std::vector<multi_params> &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<std::string>("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<info_for_vin> bitcoin_libbitcoin_client::getblock(const block_data &block, int32_t verbosity) {
|
||||
std::vector<info_for_vin> 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<std::string> 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<std::string>("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<btc_txout> 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<btc_txout> result;
|
||||
std::vector<list_unspent_replay> outputs = list_unspent(address, 0);
|
||||
uint8_t counter{0};
|
||||
std::vector<list_unspent_replay> outputs = listunspent(address, transfer_amount);
|
||||
for (auto &output : outputs) {
|
||||
btc_txout txo;
|
||||
txo.txid_ = output.hash;
|
||||
|
|
@ -327,28 +463,20 @@ std::vector<btc_txout> 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<char *>(msg[0].data()), msg[0].size());
|
||||
const auto block_hash = boost::algorithm::hex(std::string(static_cast<char *>(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<uint8_t> 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<bool>();
|
||||
}
|
||||
|
||||
if (options.count("use-p2wsh-address")) {
|
||||
use_p2wsh_address = options.at("use-p2wsh-address").as<bool>();
|
||||
if (options.count("use-bitcoind-client")) {
|
||||
use_bitcoind_client = options.at("use-bitcoind-client").as<bool>();
|
||||
}
|
||||
|
||||
ip = options.at("bitcoin-node-ip").as<std::string>();
|
||||
|
|
@ -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<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls));
|
||||
if (!wallet.empty()) {
|
||||
bitcoin_client->loadwallet(wallet);
|
||||
}
|
||||
|
||||
listener = std::unique_ptr<zmq_listener>(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<bitcoin_libbitcoin_client>(new bitcoin_libbitcoin_client("tcp://localhost"));
|
||||
|
||||
listener = std::unique_ptr<zmq_listener_libbitcoin>(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<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls));
|
||||
lib_bitcoin_client = std::unique_ptr<bitcoin_libbitcoin_client>(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<std::string>("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<zmq_listener>(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<object_id_type> &ids, const flat_set<account_id_type> &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<btc_txout> 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<btc_txout> 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<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
|
||||
|
||||
|
|
@ -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<info_for_vin> 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<info_for_vin> 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<std::string> address_list;
|
||||
|
||||
if (script.count("address")) {
|
||||
address_list.emplace_back(script.get<std::string>("address"));
|
||||
} else if (script.count("addresses")) {
|
||||
for (const auto &addr : script.get_child("addresses")) {
|
||||
address_list.emplace_back(addr.second.get_value<std::string>());
|
||||
}
|
||||
} 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<std::string>();
|
||||
string amount = o.second.get_child("value").get_value<std::string>();
|
||||
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<uint32_t>();
|
||||
vin.address = address_base58;
|
||||
result.push_back(vin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_bitcoin::on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts) {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next_changed_objects_processing = 5000;
|
||||
|
|
|
|||
Loading…
Reference in a new issue