Compare commits

...

17 commits

Author SHA1 Message Date
hirunda
53bc75a850 Fix the issue with deposit 2022-12-21 20:26:43 +01:00
hirunda
ffeff54090 Merge branch 'develop' into feature/libbitcoin-son-final 2022-12-19 19:21:47 +01:00
serkixenos
89ad18a479 Merge branch 'develop' into feature/libbitcoin-son-final 2022-10-30 03:33:44 +01:00
hirunda
8e34c3dbff Fix the issue with external fee 2022-10-17 19:07:39 +02:00
hirunda
a9b187c092 Import multi for bitcoind 2022-10-17 01:41:34 +02:00
hirunda
5e5e325333 Add external estimate fee query 2022-10-17 00:17:15 +02:00
hirunda
3a539c0d56 Add external estimate fee query 2022-10-17 00:16:55 +02:00
Davor Hirunda
d535aabb5c Revert "Back importmulti for bitcoind" 2022-10-17 00:16:39 +02:00
hirunda
985a0f3829 Back importmulti for bitcoind 2022-10-17 00:16:19 +02:00
hirunda
5c638fff71 Libbitcoin client 2022-10-17 00:15:42 +02:00
hirunda
8d5f84f46b Merge branch 'develop' into feature/libbitcoin-son-final 2022-10-17 00:03:55 +02:00
hirunda
9b047ae703 Resolving bug with vout wrong sequence 2022-09-22 01:21:15 +02:00
hirunda
6b67fe76a8 libbitcoin client and bitcoind 2022-09-20 01:06:33 +02:00
hirunda
6aee0c6f82 Moving json transaction parsing
Moving json transaction parsing in

getrawtransaction and forming btc_tx object
2022-09-09 00:07:53 +02:00
hirunda
a47498f642 libbitcoin client work in progress 2022-09-06 14:12:01 +02:00
serkixenos
789c0547cf Merge branch 'feature/417/Add_support_in_bitcoin_for_P2SH-P2WSH_address_format' into 'feature/libbitcoin-son'
Add support for P2SH-P2WSH address format

See merge request PBSA/peerplays!140
2022-09-05 13:27:27 +00:00
Davor Hirunda
3e0a21bde7 Add support for P2SH-P2WSH address format 2022-09-05 13:27:26 +00:00
11 changed files with 998 additions and 248 deletions

4
libraries/plugins/peerplays_sidechain/CMakeLists.txt Executable file → Normal file
View file

