diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp index ec9689f6..09799832 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp @@ -25,10 +25,14 @@ public: bool connection_is_not_defined() const; std::string addmultisigaddress(const std::vector public_keys); + std::string createpsbt(const std::vector &ins, const fc::flat_map outs); std::string createrawtransaction(const std::vector &ins, const fc::flat_map outs); std::string createwallet(const std::string &wallet_name); + std::string decodepsbt(std::string const &tx_psbt); + std::string decoderawtransaction(std::string const &tx_hex); std::string encryptwallet(const std::string &passphrase); uint64_t estimatesmartfee(); + std::string finalizepsbt(std::string const &tx_psbt); std::string getblock(const std::string &block_hash, int32_t verbosity = 2); void importaddress(const std::string &address_or_script); std::vector listunspent(); @@ -39,6 +43,7 @@ public: std::string signrawtransactionwithwallet(const std::string &tx_hash); std::string unloadwallet(const std::string &filename); std::string walletlock(); + std::string walletprocesspsbt(std::string const &tx_psbt); bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60); private: @@ -104,6 +109,7 @@ private: std::string sign_transaction(const std::string &transaction); std::string send_transaction(const std::string &transaction); std::string sign_and_send_transaction_with_wallet(const std::string &tx_json); + std::string processpsbt(const std::string &tx_psbt); std::string transfer_all_btc(const std::string &from_address, const std::string &to_address); std::string transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo); std::string transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index fc891054..da0fed0c 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -54,7 +54,7 @@ std::string bitcoin_rpc_client::addmultisigaddress(const std::vector &ins, const fc::flat_map outs) { + std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createpsbt\", " + "\"method\": \"createpsbt\", \"params\": ["); + body += "["; + bool first = true; + for (const auto &entry : ins) { + if (!first) + body += ","; + body += "{\"txid\":\"" + entry.txid_ + "\",\"vout\":" + std::to_string(entry.out_num_) + "}"; + first = false; + } + body += "],["; + first = true; + for (const auto &entry : outs) { + if (!first) + body += ","; + body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}"; + first = false; + } + body += std::string("]] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return json.get("result"); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + std::string bitcoin_rpc_client::createrawtransaction(const std::vector &ins, const fc::flat_map outs) { std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", " "\"method\": \"createrawtransaction\", \"params\": ["); @@ -96,7 +138,7 @@ std::string bitcoin_rpc_client::createrawtransaction(const std::vector(); - feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); - return std::stoll(feerate_str); - } + if (reply.status == 200) { + auto feerate_str = json.get("result.feerate"); + feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end()); + return std::stoll(feerate_str); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } return 0; } +std::string bitcoin_rpc_client::finalizepsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"finalizepsbt\", \"method\": " + "\"finalizepsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t verbosity) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblock\", \"method\": " "\"getblock\", \"params\": [\"" + @@ -204,7 +331,7 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -326,7 +453,7 @@ std::string bitcoin_rpc_client::loadwallet(const std::string &filename) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -386,7 +513,7 @@ std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string & if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -412,7 +539,7 @@ std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -439,7 +566,7 @@ std::string bitcoin_rpc_client::walletlock() { if (reply.body.empty()) { wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); - return std::string(); + return ""; } std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); @@ -458,6 +585,32 @@ std::string bitcoin_rpc_client::walletlock() { return ""; } +std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) { + std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": " + "\"walletprocesspsbt\", \"params\": [\"" + + tx_psbt + "\"] }"); + + const auto reply = send_post_request(body); + + if (reply.body.empty()) { + wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__)); + return ""; + } + + std::stringstream ss(std::string(reply.body.begin(), reply.body.end())); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + + if (reply.status == 200) { + return ss.str(); + } + + if (json.count("error") && !json.get_child("error").empty()) { + wlog("Bitcoin RPC call ${function} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str())); + } + return ""; +} + bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) { std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": " "\"walletpassphrase\", \"params\": [\"" + @@ -732,6 +885,55 @@ std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet return reply_str; } +std::string sidechain_net_handler_bitcoin::processpsbt(const std::string &tx_psbt) { + ilog(tx_psbt); + + if (!wallet_password.empty()) { + bitcoin_client->walletpassphrase(wallet_password, 60); + } + + std::string reply_str = bitcoin_client->walletprocesspsbt(tx_psbt); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("psbt") == 0) || (json_res.count("complete") == 0)) { + wlog("Failed to process PSBT ${psbt}", ("psbt", tx_psbt)); + return ""; + } + + std::string new_tx_psbt = json.get("result.psbt"); + bool complete_psbt = json.get("result.complete"); + + if (complete_psbt) { + std::string reply_str = bitcoin_client->finalizepsbt(new_tx_psbt); + + std::stringstream ss(reply_str); + boost::property_tree::ptree json; + boost::property_tree::read_json(ss, json); + boost::property_tree::ptree json_res = json.get_child("result"); + + if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) { + wlog("Failed to finalize PSBT ${psbt}", ("psbt", tx_psbt)); + return ""; + } + + std::string new_tx_raw = json.get("result.hex"); + bool complete_raw = json.get("result.complete"); + + if (complete_raw) { + + std::string decoded_raw = bitcoin_client->decoderawtransaction(new_tx_raw); + + bitcoin_client->sendrawtransaction(new_tx_raw); + return ""; + } + } + return new_tx_psbt; +} + std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string &from_address, const std::string &to_address) { uint64_t fee_rate = bitcoin_client->estimatesmartfee(); uint64_t min_fee_rate = 1000; @@ -758,8 +960,10 @@ std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string &f fc::flat_map outs; outs[to_address] = total_amount - min_amount; - std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); - return sign_and_send_transaction_with_wallet(reply_str); + //std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); + //return sign_and_send_transaction_with_wallet(reply_str); + std::string reply_str = bitcoin_client->createpsbt(unspent_utxo, outs); + return processpsbt(reply_str); } std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(const son_wallet_deposit_object &swdo) { @@ -798,8 +1002,10 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(co outs[pw_address] = transfer_amount; - std::string reply_str = bitcoin_client->createrawtransaction(ins, outs); - return sign_and_send_transaction_with_wallet(reply_str); + //std::string reply_str = bitcoin_client->createrawtransaction(ins, outs); + //return sign_and_send_transaction_with_wallet(reply_str); + std::string reply_str = bitcoin_client->createpsbt(ins, outs); + return processpsbt(reply_str); } std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo) { @@ -845,8 +1051,10 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall outs[pw_address] = total_amount - min_amount; } - std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); - return sign_and_send_transaction_with_wallet(reply_str); + //std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs); + //return sign_and_send_transaction_with_wallet(reply_str); + std::string reply_str = bitcoin_client->createpsbt(unspent_utxo, outs); + return processpsbt(reply_str); } void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) {