Partial integration done, some Bitcoin RPC refactoring

This commit is contained in:
Srdjan Obucina 2020-02-21 19:55:31 +01:00
parent f09ff36f1a
commit 001fe06501
4 changed files with 315 additions and 395 deletions

View file

@ -36,8 +36,6 @@ protected:
virtual std::string transfer( const std::string& from, const std::string& to, const uint64_t amount ) = 0;
virtual std::string sign_transaction( const std::string& transaction ) = 0;
virtual std::string send_transaction( const std::string& transaction ) = 0;
//virtual std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed) = 0;
//virtual std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) = 0;
private:

View file

@ -7,6 +7,8 @@
#include <fc/signals.hpp>
#include <fc/network/http/connection.hpp>
#include <graphene/chain/son_wallet_deposit_object.hpp>
#include <graphene/chain/son_wallet_withdraw_object.hpp>
namespace graphene { namespace peerplays_sidechain {
@ -21,20 +23,18 @@ public:
class bitcoin_rpc_client {
public:
bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ;
std::string receive_full_block( const std::string& block_hash );
int32_t receive_confirmations_tx( const std::string& tx_hash );
bool receive_mempool_entry_tx( const std::string& tx_hash );
uint64_t receive_estimated_fee();
void send_btc_tx( const std::string& tx_hex );
std::string add_multisig_address( const std::vector<std::string> public_keys );
bool connection_is_not_defined() const;
std::string create_raw_transaction(const std::string& txid, const std::string& vout, const std::string& out_address, double transfer_amount);
std::string sign_raw_transaction_with_wallet(const std::string& tx_hash);
std::string sign_raw_transaction_with_privkey(const std::string& tx_hash, const std::string& private_key);
void import_address( const std::string& address_or_script);
std::vector<btc_txout> list_unspent();
std::vector<btc_txout> list_unspent_by_address_and_amount(const std::string& address, double transfer_amount);
std::string prepare_tx(const std::vector<btc_txout>& ins, const fc::flat_map<std::string, double> outs);
std::string addmultisigaddress( const std::vector<std::string> public_keys );
std::string createrawtransaction(const std::vector<btc_txout>& ins, const fc::flat_map<std::string, double> outs);
uint64_t estimatesmartfee();
std::string getblock( const std::string& block_hash, int32_t verbosity = 2 );
void importaddress( const std::string& address_or_script);
std::vector<btc_txout> listunspent();
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string& address, double transfer_amount);
void sendrawtransaction( const std::string& tx_hex );
std::string signrawtransactionwithkey(const std::string& tx_hash, const std::string& private_key);
std::string signrawtransactionwithwallet(const std::string& tx_hash);
private:
@ -86,8 +86,8 @@ public:
std::string send_transaction( const std::string& transaction );
std::string sign_and_send_transaction_with_wallet ( const std::string& tx_json );
std::string transfer_all_btc(const std::string& from_address, const std::string& to_address);
std::string transfer_deposit_to_primary_wallet (const sidechain_event_data& sed);
std::string transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount);
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);
private:

View file