@ -16,6 +16,8 @@ add_library( peerplays_sidechain
bitcoin/segwit_addr.cpp
bitcoin/utils.cpp
bitcoin/sign_bitcoin_transaction.cpp
bitcoin/libbitcoin_client.cpp
bitcoin/estimate_fee_external.cpp
common/rpc_client.cpp
common/utils.cpp
ethereum/encoders.cpp
@ -42,7 +44,7 @@ endif()
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE)
target_link_libraries( peerplays_sidechain PRIVATE graphene_plugin sha3 zmq )
target_link_libraries( peerplays_sidechain PRIVATE curl graphene_plugin sha3 zmq bitcoin-system bitcoin-protocol bitcoin-client bitcoin-explorer )
target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -242,12 +242,12 @@ bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) {
}
btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network ntype) {
network ntype, payment_type type) {
network_type = ntype;
this->type = type;
create_redeem_script(keys_data);
create_witness_script();
create_segwit_address();
type = payment_type::P2WSH;
}
void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
@ -278,26 +278,43 @@ void btc_weighted_multisig_address::create_witness_script() {
script_builder builder;
builder << op::_0;
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
witness_script_ = builder;
}
void btc_weighted_multisig_address::create_segwit_address() {
std::string hrp;
address_types byte_version;
switch (network_type) {
case (network::mainnet):
hrp = "bc";
byte_version = address_types::MAINNET_SCRIPT;
break;
case (network::testnet):
hrp = "tb";
byte_version = address_types::TESTNET_SCRIPT;
break;
case (network::regtest):
hrp = "bcrt";
byte_version = address_types::TESTNET_SCRIPT;
break;
}
if (type == payment_type::P2WSH) {
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
address = segwit_addr::encode(hrp, 0, hash_data);
} else if (type == payment_type::P2SH_WSH) {
fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size());
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
bytes address_bytes(1, byte_version); // 1 byte version
address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end());
fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum
address = fc::to_base58(address_bytes);
} else {
wlog("Unsupported payment type of address");
}
}
btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data,
@ -353,12 +370,12 @@ void btc_one_or_m_of_n_multisig_address::create_segwit_address() {
btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data,
const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
bitcoin_address::network ntype) {
bitcoin_address::network ntype, payment_type type) {
network_type = ntype;
this->type = type;
create_redeem_script(user_key_data, keys_data);
create_witness_script();
create_segwit_address();
type = payment_type::P2WSH;
}
void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
@ -398,20 +415,39 @@ void btc_one_or_weighted_multisig_address::create_witness_script() {
void btc_one_or_weighted_multisig_address::create_segwit_address() {
std::string hrp;
address_types byte_version;
switch (network_type) {
case (network::mainnet):
byte_version = address_types::MAINNET_SCRIPT;
hrp = "bc";
break;
case (network::testnet):
byte_version = address_types::TESTNET_SCRIPT;
hrp = "tb";
break;
case (network::regtest):
byte_version = address_types::TESTNET_SCRIPT;
hrp = "bcrt";
break;
}
if (type == payment_type::P2WSH) {
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
address = segwit_addr::encode(hrp, 0, hash_data);
} else if (type == payment_type::P2SH_WSH) {
fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size());
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
bytes address_bytes(1, byte_version); // 1 byte version test net
address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end());
fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum
address = fc::to_base58(address_bytes);
} else {
elog("Unsupported payment type of address");
}
}
btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, bitcoin_address::network ntype) :

View file

@ -0,0 +1,155 @@
#include <graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fc/log/logger.hpp>
#include <iostream>
namespace graphene {
namespace peerplays_sidechain {
static size_t writeFunction(void *ptr, size_t size, size_t nmemb, std::string *data) {
data->append((char *)ptr, size * nmemb);
return size * nmemb;
}
estimate_fee_external::estimate_fee_external() {
curl = curl_easy_init();
}
estimate_fee_external::~estimate_fee_external() {
curl_easy_cleanup(curl);
}
uint64_t estimate_fee_external::get_fee_external(uint16_t target_block) {
this->target_block = target_block;
for (auto &url_fee_parser : url_get_fee_parsers) {
response = get_response(url_fee_parser.first);
uint64_t fee = url_fee_parser.second();
if (fee != 0) {
return fee;
}
}
return 0;
}
std::string estimate_fee_external::get_response(std::string url) {
std::string response;
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl, CURLOPT_USERPWD, "user:pass");
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.42.0");
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunction);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_perform(curl);
}
return response;
}
uint64_t estimate_fee_external::parse_and_get_fee_1() {
//"https://www.bitgo.com/api/v2/btc/tx/fee"
uint64_t founded_fee = 0;
if (response.empty()) {
return founded_fee;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
for (const auto &tx_child : response_pt.get_child("feeByBlockTarget")) {
const auto &block_num = tx_child.first.data();
const auto &fee = tx_child.second.data();
founded_fee = std::stoi(fee);
if (std::stoi(block_num) > target_block) {
return founded_fee;
}
}
return founded_fee;
}
uint64_t estimate_fee_external::parse_and_get_fee_2() {
// https://bitcoiner.live/api/fees/estimates/latest
uint64_t founded_fee = 0;
if (response.empty()) {
return founded_fee;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
for (const auto &tx_child : response_pt.get_child("estimates")) {
const auto &time_str = tx_child.first.data();
auto time = std::stoi(time_str);
auto block_num = time / 10;
if (tx_child.second.count("sat_per_vbyte")) {
auto founded_fee_str = tx_child.second.get_child("sat_per_vbyte").data();
founded_fee = std::stoi(founded_fee_str) * 1000;
}
if (block_num > target_block) {
return founded_fee;
}
}
return founded_fee;
}
uint64_t estimate_fee_external::parse_and_get_fee_3() {
// https://api.blockchain.info/mempool/fees
if (response.empty()) {
return 0;
}
std::stringstream response_ss(response);
boost::property_tree::ptree response_pt;
boost::property_tree::read_json(response_ss, response_pt);
if (response_pt.get_child("limits").count("min") && response_pt.get_child("limits").count("max")) {
auto limits_min_str = response_pt.get_child("limits.min").data();
auto limits_max_str = response_pt.get_child("limits.max").data();
auto limits_min = std::stoi(limits_min_str);
auto limits_max = std::stoi(limits_max_str);
auto priority_max = (limits_max - (limits_min - 1)) / 2;
if (response_pt.count("regular") && response_pt.count("priority")) {
auto regular_str = response_pt.get_child("regular").data();
auto priority_str = response_pt.get_child("priority").data();
auto regular = std::stoi(regular_str);
auto priority = std::stoi(priority_str);
if (target_block > priority_max) {
return regular * 1000;
} else {
return priority * 1000;
}
}
}
return 0;
}
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,156 @@
#include <graphene/peerplays_sidechain/bitcoin/libbitcoin_client.hpp>
#include <system_error>
#include <bitcoin/explorer/config/transaction.hpp>
#include <bitcoin/system/config/hash256.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/log/logger.hpp>
namespace graphene { namespace peerplays_sidechain {
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;
libbitcoin::config::endpoint address(final_url, port_num);
libbitcoin::client::connection_type connection;
connection.retries = LIBBITCOIN_SERVER_RETRIES;
connection.server = address;
if (!obelisk_client.connect(connection)) {
elog("Can't connect libbitcoin for url: ${url}", ("url", final_url));
}
is_connected = true;
}
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()));
};
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();
return res;
}
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_hash: ${hash} ${error_code}", ("hash", tx_id)("error_code", ec.message()));
};
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 hash = libbitcoin::config::hash256(tx_id);
// 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::listunspent(std::string address, double amount) {
std::vector<list_unspent_replay> result;
auto error_handler = [&](const std::error_code &ec) {
elog("error on list_unspent ${error_code}", ("error_code", ec.message()));
};
auto replay_handler = [&](const libbitcoin::chain::points_value &points) {
for (auto &point : points.points) {
list_unspent_replay output;
output.hash = libbitcoin::config::hash256(point.hash()).to_string();
output.value = point.value();
output.index = point.index();
result.emplace_back(output);
}
};
libbitcoin::wallet::payment_address payment_address(address);
uint64_t satoshi = 100000000 * amount;
obelisk_client.blockchain_fetch_unspent_outputs(error_handler,
replay_handler, payment_address, satoshi, libbitcoin::wallet::select_outputs::algorithm::individual);
obelisk_client.wait();
return result;
}
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

View file