@ -87,7 +87,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime );
ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son}", ("son", son_id));
//ilog("sidechain_net_handler: sending proposal for son wallet deposit create operation by ${son}", ("son", son_id));
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
try {
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
@ -130,7 +130,7 @@ void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( database.head_block_time().sec_since_epoch() + lifetime );
ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son}", ("son", son_id));
//ilog("sidechain_net_handler: sending proposal for son wallet withdraw create operation by ${son}", ("son", son_id));
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
try {
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
@ -158,6 +158,8 @@ void sidechain_net_handler::process_deposits() {
std::for_each(idx_range.first, idx_range.second,
[&] (const son_wallet_deposit_object& swdo) {
ilog("Deposit to process: ${swdo}", ("swdo", swdo));
process_deposit(swdo);
const chain::global_property_object& gpo = plugin.database().get_global_properties();
@ -175,10 +177,8 @@ void sidechain_net_handler::process_deposits() {
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime );
ilog("sidechain_net_handler: sending proposal for transfer operation ${swdo} by ${son}", ("swdo", swdo.id) ("son", son_id));
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
trx.validate();
ilog("sidechain_net_handler: transaction validated ${swdo} by ${son}", ("swdo", swdo.id) ("son", son_id));
try {
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
if(plugin.app().p2p_node())
@ -198,6 +198,8 @@ void sidechain_net_handler::process_withdrawals() {
std::for_each(idx_range.first, idx_range.second,
[&] (const son_wallet_withdraw_object& swwo) {
ilog("Withdraw to process: ${swwo}", ("swwo", swwo));
process_withdrawal(swwo);
const chain::global_property_object& gpo = plugin.database().get_global_properties();
@ -205,28 +207,25 @@ void sidechain_net_handler::process_withdrawals() {
for (son_id_type son_id : plugin.get_sons()) {
if (plugin.is_active_son(son_id)) {
ilog("SON ${son_id}: Withdraw to process: ${swwo}", ("son_id", son_id) ("swwo", swwo));
//son_wallet_withdraw_process_operation p_op;
//p_op.payer = GRAPHENE_SON_ACCOUNT;
//p_op.son_wallet_withdraw_id = swwo.id;
//
//proposal_create_operation proposal_op;
//proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
//proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) );
//uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
//proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime );
//
//ilog("sidechain_net_handler: sending proposal for transfer operation ${swwo} by ${son}", ("swwo", swwo.id) ("son", son_id));
//signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
//trx.validate();
//ilog("sidechain_net_handler: transaction validated ${swwo} by ${son}", ("swwo", swwo.id) ("son", son_id));
//try {
// plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
// if(plugin.app().p2p_node())
// plugin.app().p2p_node()->broadcast(net::trx_message(trx));
//} catch(fc::exception e){
// ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what()));
//}
son_wallet_withdraw_process_operation p_op;
p_op.payer = GRAPHENE_SON_ACCOUNT;
p_op.son_wallet_withdraw_id = swwo.id;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = plugin.get_son_object(son_id).son_account;
proposal_op.proposed_ops.emplace_back( op_wrapper( p_op ) );
uint32_t lifetime = ( gpo.parameters.block_interval * gpo.active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( plugin.database().head_block_time().sec_since_epoch() + lifetime );
signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), proposal_op);
trx.validate();
try {
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
if(plugin.app().p2p_node())
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
} catch(fc::exception e){
ilog("sidechain_net_handler: sending proposal for transfer operation failed with exception ${e}",("e", e.what()));
}
}
}
});

View file

@ -15,8 +15,6 @@
#include <graphene/chain/sidechain_address_object.hpp>
#include <graphene/chain/son_info.hpp>
#include <graphene/chain/son_wallet_object.hpp>
#include <graphene/chain/son_wallet_deposit_object.hpp>
#include <graphene/chain/son_wallet_withdraw_object.hpp>
#include <graphene/chain/protocol/son_wallet.hpp>
namespace graphene { namespace peerplays_sidechain {
@ -30,116 +28,13 @@ bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::str
authorization.val = "Basic " + fc::base64_encode( user + ":" + password );
}
std::string bitcoin_rpc_client::receive_full_block( const std::string& block_hash )
{
fc::http::connection conn;
conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) );
const auto url = "http://" + ip + ":" + std::to_string( rpc_port ) + "/rest/block/" + block_hash + ".json";
const auto reply = conn.request( "GET", url );
if ( reply.status != 200 )
return "";
ilog( "Receive Bitcoin block: ${hash}", ( "hash", block_hash ) );
return std::string( reply.body.begin(), reply.body.end() );
bool bitcoin_rpc_client::connection_is_not_defined() const {
return ip.empty() || rpc_port == 0 || user.empty() || password.empty();
}
//int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash )
//{
// const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getrawtransaction\", \"params\": [") +
// std::string("\"") + tx_hash + std::string("\"") + ", " + "true" + std::string("] }");
//
// const auto reply = send_post_request( body );
//
// if ( reply.status != 200 )
// return 0;
//
// const auto result = std::string( reply.body.begin(), reply.body.end() );
//
// std::stringstream ss( result );
// boost::property_tree::ptree tx;
// boost::property_tree::read_json( ss, tx );
//
// if( tx.count( "result" ) ) {
// if( tx.get_child( "result" ).count( "confirmations" ) ) {
// return tx.get_child( "result" ).get_child( "confirmations" ).get_value<int64_t>();
// }
// }
// return 0;
//}
bool bitcoin_rpc_client::receive_mempool_entry_tx( const std::string& tx_hash )
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getmempoolentry\", \"params\": [") +
std::string("\"") + tx_hash + std::string("\"") + std::string("] }");
const auto reply = send_post_request( body );
if ( reply.status != 200 )
return false;
return true;
}
uint64_t bitcoin_rpc_client::receive_estimated_fee()
{
static const auto confirmation_target_blocks = 6;
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimated_feerate\", \"method\": \"estimatesmartfee\", \"params\": [") +
std::to_string(confirmation_target_blocks) + std::string("] }");
const auto reply = send_post_request( body );
if( reply.status != 200 )
return 0;
std::stringstream ss( std::string( reply.body.begin(), reply.body.end() ) );
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( json.count( "result" ) )
if ( json.get_child( "result" ).count( "feerate" ) ) {
auto feerate_str = json.get_child( "result" ).get_child( "feerate" ).get_value<std::string>();
feerate_str.erase( std::remove( feerate_str.begin(), feerate_str.end(), '.' ), feerate_str.end() );
return std::stoll( feerate_str );
}
return 0;
}
void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex )
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") +
std::string("\"") + tx_hex + std::string("\"") + std::string("] }");
ilog(body);
const auto reply = send_post_request( body );
if( reply.body.empty() )
return;
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
idump(( tx_hex ));
return;
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
const auto error_code = json.get_child( "error" ).get_child( "code" ).get_value<int>();
if( error_code == -27 ) // transaction already in block chain
return;
wlog( "BTC tx is not sent! Reply: ${msg}", ("msg", reply_str) );
}
}
std::string bitcoin_rpc_client::add_multisig_address( const std::vector<std::string> public_keys )
{
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", \"method\": \"addmultisigaddress\", \"params\": [");
std::string bitcoin_rpc_client::addmultisigaddress(const std::vector<std::string> public_keys) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"addmultisigaddress\", "
"\"method\": \"addmultisigaddress\", \"params\": [");
std::string params = "2, [";
std::string pubkeys = "";
for (std::string pubkey : public_keys) {
@ -151,225 +46,42 @@ std::string bitcoin_rpc_client::add_multisig_address( const std::vector<std::str
params = params + pubkeys + std::string("]");
body = body + params + std::string("] }");
const auto reply = send_post_request( body );
const auto reply = send_post_request(body);
if( reply.body.empty() )
return "";
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
return reply_str;
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return std::string();
}
if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "BTC multisig address creation failed! Reply: ${msg}", ("msg", reply_str) );
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("BTC multisig address creation failed! Reply: ${msg}", ("msg", ss.str()));
}
return "";
}
std::string bitcoin_rpc_client::create_raw_transaction(const std::string& txid, const std::string& nvout, const std::string& out_address, double transfer_amount)
{
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", \"method\": \"createrawtransaction\", \"params\": [");
std::string params = "";
std::string input = std::string("[{\"txid\":\"") + txid + std::string("\",\"vout\":")+ nvout +std::string("}]");
std::string output = std::string("[{\"") + out_address + std::string("\":") + std::to_string(transfer_amount) + std::string("}]");
params = params + input + std::string(",") + output;
body = body + params + std::string("]}");
ilog(body);
const auto reply = send_post_request( body );
if( reply.body.empty() )
return "";
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
return reply_str;
}
if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "BTC createrawtransaction failed! Reply: ${msg}", ("msg", reply_str) );
}
return "";
}
std::string bitcoin_rpc_client::sign_raw_transaction_with_wallet(const std::string& tx_hash)
{
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"signrawtransactionwithwallet\", \"method\": \"signrawtransactionwithwallet\", \"params\": [");
std::string params = "\"" + tx_hash + "\"";
body = body + params + std::string("]}");
ilog(body);
const auto reply = send_post_request( body );
if( reply.body.empty() )
return "";
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
return reply_str;
}
if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "BTC sign_raw_transaction_with_wallet failed! Reply: ${msg}", ("msg", reply_str) );
}
return "";
}
std::string bitcoin_rpc_client::sign_raw_transaction_with_privkey(const std::string& tx_hash, const std::string& private_key)
{
return "";
}
bool bitcoin_rpc_client::connection_is_not_defined() const
{
return ip.empty() || rpc_port == 0 || user.empty() || password.empty();
}
void bitcoin_rpc_client::import_address(const std::string &address_or_script)
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"importaddress\", \"params\": [") +
std::string("\"") + address_or_script + std::string("\"") + std::string("] }");
const auto reply = send_post_request( body );
if( reply.body.empty() )
{
wlog("Failed to import address [${addr}]", ("addr", address_or_script));
return;
}
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
idump((address_or_script)(reply_str));
return;
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "Failed to import address [${addr}]! Reply: ${msg}", ("addr", address_or_script)("msg", reply_str) );
}
}
std::vector<btc_txout> bitcoin_rpc_client::list_unspent()
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": [] }");
const auto reply = send_post_request( body );
std::vector<btc_txout> result;
if( reply.body.empty() )
{
wlog("Failed to list unspent txo");
return result;
}
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
idump((reply_str));
if( json.count( "result" ) )
{
for(auto& entry: json.get_child("result"))
{
btc_txout txo;
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
txo.amount_ = entry.second.get_child("amount").get_value<double>();
result.push_back(txo);
}
}
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) );
}
return result;
}
std::vector<btc_txout> bitcoin_rpc_client::list_unspent_by_address_and_amount(const std::string& address, double minimum_amount)
{
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"listunspent\", \"params\": [");
body += std::string("1,999999,[\"");
body += address;
body += std::string("\"],true,{\"minimumAmount\":");
body += std::to_string(minimum_amount);
body += std::string("}] }");
ilog(body);
const auto reply = send_post_request( body );
std::vector<btc_txout> result;
if( reply.body.empty() )
{
wlog("Failed to list unspent txo");
return result;
}
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
idump((reply_str));
if( json.count( "result" ) )
{
for(auto& entry: json.get_child("result"))
{
btc_txout txo;
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
txo.amount_ = entry.second.get_child("amount").get_value<double>();
result.push_back(txo);
}
}
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "Failed to list unspent txo! Reply: ${msg}", ("msg", reply_str) );
}
return result;
}
std::string bitcoin_rpc_client::prepare_tx(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs)
{
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": \"createrawtransaction\", \"params\": [");
std::string bitcoin_rpc_client::createrawtransaction(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs) {
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", "
"\"method\": \"createrawtransaction\", \"params\": [");
body += "[";
bool first = true;
for(const auto& entry: ins)
{
if(!first)
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)
for (const auto &entry : outs) {
if (!first)
body += ",";
body += "{\"" + entry.first + "\":" + std::to_string(entry.second) + "}";
first = false;
@ -378,30 +90,237 @@ std::string bitcoin_rpc_client::prepare_tx(const std::vector<btc_txout> &ins, co
ilog(body);
const auto reply = send_post_request( body );
const auto reply = send_post_request(body);
if( reply.body.empty() )
{
wlog("Failed to create raw transaction: [${body}]", ("body", body));
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return std::string();
}
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
boost::property_tree::read_json(ss, json);
if( reply.status == 200 ) {
idump((reply_str));
if( json.count( "result" ) )
return reply_str;
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
wlog( "Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", reply_str) );
if (reply.status == 200) {
if (json.count("result"))
return ss.str();
} else if (json.count("error") && !json.get_child("error").empty()) {
wlog("Failed to create raw transaction: [${body}]! Reply: ${msg}", ("body", body)("msg", ss.str()));
}
return std::string();
}
uint64_t bitcoin_rpc_client::estimatesmartfee() {
static const auto confirmation_target_blocks = 6;
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimatesmartfee\", "
"\"method\": \"estimatesmartfee\", \"params\": [") +
std::to_string(confirmation_target_blocks) + std::string("] }");
const auto reply = send_post_request(body);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return 0;
}
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
if (json.count("result"))
if (json.get_child("result").count("feerate")) {
auto feerate_str = json.get_child("result").get_child("feerate").get_value<std::string>();
feerate_str.erase(std::remove(feerate_str.begin(), feerate_str.end(), '.'), feerate_str.end());
return std::stoll(feerate_str);
}
return 0;
}
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\": [\"" +
block_hash + "\", " + std::to_string(verbosity) + "] }");
const auto reply = send_post_request(body);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return std::string();
}
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) {
std::stringstream ss;
boost::property_tree::json_parser::write_json(ss, json.get_child("result"));
return ss.str();
}
if (json.count("error") && !json.get_child("error").empty()) {
wlog("Bitcoin RPC call ${function} failed with reply '${msg}'", ("function", __FUNCTION__)("msg", ss.str()));
}
return "";
}
void bitcoin_rpc_client::importaddress(const std::string &address_or_script) {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", "
"\"method\": \"importaddress\", \"params\": [") +
std::string("\"") + address_or_script + std::string("\"") + 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) {
idump((address_or_script)(ss.str()));
return;
} else if (json.count("error") && !json.get_child("error").empty()) {
wlog("Failed to import address [${addr}]! Reply: ${msg}", ("addr", address_or_script)("msg", ss.str()));
}
}
std::vector<btc_txout> bitcoin_rpc_client::listunspent() {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": "
"\"listunspent\", \"params\": [] }");
const auto reply = send_post_request(body);
std::vector<btc_txout> result;
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return result;
}
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) {
if (json.count("result")) {
for (auto &entry : json.get_child("result")) {
btc_txout txo;
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
txo.amount_ = entry.second.get_child("amount").get_value<double>();
result.push_back(txo);
}
}
} else if (json.count("error") && !json.get_child("error").empty()) {
wlog("Failed to list unspent txo! Reply: ${msg}", ("msg", ss.str()));
}
return result;
}
std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(const std::string &address, double minimum_amount) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"pp_plugin\", \"method\": "
"\"listunspent\", \"params\": [");
body += std::string("1,999999,[\"");
body += address;
body += std::string("\"],true,{\"minimumAmount\":");
body += std::to_string(minimum_amount);
body += std::string("}] }");
const auto reply = send_post_request(body);
std::vector<btc_txout> result;
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return result;
}
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) {
if (json.count("result")) {
for (auto &entry : json.get_child("result")) {
btc_txout txo;
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
txo.amount_ = entry.second.get_child("amount").get_value<double>();
result.push_back(txo);
}
}
} else if (json.count("error") && !json.get_child("error").empty()) {
wlog("Failed to list unspent txo! Reply: ${msg}", ("msg", ss.str()));
}
return result;
}
void bitcoin_rpc_client::sendrawtransaction(const std::string &tx_hex) {
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"sendrawtransaction\", "
"\"method\": \"sendrawtransaction\", \"params\": [") +
std::string("\"") + tx_hex + std::string("\"") + std::string("] }");
ilog(body);
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;
} else if (json.count("error") && !json.get_child("error").empty()) {
const auto error_code = json.get_child("error").get_child("code").get_value<int>();
if (error_code == -27) // transaction already in block chain
return;
wlog("BTC tx is not sent! Reply: ${msg}", ("msg", ss.str()));
}
}
std::string bitcoin_rpc_client::signrawtransactionwithkey(const std::string &tx_hash, const std::string &private_key) {
return "";
}
std::string bitcoin_rpc_client::signrawtransactionwithwallet(const std::string &tx_hash) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"signrawtransactionwithwallet\", "
"\"method\": \"signrawtransactionwithwallet\", \"params\": [");
std::string params = "\"" + tx_hash + "\"";
body = body + params + std::string("]}");
ilog(body);
const auto reply = send_post_request(body);
if (reply.body.empty()) {
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
return std::string();
}
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("BTC sign_raw_transaction_with_wallet failed! Reply: ${msg}", ("msg", ss.str()));
}
return "";
}
fc::http::reply bitcoin_rpc_client::send_post_request( std::string body )
{
fc::http::connection conn;
@ -556,18 +475,22 @@ void sidechain_net_handler_bitcoin::process_deposits() {
sidechain_net_handler::process_deposits();
}
void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object& swdo) {
void sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) {
ilog(__FUNCTION__);
transfer_deposit_to_primary_wallet(swdo);
}
void sidechain_net_handler_bitcoin::process_withdrawals() {
sidechain_net_handler::process_withdrawals();
}
void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object& swwo) {
void sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw_object &swwo) {
ilog(__FUNCTION__);
transfer_withdrawal_from_primary_wallet(swwo);
}
std::string sidechain_net_handler_bitcoin::create_multisignature_wallet( const std::vector<std::string> public_keys ) {
return bitcoin_client->add_multisig_address(public_keys);
return bitcoin_client->addmultisigaddress(public_keys);
}
std::string sidechain_net_handler_bitcoin::transfer( const std::string& from, const std::string& to, const uint64_t amount ) {
@ -598,7 +521,7 @@ std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet
std::string unsigned_tx_hex = pt.get<std::string>("result");
reply_str = bitcoin_client->sign_raw_transaction_with_wallet(unsigned_tx_hex);
reply_str = bitcoin_client->signrawtransactionwithwallet(unsigned_tx_hex);
ilog(reply_str);
std::stringstream ss_stx(reply_str);
boost::property_tree::ptree stx_json;
@ -610,20 +533,20 @@ std::string sidechain_net_handler_bitcoin::sign_and_send_transaction_with_wallet
std::string signed_tx_hex = stx_json.get<std::string>("result.hex");
bitcoin_client->send_btc_tx(signed_tx_hex);
bitcoin_client->sendrawtransaction(signed_tx_hex);
return reply_str;
}
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->receive_estimated_fee();
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
uint64_t min_fee_rate = 1000;
fee_rate = std::max(fee_rate, min_fee_rate);
double min_amount = ((double)fee_rate/100000000.0); // Account only for relay fee for now
double total_amount = 0.0;
std::vector<btc_txout> unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(from_address, 0);
std::vector<btc_txout> unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(from_address, 0);
if(unspent_utxo.size() == 0)
{
@ -647,16 +570,16 @@ std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string& f
fc::flat_map<std::string, double> outs;
outs[to_address] = total_amount - min_amount;
std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs);
std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs);
return sign_and_send_transaction_with_wallet(reply_str);
}
std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet ( const sidechain_event_data& sed )
std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (const son_wallet_deposit_object &swdo)
{
const auto& idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
auto obj = idx.rbegin();
if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) {
return "";
return "";
}
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
@ -667,11 +590,11 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (
std::string pw_address = json.get<std::string>("address");
std::string txid = sed.sidechain_transaction_id;
std::string suid = sed.sidechain_uid;
std::string txid = swdo.sidechain_transaction_id;
std::string suid = swdo.sidechain_uid;
std::string nvout = suid.substr(suid.find_last_of("-")+1);
uint64_t deposit_amount = sed.sidechain_amount.value;
uint64_t fee_rate = bitcoin_client->receive_estimated_fee();
uint64_t deposit_amount = swdo.sidechain_amount.value;
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
uint64_t min_fee_rate = 1000;
fee_rate = std::max(fee_rate, min_fee_rate);
deposit_amount -= fee_rate; // Deduct minimum relay fee
@ -688,16 +611,16 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet (
outs[pw_address] = transfer_amount;
std::string reply_str = bitcoin_client->prepare_tx(ins, outs);
std::string reply_str = bitcoin_client->createrawtransaction(ins, outs);
return sign_and_send_transaction_with_wallet(reply_str);
}
std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const std::string& user_address, double sidechain_amount) {
std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wallet(const son_wallet_withdraw_object &swwo) {
const auto& idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
auto obj = idx.rbegin();
if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end())
{
return "";
return "";
}
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
@ -708,13 +631,13 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall
std::string pw_address = json.get<std::string>("address");
uint64_t fee_rate = bitcoin_client->receive_estimated_fee();
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
uint64_t min_fee_rate = 1000;
fee_rate = std::max(fee_rate, min_fee_rate);
double min_amount = sidechain_amount + ((double)fee_rate/100000000.0); // Account only for relay fee for now
double min_amount = swwo.withdraw_amount.value + ((double)fee_rate/100000000.0); // Account only for relay fee for now
double total_amount = 0.0;
std::vector<btc_txout> unspent_utxo= bitcoin_client->list_unspent_by_address_and_amount(pw_address, 0);
std::vector<btc_txout> unspent_utxo = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0);
if(unspent_utxo.size() == 0)
{
@ -731,23 +654,23 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall
if(min_amount > total_amount)
{
wlog("Failed not enough BTC to spend for ${pw}",("pw", pw_address));
return "";
return "";
}
}
fc::flat_map<std::string, double> outs;
outs[user_address] = sidechain_amount;
outs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0;
if((total_amount - min_amount) > 0.0)
{
outs[pw_address] = total_amount - min_amount;
}
std::string reply_str = bitcoin_client->prepare_tx(unspent_utxo, outs);
std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs);
return sign_and_send_transaction_with_wallet(reply_str);
}
void sidechain_net_handler_bitcoin::handle_event( const std::string& event_data ) {
std::string block = bitcoin_client->receive_full_block( event_data );
std::string block = bitcoin_client->getblock(event_data);
if( block != "" ) {
const auto& vins = extract_info_from_block( block );