@ -8,6 +8,14 @@ using namespace graphene::chain;
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15
enum address_types { MAINNET_SCRIPT = 5,
TESTNET_SCRIPT = 196 };
enum script_op {
OP_0 = 0x00,
OP_PUSH = 0x20,
OP_SIZE_34 = 0x22
};
class bitcoin_address {
@ -96,9 +104,6 @@ private:
void create_address();
public:
enum address_types { MAINNET_SCRIPT = 5,
TESTNET_SCRIPT = 196 };
enum { OP_0 = 0x00,
OP_EQUAL = 0x87,
OP_HASH160 = 0xa9,
@ -145,7 +150,7 @@ public:
btc_weighted_multisig_address() = default;
btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network network_type = network::regtest);
network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
bytes get_redeem_script() const {
return redeem_script_;
@ -190,7 +195,7 @@ class btc_one_or_weighted_multisig_address : public bitcoin_address {
public:
btc_one_or_weighted_multisig_address() = default;
btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
network network_type = network::regtest);
network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH);
bytes get_redeem_script() const {
return redeem_script_;
}

View file

@ -0,0 +1,36 @@
#pragma once
#include <curl/curl.h>
#include <functional>
#include <map>
typedef std::function<uint64_t()> get_fee_func_type;
namespace graphene { namespace peerplays_sidechain {
class estimate_fee_external {
public:
estimate_fee_external();
~estimate_fee_external();
uint64_t get_fee_external(uint16_t target_block);
private:
std::string get_response(std::string url);
// Here add your custom parser for external url. Take care of incremental name
// and populate the list of url_parsers bellow paired with the function
uint64_t parse_and_get_fee_1();
uint64_t parse_and_get_fee_2();
uint64_t parse_and_get_fee_3();
const std::map<std::string, get_fee_func_type> url_get_fee_parsers{
{"https://www.bitgo.com/api/v2/btc/tx/fee", std::bind(&estimate_fee_external::parse_and_get_fee_1, this)},
{"https://bitcoiner.live/api/fees/estimates/latest", std::bind(&estimate_fee_external::parse_and_get_fee_2, this)},
{"https://api.blockchain.info/mempool/fees", std::bind(&estimate_fee_external::parse_and_get_fee_3, this)}};
std::string response;
uint16_t target_block;
CURL *curl{nullptr};
};
}} // namespace graphene::peerplays_sidechain

View file

@ -0,0 +1,47 @@
#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;
uint32_t index;
};
class libbitcoin_client {
public:
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

View file

@ -59,6 +59,7 @@ protected:
sidechain_type sidechain;
bool debug_rpc_calls;
bool use_bitcoind_client;
std::map<std::string, std::string> private_keys;

View file

@ -2,6 +2,7 @@
#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>
@ -13,6 +14,7 @@
#include <fc/network/http/connection.hpp>
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
#include <graphene/peerplays_sidechain/bitcoin/estimate_fee_external.hpp>
namespace graphene { namespace peerplays_sidechain {
@ -23,7 +25,27 @@ public:
uint64_t amount_;
};
class bitcoin_rpc_client : public rpc_client {
class btc_txin {
public:
std::vector<std::string> tx_address;
uint64_t tx_amount;
uint64_t tx_vout;
};
class btc_tx {
public:
std::string tx_txid;
uint32_t tx_confirmations;
std::vector<btc_txin> tx_in_list;
};
class block_data {
public:
std::string block_hash;
libbitcoin::chain::block block;
};
class bitcoin_client_base {
public:
enum class multi_type {
script,
@ -41,14 +63,34 @@ public:
std::string label;
};
virtual uint64_t estimatesmartfee(uint16_t conf_target = 128) = 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 void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true) {;};
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_base, public rpc_client {
public:
public:
bitcoin_rpc_client(std::string _url, std::string _user, std::string _password, bool _debug_rpc_calls);
std::string createwallet(const std::string &wallet_name);
uint64_t estimatesmartfee(uint16_t conf_target = 128);
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
std::string getrawtransaction(const std::string &txid, const bool verbose = false);
std::string getnetworkinfo();
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();
void importmulti(const std::vector<multi_params> &address_or_script_array, const bool rescan = true);
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
@ -60,35 +102,75 @@ public:
private:
std::string ip;
uint32_t rpc_port;
std::string user;
std::string password;
std::string wallet_name;
std::string wallet_password;
uint32_t bitcoin_major_version;
};
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::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;
std::unique_ptr<estimate_fee_external> estimate_fee_ext;
};
// =============================================================================
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;
};
// =============================================================================
@ -109,16 +191,18 @@ public:
virtual optional<asset> estimate_withdrawal_transaction_fee() const override;
private:
std::string ip;
uint32_t zmq_port;
std::string bitcoin_node_ip;
std::string libbitcoin_server_ip;
uint32_t libbitcoin_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;
std::unique_ptr<bitcoin_rpc_client> 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;
@ -138,9 +222,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);
};

View file

@ -175,6 +175,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-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");
@ -183,6 +184,8 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options(
cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user");
cli.add_options()("bitcoin-node-rpc-password", bpo::value<string>()->default_value("1"), "Bitcoin RPC password");
cli.add_options()("bitcoin-wallet-name", bpo::value<string>(), "Bitcoin wallet name");
cli.add_options()("libbitcoin-server-ip", bpo::value<string>()->default_value("127.0.0.1"), "Libbitcoin server IP address");
cli.add_options()("libbitcoin-server-zmq-port", bpo::value<uint32_t>()->default_value(9093), "ZMQ port of libbitcoin server");
cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password");
cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")),
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
@ -247,12 +250,14 @@ void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_opt
}
sidechain_enabled_bitcoin = options.at("bitcoin-sidechain-enabled").as<bool>();
config_ready_bitcoin = options.count("bitcoin-node-ip") &&
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") &&
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") &&
options.count("bitcoin-wallet-name") && options.count("bitcoin-wallet-password") &&
options.count("bitcoin-private-key");
if (sidechain_enabled_bitcoin && !config_ready_bitcoin) {
config_ready_bitcoin = (((options.count("libbitcoin-server-ip") && options.count("libbitcoin-server-zmq-port")) ||
(options.count("bitcoin-node-ip") && options.count("bitcoin-node-zmq-port") &&
options.count("bitcoin-node-rpc-port") && options.count("bitcoin-node-rpc-user") &&
options.count("bitcoin-node-rpc-password") && options.count("bitcoin-wallet-name") &&
options.count("bitcoin-wallet-password"))) &&
options.count("bitcoin-private-key"));
if (!config_ready_bitcoin) {
wlog("Haven't set up Bitcoin sidechain parameters");
}

View file

@ -29,11 +29,6 @@ bitcoin_rpc_client::bitcoin_rpc_client(std::string _url, std::string _user, std:
rpc_client(_url, _user, _password, _debug_rpc_calls) {
}
std::string bitcoin_rpc_client::createwallet(const std::string &wallet_name) {
const std::string params = std::string("[\"") + wallet_name + std::string("\"]");
return send_post_request("createwallet", params, debug_rpc_calls);
}
uint64_t bitcoin_rpc_client::estimatesmartfee(uint16_t conf_target) {
const std::string params = std::string("[") + std::to_string(conf_target) + std::string("]");
const std::string str = send_post_request("estimatesmartfee", params, debug_rpc_calls);
@ -58,36 +53,130 @@ 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) {
const std::string params = std::string("[\"") + block_hash + std::string("\",") + std::to_string(verbosity) + std::string("]");
return send_post_request("getblock", params, debug_rpc_calls);
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);
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;
}
std::string bitcoin_rpc_client::getnetworkinfo() {
static const std::string params = std::string("[]");
return send_post_request("getnetworkinfo", params, debug_rpc_calls);
void bitcoin_rpc_client::getnetworkinfo() {
std::string params = std::string("[]");
std::string str = send_post_request("getnetworkinfo", params, debug_rpc_calls);
std::stringstream network_info_ss(str);
boost::property_tree::ptree network_info_json;
boost::property_tree::read_json(network_info_ss, network_info_json);
bitcoin_major_version = network_info_json.get<uint32_t>("result.version") / 10000;
ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version));
}
std::string bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) {
const std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]");
return send_post_request("getrawtransaction", params, debug_rpc_calls);
btc_tx bitcoin_rpc_client::getrawtransaction(const std::string &txid, const bool verbose) {
std::string params = std::string("[\"") + txid + std::string("\",") + (verbose ? "true" : "false") + std::string("]");
std::string str = send_post_request("getrawtransaction", params, debug_rpc_calls);
btc_tx tx;
std::stringstream tx_ss(str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
if (tx_json.count("error") && tx_json.get_child("error").empty()) {
std::string tx_txid = tx_json.get<std::string>("result.txid");
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations");
tx.tx_txid = tx_txid;
tx.tx_confirmations = tx_confirmations;
for (auto &input : tx_json.get_child("result.vout")) {
btc_txin tx_in;
std::string tx_vout_s = input.second.get<std::string>("n");
tx_in.tx_vout = std::stoll(tx_vout_s);
if (bitcoin_major_version > 21) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
tx_in.tx_address.emplace_back(address);
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
tx_in.tx_address.emplace_back(address.second.data());
}
}
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.tx_in_list.emplace_back(tx_in);
}
}
return tx;
}
std::string bitcoin_rpc_client::getblockchaininfo() {
static const std::string params = std::string("[]");
const 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) {
@ -243,15 +332,145 @@ bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_
else
return true;
}
bitcoin_libbitcoin_client::bitcoin_libbitcoin_client(std::string url) :
libbitcoin_client(url) {
estimate_fee_ext = std::unique_ptr<estimate_fee_external>(new estimate_fee_external());
}
uint64_t bitcoin_libbitcoin_client::estimatesmartfee(uint16_t conf_target) {
uint64_t fee = estimate_fee_ext->get_fee_external(conf_target);
if (fee != 0) {
return fee;
}
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();
for (const auto &tx : trx_list) {
uint32_t vout_seq = 0;
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();
vin.out.amount = std::floor(o.value());
vin.out.n_vout = vout_seq;
vin.address = address_base58;
result.push_back(vin);
}
vout_seq++;
}
}
return result;
}
btc_tx bitcoin_libbitcoin_client::getrawtransaction(const std::string &txid, const bool verbose) {
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_sequence = 0;
for (auto &out : outs) {
btc_txin tx_in;
tx_in.tx_vout = tx_vout_sequence++;
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());
}
tx_in.tx_amount = std::floor(out.value());
tx.tx_in_list.emplace_back(tx_in);
}
return tx;
}
void bitcoin_libbitcoin_client::getnetworkinfo() {
// 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() {
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 = listunspent(address, transfer_amount);
for (auto &output : outputs) {
btc_txout txo;
txo.txid_ = output.hash;
txo.out_num_ = output.index;
txo.amount_ = output.value;
result.push_back(txo);
}
return result;
}
std::string bitcoin_libbitcoin_client::sendrawtransaction(const std::string &tx_hex) {
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() {
@ -302,7 +521,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()));
@ -314,6 +535,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() {
std::string endpoint_address = "tcp://" + ip;
libbitcoin::config::endpoint address(endpoint_address, 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()) {
// FIXME 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;
@ -322,8 +595,14 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
debug_rpc_calls = options.at("debug-rpc-calls").as<bool>();
}
ip = options.at("bitcoin-node-ip").as<std::string>();
zmq_port = options.at("bitcoin-node-zmq-port").as<uint32_t>();
if (options.count("use-bitcoind-client")) {
use_bitcoind_client = options.at("use-bitcoind-client").as<bool>();
}
bitcoin_node_ip = options.at("bitcoin-node-ip").as<std::string>();
bitcoin_node_zmq_port = options.at("bitcoin-node-zmq-port").as<uint32_t>();
libbitcoin_server_ip = options.at("libbitcoin-server-ip").as<std::string>();
libbitcoin_zmq_port = options.at("libbitcoin-server-zmq-port").as<uint32_t>();
rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>();
rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>();
rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>();
@ -348,54 +627,39 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
}
}
std::string url = ip + ":" + std::to_string(rpc_port);
if (use_bitcoind_client) {
std::string url = bitcoin_node_ip + ":" + std::to_string(rpc_port);
if (!wallet_name.empty()) {
url = url + "/wallet/" + wallet_name;
}
bitcoin_client = std::unique_ptr<bitcoin_rpc_client>(new bitcoin_rpc_client(url, rpc_user, rpc_password, debug_rpc_calls));
if (!wallet_name.empty()) {
bitcoin_client->loadwallet(wallet_name);
}
std::string blockchain_info = bitcoin_client->getblockchaininfo();
if (blockchain_info.empty()) {
elog("No Bitcoin node running at ${url}", ("url", url));
FC_ASSERT(false);
listener = std::unique_ptr<zmq_listener>(new zmq_listener(bitcoin_node_ip, bitcoin_node_zmq_port));
} else {
bitcoin_client = std::unique_ptr<bitcoin_libbitcoin_client>(new bitcoin_libbitcoin_client(libbitcoin_server_ip));
listener = std::unique_ptr<zmq_listener_libbitcoin>(new zmq_listener_libbitcoin(libbitcoin_server_ip, libbitcoin_zmq_port));
}
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") {
if (chain_info == "test") {
network_type = bitcoin_address::network::testnet;
} else if (chain == "regtest") {
} else if (chain_info == "regtest") {
network_type = bitcoin_address::network::regtest;
}
}
}
std::string network_info_str = bitcoin_client->getnetworkinfo();
if (network_info_str.empty()) {
elog("No Bitcoin node running at ${url}", ("url", url));
FC_ASSERT(false);
}
std::stringstream network_info_ss(network_info_str);
boost::property_tree::ptree network_info_json;
boost::property_tree::read_json(network_info_ss, network_info_json);
bitcoin_client->getnetworkinfo();
bitcoin_major_version = network_info_json.get<uint32_t>("result.version") / 10000;
ilog("Bitcoin major version is: '${version}'", ("version", bitcoin_major_version));
listener = std::unique_ptr<zmq_listener>(new zmq_listener(ip, zmq_port));
listener->start();
listener->event_received.connect([this](const std::string &event_data) {
listener->event_received.connect([this](const block_data &event_data) {
std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach();
});
@ -418,7 +682,7 @@ sidechain_net_handler_bitcoin::~sidechain_net_handler_bitcoin() {
bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po) {
//ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain)));
// ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id(sidechain)));
bool should_approve = false;
@ -527,40 +791,26 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
uint64_t swdo_amount = swdo->sidechain_amount.value;
uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1));
const std::string tx_str = bitcoin_client->getrawtransaction(swdo_txid, true);
if (tx_str != "") {
std::stringstream tx_ss(tx_str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
btc_tx tx = bitcoin_client->getrawtransaction(swdo_txid, true);
if (tx_json.count("error") && tx_json.get_child("error").empty()) {
if (!tx.tx_in_list.empty()) {
std::string tx_txid = tx_json.get<std::string>("result.txid");
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations");
std::string tx_txid = tx.tx_txid;
uint32_t tx_confirmations = tx.tx_confirmations;
std::string tx_address = "";
uint64_t tx_amount = -1;
uint64_t tx_vout = -1;
for (auto &input : tx_json.get_child("result.vout")) {
std::string tx_vout_s = input.second.get<std::string>("n");
tx_vout = std::stoll(tx_vout_s);
for (auto &input : tx.tx_in_list) {
tx_vout = input.tx_vout;
if (tx_vout == swdo_vout) {
if (bitcoin_major_version > 21) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
for (auto &address : input.tx_address) {
if (address == swdo_address) {
tx_address = address;
}
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
if (address.second.data() == swdo_address) {
tx_address = address.second.data();
break;
}
}
}
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_amount = std::stoll(tx_amount_s);
tx_amount = input.tx_amount;
break;
}
}
@ -571,7 +821,6 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
(swdo_vout == tx_vout) &&
(gpo.parameters.son_bitcoin_min_tx_confirmations() <= tx_confirmations);
}
}
object_id_type object_id = op_obj_idx_1.get<sidechain_transaction_create_operation>().object_id;
std::string op_tx_str = op_obj_idx_1.get<sidechain_transaction_create_operation>().transaction;
@ -799,7 +1048,12 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() {
if (sao.expires == time_point_sec::maximum()) {
auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key)));
btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type);
payment_type payment_type_address = payment_type::P2SH_WSH;
if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type, payment_type_address);
std::string address_data = "{ \"redeemScript\": \"" + fc::to_hex(addr.get_redeem_script()) +
"\", \"witnessScript\": \"" + fc::to_hex(addr.get_witness_script()) + "\" }";
@ -942,13 +1196,11 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
return false;
}
const std::string tx_str = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true);
if (tx_str != "") {
std::stringstream tx_ss(tx_str);
boost::property_tree::ptree tx_json;
boost::property_tree::read_json(tx_ss, tx_json);
btc_tx tx = bitcoin_client->getrawtransaction(sto.sidechain_transaction, true);
if ((tx_json.count("error")) && (!tx_json.get_child("error").empty())) {
if (tx.tx_in_list.empty()) {
// This case will result with segmentation fault.
// FIXME check if that happened before introducing libbitcoin
return false;
}
@ -960,37 +1212,32 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key)));
pubkey_weights.push_back(std::make_pair(pub_key, si.weight));
}
btc_weighted_multisig_address addr(pubkey_weights, network_type);
std::string tx_txid = tx_json.get<std::string>("result.txid");
uint32_t tx_confirmations = tx_json.get<uint32_t>("result.confirmations");
payment_type payment_type_address = payment_type::P2SH_WSH;
if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address);
std::string tx_txid = tx.tx_txid;
uint32_t tx_confirmations = tx.tx_confirmations;
std::string tx_address = addr.get_address();
int64_t tx_amount = -1;
if (tx_confirmations >= gpo.parameters.son_bitcoin_min_tx_confirmations()) {
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
for (auto &input : tx_json.get_child("result.vout")) {
if (bitcoin_major_version > 21) {
std::string address = input.second.get<std::string>("scriptPubKey.address");
for (auto &input : tx.tx_in_list) {
for (auto &address : input.tx_address) {
if (address == tx_address) {
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_amount = std::stoll(tx_amount_s);
}
} else {
for (auto &address : input.second.get_child("scriptPubKey.addresses")) {
if (address.second.data() == tx_address) {
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_amount = std::stoll(tx_amount_s);
tx_amount = input.tx_amount;
break;
}
}
}
}
settle_amount = asset(tx_amount, database.get_global_properties().parameters.btc_asset());
return true;
}
}
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
auto swwo = database.get<son_wallet_withdraw_object>(sto.object_id);
@ -998,7 +1245,6 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain
return true;
}
}
}
return false;
}
@ -1016,7 +1262,11 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s
pubkey_weights.push_back(std::make_pair(pub_key, son.weight));
}
btc_weighted_multisig_address addr(pubkey_weights, network_type);
payment_type payment_type_address = payment_type::P2SH_WSH;
if (use_bitcoind_client) {
payment_type_address = payment_type::P2WSH;
}
btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address);
std::stringstream ss;
@ -1252,18 +1502,34 @@ 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_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());
std::string witness_script(sha.str());
witness_script.insert(0, std::string("220020"));
for (size_t i = 0; i < tx.vin.size(); i++) {
tx.vin[i].scriptSig = parse_hex(witness_script);
}
}
std::string final_tx_hex = fc::to_hex(pack(tx));
return bitcoin_client->sendrawtransaction(final_tx_hex);
std::string res = bitcoin_client->sendrawtransaction(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) {
const std::string block = bitcoin_client->getblock(event_data);
if (block.empty())
return;
void sidechain_net_handler_bitcoin::handle_event(const block_data &event_data) {
add_to_son_listener_log("BLOCK : " + event_data);
auto vins = bitcoin_client->getblock(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>();
@ -1319,58 +1585,16 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons
pubkey_weights.push_back(std::make_pair(pub_key, son.weight));
}
auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key)));
btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type);
payment_type payment_type_address = payment_type::P2SH_WSH;
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);
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");
if (bitcoin_major_version > 21) {
if (!script.count("address"))
continue;
} else {
if (!script.count("addresses"))
continue;
}
auto sort_out_vin = [&](std::string address) {
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);
};
if (bitcoin_major_version > 21) {
std::string address = script.get<std::string>("address");
sort_out_vin(address);
} else {
for (const auto &addr : script.get_child("addresses")) // in which cases there can be more addresses?
sort_out_vin(addr.second.get_value<std::string>());
}
}
}
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;