Compare commits

...

24 commits

Author SHA1 Message Date
gladcow
f72862ab38 signing and sending tx 2020-04-10 20:07:18 +03:00
gladcow
f11bf5dab5 weighted redeem script 2020-04-10 15:54:26 +03:00
gladcow
74c5435e94 signatures collection 2020-04-10 12:20:42 +03:00
satyakoneru
3736bd9f2e Add signing and deposit checks for psbt 2020-04-09 18:15:28 +00:00
gladcow
75ad306154 save inputs in transaction string 2020-04-09 16:42:48 +03:00
gladcow
f8d39d139a change double input values to satoshis 2020-04-09 16:42:48 +03:00
gladcow
fa2fc36502 tx and inputs amounts serialization 2020-04-09 16:42:48 +03:00
gladcow
901af715da move script creation to helper function 2020-04-09 16:42:48 +03:00
Srdjan Obucina
508549496c Merge branch 'feature/SONs-base' into feature/SON-333-v2 2020-04-08 22:54:32 +02:00
Srdjan Obucina
1c742c089a Merge branch 'feature/SONs-base' into feature/SON-333-v2 2020-04-08 17:25:04 +02:00
Srdjan Obucina
587b227a45 Prepare signing function 2020-04-07 21:21:35 +02:00
Srdjan Obucina
d35586d89f Prepare signing function 2020-04-07 21:17:36 +02:00
Srdjan Obucina
692ae3f271 Discover network type (mainnet/testnet), and set correct parameters to use in libbitcoin calls 2020-04-07 20:46:47 +02:00
gladcow
00569f84b5 get network type with rpc call 2020-04-07 18:28:48 +03:00
gladcow
d91008ecb7 base transaction creation with libbitcoin 2020-04-07 17:23:45 +03:00
Srdjan Obucina
e0fb5a03d6 Fix build error 2020-04-06 19:25:39 +02:00
Srdjan Obucina
ab76708979 Merge branch 'feature/SONs-base' into feature/SON-333-v2 2020-04-06 18:52:11 +02:00
Srdjan Obucina
a4c4fac850 Merge branch 'feature/SONs-base' into feature/SON-333-v2 2020-04-06 11:53:26 +02:00
Srdjan Obucina
3c4899f369 Creating multisig address with custom script using libbitcoin 2020-04-06 04:05:49 +02:00
Srdjan Obucina
4aa93b6035 Creating multisig address with custom script using libbitcoin 2020-04-06 01:18:53 +02:00
Srdjan Obucina
3e901c264c Creating multisig address with custom script using libbitcoin 2020-04-05 22:41:46 +02:00
Srdjan Obucina
268edc904e Creating multisig m-of-n address using libbitcoin 2020-04-05 07:17:24 +02:00
Srdjan Obucina
47f279c68f Creating multisig m-of-n address using libbitcoin 2020-04-05 06:31:58 +02:00
Srdjan Obucina
81db3a5c0d Creating multisig m-of-n address using libbitcoin 2020-04-05 06:23:18 +02:00
5 changed files with 564 additions and 37 deletions

View file

@ -16,7 +16,7 @@ endif()
unset(SUPPORT_MULTIPLE_SONS)
unset(SUPPORT_MULTIPLE_SONS CACHE)
target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq )
target_link_libraries( peerplays_sidechain graphene_chain graphene_app bitcoin fc zmq )
target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -1,11 +1,14 @@
#include <fc/crypto/base58.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/crypto/ripemd160.hpp>
#include <fc/crypto/sha256.hpp>
#include <fc/io/raw.hpp>
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
#include <secp256k1.h>
#include <boost/property_tree/json_parser.hpp>
namespace graphene { namespace peerplays_sidechain {
static const unsigned char OP_0 = 0x00;
@ -677,4 +680,5 @@ bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector<
return ret;
}
}} // namespace graphene::peerplays_sidechain

View file

@ -10,11 +10,17 @@
namespace graphene { namespace peerplays_sidechain {
enum class network_type {
mainnet,
testnet,
regtest
};
class btc_txout {
public:
std::string txid_;
unsigned int out_num_;
double amount_;
uint64_t amount_;
};
class bitcoin_rpc_client {
@ -25,6 +31,7 @@ public:
std::string combinepsbt(const vector<std::string> &psbts);
std::string createmultisig(const uint32_t nrequired, const std::vector<std::string> public_keys);
std::string createpsbt(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
std::string converttopsbt(const std::string &hex);
std::string createrawtransaction(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
std::string createwallet(const std::string &wallet_name);
std::string decodepsbt(std::string const &tx_psbt);
@ -34,6 +41,7 @@ public:
std::string finalizepsbt(std::string const &tx_psbt);
std::string getaddressinfo(const std::string &address);
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
std::string getblockchaininfo();
std::string gettransaction(const std::string &txid, const bool include_watch_only = false);
void importaddress(const std::string &address_or_script);
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
@ -104,16 +112,28 @@ private:
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
std::unique_ptr<zmq_listener> listener;
network_type network;
uint8_t payment_address_p2kh;
uint8_t payment_address_p2sh;
uint8_t ec_private_wif;
uint8_t ec_private_p2kh;
uint16_t ec_private_version;
fc::future<void> on_changed_objects_task;
std::string create_primary_wallet_transaction();
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo);
std::string create_multisig_address(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys);
std::string create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
std::string sign_transaction(const sidechain_transaction_object &sto, bool &complete);
bool send_transaction(const sidechain_transaction_object &sto, std::string &sidechain_transaction);
std::string create_multisig_address_raw(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys);
std::string create_multisig_address_psbt(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys);
std::string create_multisig_address_standalone(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys);
std::string create_transaction_raw(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
std::string create_transaction_psbt(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
std::string create_transaction_standalone(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
@ -124,6 +144,7 @@ private:
bool send_transaction_raw(const sidechain_transaction_object &sto, std::string &sidechain_transaction);
bool send_transaction_psbt(const sidechain_transaction_object &sto, std::string &sidechain_transaction);
bool send_transaction_standalone(const sidechain_transaction_object &sto, std::string &sidechain_transaction);
void handle_event(const std::string &event_data);
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);

View file

@ -3,11 +3,14 @@
#include <algorithm>
#include <thread>
#include <bitcoin/bitcoin.hpp>
#include <boost/algorithm/hex.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fc/crypto/base64.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/log/logger.hpp>
#include <fc/network/ip.hpp>
@ -15,6 +18,7 @@
#include <graphene/chain/protocol/son_wallet.hpp>
#include <graphene/chain/son_info.hpp>
#include <graphene/chain/son_wallet_object.hpp>
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
namespace graphene { namespace peerplays_sidechain {
@ -179,6 +183,34 @@ std::string bitcoin_rpc_client::createpsbt(const std::vector<btc_txout> &ins, co
return "";
}
std::string bitcoin_rpc_client::converttopsbt(const std::string &hex) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"converttopsbt\", \"method\": "
"\"converttopsbt\", \"params\": [\"" +
hex + "\"] }");
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) {
if (json.find("result") != json.not_found()) {
return json.get<std::string>("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<btc_txout> &ins, const fc::flat_map<std::string, double> outs) {
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", "
"\"method\": \"createrawtransaction\", \"params\": [");
@ -454,6 +486,33 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t
return "";
}
std::string bitcoin_rpc_client::getblockchaininfo() {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"getblockchaininfo\", \"method\": "
"\"getblockchaininfo\", \"params\": [] }");
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) {
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} with body ${body} failed with reply '${msg}'", ("function", __FUNCTION__)("body", body)("msg", ss.str()));
}
return "";
}
std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) {
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": "
"\"gettransaction\", \"params\": [\"" +
@ -527,7 +586,9 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent(const uint32_t minconf, c
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>();
string amount = entry.second.get_child("amount").get_value<std::string>();
amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end());
txo.amount_ = std::stoll(amount);
result.push_back(txo);
}
}
@ -565,7 +626,9 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(con
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>();
string amount = entry.second.get_child("amount").get_value<std::string>();
amount.erase(std::remove(amount.begin(), amount.end(), '.'), amount.end());
txo.amount_ = std::stoll(amount);
result.push_back(txo);
}
}
@ -879,6 +942,31 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
bitcoin_client->loadwallet(wallet);
}
std::string blockchain_info = bitcoin_client->getblockchaininfo();
std::stringstream bci_ss(std::string(blockchain_info.begin(), blockchain_info.end()));
boost::property_tree::ptree bci_json;
boost::property_tree::read_json(bci_ss, bci_json);
network = network_type::mainnet;
if (bci_json.count("chain")) {
std::string chain = bci_json.get<std::string>("chain");
if (chain != "mainnet") {
network = network_type::testnet;
}
}
if (network == network_type::mainnet) {
payment_address_p2kh = libbitcoin::wallet::payment_address::mainnet_p2kh;
payment_address_p2sh = libbitcoin::wallet::payment_address::mainnet_p2sh;
ec_private_wif = libbitcoin::wallet::ec_private::mainnet_wif;
ec_private_p2kh = libbitcoin::wallet::ec_private::mainnet_p2kh;
ec_private_version = libbitcoin::wallet::ec_private::mainnet;
} else {
payment_address_p2kh = libbitcoin::wallet::payment_address::testnet_p2kh;
payment_address_p2sh = libbitcoin::wallet::payment_address::testnet_p2sh;
ec_private_wif = libbitcoin::wallet::ec_private::testnet_wif;
ec_private_p2kh = libbitcoin::wallet::ec_private::testnet_p2kh;
ec_private_version = libbitcoin::wallet::ec_private::testnet;
}
listener = std::unique_ptr<zmq_listener>(new zmq_listener(ip, zmq_port));
listener->event_received.connect([this](const std::string &event_data) {
std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach();
@ -937,13 +1025,12 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
if (son_sets_equal) {
auto active_sons = gpo.active_sons;
vector<string> son_pubkeys_bitcoin;
vector<pair<string, uint16_t>> son_pubkeys_bitcoin;
for (const son_info &si : active_sons) {
son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin));
son_pubkeys_bitcoin.push_back(make_pair(si.sidechain_public_keys.at(sidechain_type::bitcoin), si.weight));
}
uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1;
string reply_str = bitcoin_client->createmultisig(nrequired, son_pubkeys_bitcoin);
string reply_str = create_multisig_address(son_pubkeys_bitcoin);
std::stringstream active_pw_ss(reply_str);
boost::property_tree::ptree active_pw_pt;
@ -1068,16 +1155,12 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
const chain::global_property_object &gpo = database.get_global_properties();
auto active_sons = gpo.active_sons;
vector<string> son_pubkeys_bitcoin;
vector<std::pair<string, uint16_t>> son_pubkeys_bitcoin;
for (const son_info &si : active_sons) {
son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin));
son_pubkeys_bitcoin.push_back(std::make_pair(si.sidechain_public_keys.at(sidechain_type::bitcoin), si.weight));
}
if (!wallet_password.empty()) {
bitcoin_client->walletpassphrase(wallet_password, 5);
}
uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1;
string reply_str = bitcoin_client->createmultisig(nrequired, son_pubkeys_bitcoin);
string reply_str = create_multisig_address(son_pubkeys_bitcoin);
std::stringstream active_pw_ss(reply_str);
boost::property_tree::ptree active_pw_pt;
@ -1274,8 +1357,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction() {
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;
uint64_t total_amount = 0.0;
std::vector<btc_txout> inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0);
if (inputs.size() == 0) {
@ -1286,14 +1368,14 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction() {
total_amount += utx.amount_;
}
if (min_amount >= total_amount) {
if (fee_rate >= total_amount) {
elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address));
return "";
}
}
fc::flat_map<std::string, double> outputs;
outputs[active_pw_address] = total_amount - min_amount;
outputs[active_pw_address] = double(total_amount - fee_rate) / 100000000.0;
return create_transaction(inputs, outputs);
}
@ -1334,7 +1416,7 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_
outputs[pw_address] = transfer_amount;
return create_transaction(inputs, outputs);
return create_transaction_psbt(inputs, outputs);
}
std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
@ -1356,8 +1438,7 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
uint64_t min_fee_rate = 1000;
fee_rate = std::max(fee_rate, min_fee_rate);
double min_amount = ((double)(swwo.withdraw_amount.value + fee_rate) / 100000000.0); // Account only for relay fee for now
double total_amount = 0.0;
uint64_t total_amount = 0;
std::vector<btc_txout> inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0);
if (inputs.size() == 0) {
@ -1368,7 +1449,7 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
total_amount += utx.amount_;
}
if (min_amount > total_amount) {
if (fee_rate > total_amount) {
elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address));
return "";
}
@ -1376,20 +1457,30 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
fc::flat_map<std::string, double> outputs;
outputs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0;
if ((total_amount - min_amount) > 0.0) {
outputs[pw_address] = total_amount - min_amount;
if ((total_amount - fee_rate) > 0.0) {
outputs[pw_address] = double(total_amount - fee_rate) / 100000000.0;
}
return create_transaction(inputs, outputs);
}
// Creates segwit multisig address
// Function to actually create segwit multisig address should return json string with address info, or empty string in case of failure
std::string sidechain_net_handler_bitcoin::create_multisig_address(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys) {
std::string new_addr = "";
//new_addr = create_multisig_address_raw(son_pubkeys);
//new_addr = create_multisig_address_psbt(son_pubkeys);
new_addr = create_multisig_address_standalone(son_pubkeys);
return new_addr;
}
// Creates transaction in any format
// Function to actually create transaction should return transaction string, or empty string in case of failure
std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
std::string new_tx = "";
//new_tx = create_transaction_raw(inputs, outputs);
new_tx = create_transaction_psbt(inputs, outputs);
//new_tx = create_transaction_standalone(inputs, outputs);
//new_tx = create_transaction_psbt(inputs, outputs);
new_tx = create_transaction_standalone(inputs, outputs);
return new_tx;
}
@ -1399,7 +1490,11 @@ std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_tran
complete = false;
std::string new_tx = "";
//new_tx = sign_transaction_raw(sto, complete);
new_tx = sign_transaction_psbt(sto, complete);
if (sto.object_id.type() == 30) {
new_tx = sign_transaction_psbt(sto, complete);
} else {
new_tx = sign_transaction_standalone(sto, complete);
}
//new_tx = sign_transaction_standalone(sto, complete);
return new_tx;
}
@ -1407,7 +1502,304 @@ std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_tran
bool sidechain_net_handler_bitcoin::send_transaction(const sidechain_transaction_object &sto, std::string &sidechain_transaction) {
sidechain_transaction = "";
//return send_transaction_raw(sto, sidechain_transaction);
return send_transaction_psbt(sto, sidechain_transaction);
//return send_transaction_psbt(sto, sidechain_transaction);
if (sto.object_id.type() == 30) {
return send_transaction_psbt(sto, sidechain_transaction);
} else {
return send_transaction_standalone(sto, sidechain_transaction);
}
}
std::string sidechain_net_handler_bitcoin::create_multisig_address_raw(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys) {
vector<std::string> pubkeys;
for (auto s : son_pubkeys) {
pubkeys.push_back(s.first);
}
if (!wallet_password.empty()) {
bitcoin_client->walletpassphrase(wallet_password, 5);
}
uint32_t nrequired = pubkeys.size() * 2 / 3 + 1;
return bitcoin_client->addmultisigaddress(nrequired, pubkeys);
}
std::string sidechain_net_handler_bitcoin::create_multisig_address_psbt(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys) {
vector<std::string> pubkeys;
for (auto s : son_pubkeys) {
pubkeys.push_back(s.first);
}
if (!wallet_password.empty()) {
bitcoin_client->walletpassphrase(wallet_password, 5);
}
uint32_t nrequired = pubkeys.size() * 2 / 3 + 1;
return bitcoin_client->addmultisigaddress(nrequired, pubkeys);
}
std::vector<std::vector<unsigned char>> read_byte_arrays_from_string(const std::string &string_buf)
{
std::stringstream ss(string_buf);
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
std::vector<bytes> data;
for(auto &v: json)
{
std::string hex = v.second.data();
bytes item;
item.resize(hex.size() / 2);
fc::from_hex(hex, (char*)&item[0], item.size());
data.push_back(item);
}
return data;
}
std::string write_byte_arrays_to_string(const std::vector<std::vector<unsigned char>>& data)
{
std::string res = "[";
for (unsigned int idx = 0; idx < data.size(); ++idx) {
res += "\"" + fc::to_hex((char*)&data[idx][0], data[idx].size()) + "\"";
if (idx != data.size() - 1)
res += ",";
}
res += "]";
return res;
}
libbitcoin::chain::script get_multisig_witness_script(const std::vector<libbitcoin::wallet::ec_public> &son_pubkeys)
{
libbitcoin::point_list keys;
for (auto& key : son_pubkeys) {
keys.push_back(key.point());
}
uint32_t nrequired = son_pubkeys.size() * 2 / 3 + 1;
libbitcoin::chain::script multisig = libbitcoin::chain::script::to_pay_multisig_pattern(nrequired, keys);
return multisig;
}
libbitcoin::chain::script getRedeemScript(const std::vector<libbitcoin::wallet::ec_public> &son_pubkeys) {
libbitcoin::chain::script multisig = get_multisig_witness_script(son_pubkeys);
libbitcoin::data_chunk multisig_hash = libbitcoin::to_chunk(libbitcoin::sha256_hash(multisig.to_data(0)));
libbitcoin::machine::operation::list redeemscript_ops {libbitcoin::machine::operation(libbitcoin::machine::opcode(0)), libbitcoin::machine::operation(multisig_hash)};
libbitcoin::chain::script redeem_script = libbitcoin::chain::script(redeemscript_ops);
return redeem_script;
}
libbitcoin::machine::operation script_num(uint32_t val)
{
if (val < 16)
return libbitcoin::machine::operation::opcode_from_positive(val & 0xff);
libbitcoin::data_chunk result;
while (val) {
result.push_back(val & 0xff);
val >>= 8;
}
// - If the most significant byte is >= 0x80 and the value is positive, push a
// new zero-byte to make the significant byte < 0x80 again.
if (result.back() & 0x80)
result.push_back(0);
return result;
}
libbitcoin::chain::script get_weighted_multisig_witness_script(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys)
{
using namespace libbitcoin;
using namespace libbitcoin::chain;
using namespace libbitcoin::machine;
using namespace libbitcoin::wallet;
// Online visualizer/debugger
// https://siminchen.github.io/bitcoinIDE/build/editor.html
//
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// 03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275 OP_CHECKSIG
// OP_IF OP_1 OP_ELSE 0 OP_ENDIF
// OP_SWAP
// 02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// OP_SWAP
// 02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73 OP_CHECKSIG
// OP_IF OP_1 OP_ADD OP_ENDIF
// 11 OP_GREATERTHANOREQUAL
libbitcoin::machine::operation::list witness_script_ops;
uint16_t idx = 0;
uint32_t total_weight = 0;
for (auto son_pubkey : son_pubkeys) {
ec_public key = ec_public(son_pubkey.first);
data_chunk key_data = to_chunk(key.point());
uint16_t weight = son_pubkey.second;
total_weight = total_weight + weight;
witness_script_ops.emplace_back(key_data);
witness_script_ops.emplace_back(opcode::checksig);
witness_script_ops.emplace_back(opcode::if_);
witness_script_ops.emplace_back(script_num(weight));
if (idx == 0) {
witness_script_ops.emplace_back(opcode::else_);
witness_script_ops.emplace_back(opcode::push_size_0);
} else {
witness_script_ops.emplace_back(opcode::add);
}
witness_script_ops.emplace_back(opcode::endif);
if (idx < son_pubkeys.size() - 1) {
witness_script_ops.emplace_back(opcode::swap);
}
idx = idx + 1;
}
witness_script_ops.emplace_back(script_num(total_weight * 2 / 3));
witness_script_ops.emplace_back(opcode::greaterthanorequal);
return script(witness_script_ops);
}
void read_tx_data_from_string(const std::string &string_buf, std::vector<unsigned char> &tx, std::vector<uint64_t> &in_amounts)
{
std::stringstream ss(string_buf);
boost::property_tree::ptree json;
boost::property_tree::read_json(ss, json);
std::string tx_hex = json.get<std::string>("tx_hex");
tx.clear();
tx.resize(tx_hex.size() / 2);
fc::from_hex(tx_hex, (char*)&tx[0], tx.size());
in_amounts.clear();
for(auto &v: json.get_child("in_amounts"))
in_amounts.push_back(fc::to_uint64(v.second.data()));
}
std::string save_tx_data_to_string(const std::vector<unsigned char> &tx, const std::vector<uint64_t> &in_amounts)
{
std::string res = "{\"tx_hex\":\"" + fc::to_hex((const char*)&tx[0], tx.size()) + "\",\"in_amounts\":[";
for (unsigned int idx = 0; idx < in_amounts.size(); ++idx) {
res += fc::to_string(in_amounts[idx]);
if (idx != in_amounts.size() - 1)
res += ",";
}
res += "]}";
return res;
}
std::string sidechain_net_handler_bitcoin::create_multisig_address_standalone(const std::vector<std::pair<std::string, uint16_t>> &son_pubkeys) {
//using namespace libbitcoin;
//using namespace libbitcoin::chain;
//using namespace libbitcoin::machine;
//using namespace libbitcoin::wallet;
//
//uint32_t nrequired = son_pubkeys.size() * 2 / 3 + 1;
//point_list keys;
//for (auto son_pubkey : son_pubkeys) {
// keys.push_back(ec_public(son_pubkey.first));
//}
//script witness_script = script::to_pay_multisig_pattern(nrequired, keys);
//
//// sha256 of witness script
//data_chunk multisig_hash = to_chunk(sha256_hash(witness_script.to_data(0)));
//
//// redeem script
//libbitcoin::machine::operation::list redeemscript_ops{libbitcoin::machine::operation(opcode(0)), libbitcoin::machine::operation(multisig_hash)};
//script redeem_script = script(redeemscript_ops);
//
//// address
//payment_address address = payment_address(redeem_script, payment_address_p2sh);
//
//std::stringstream ss;
//
//
//ss << "{\"result\": {\"address\": \"" << address.encoded() << "\", \"redeemScript\": \"" << encode_base16(witness_script.to_data(0)) << "\"" << "}, \"error\":null}";
//std::string res = ss.str();
//
//std::cout << "Redeem Script Hash: " << encode_base16(address.hash()) << std::endl;
//std::cout << "Payment Address: " << address.encoded() << std::endl;
//std::cout << "Redeem Script: " << redeem_script.to_string(0) << std::endl;
//std::cout << "Witness Script: " << witness_script.to_string(0) << std::endl;
//std::cout << "Witness Script: " << encode_base16(witness_script.to_data(0)) << std::endl;
//
//std::cout << res << std::endl;
////create_multisig_address_psbt(son_pubkeys);
//
//return res;
using namespace libbitcoin;
using namespace libbitcoin::chain;
using namespace libbitcoin::machine;
using namespace libbitcoin::wallet;
script witness_script = get_weighted_multisig_witness_script(son_pubkeys);
std::cout << "Witness Script is valid: " << witness_script.is_valid() << std::endl;
std::cout << "Witness Script operations are valid: " << witness_script.is_valid_operations() << std::endl;
// sha256 of witness script
data_chunk multisig_hash = to_chunk(sha256_hash(witness_script.to_data(0)));
// redeem script
libbitcoin::machine::operation::list redeemscript_ops{libbitcoin::machine::operation(opcode(0)), libbitcoin::machine::operation(multisig_hash)};
script redeem_script = script(redeemscript_ops);
// address
payment_address address = payment_address(redeem_script, payment_address_p2sh);
std::stringstream ss;
ss << "{\"result\": {\"address\": \"" << address.encoded() << "\", \"redeemScript\": \"" << encode_base16(witness_script.to_data(0)) << "\""
<< "}, \"error\":null}";
std::string res = ss.str();
std::cout << "Redeem Script Hash: " << encode_base16(address.hash()) << std::endl;
std::cout << "Payment Address: " << address.encoded() << std::endl;
std::cout << "Redeem Script: " << redeem_script.to_string(0) << std::endl;
std::cout << "Witness Script: " << witness_script.to_string(0) << std::endl;
std::cout << "Witness Script: " << encode_base16(witness_script.to_data(0)) << std::endl;
std::cout << res << std::endl;
//create_multisig_address_psbt(son_pubkeys);
return res;
}
std::string sidechain_net_handler_bitcoin::create_transaction_raw(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
@ -1475,8 +1867,34 @@ std::string sidechain_net_handler_bitcoin::create_transaction_standalone(const s
// }
// ]
//}
libbitcoin::chain::transaction tx;
tx.set_version(2u);
std::vector<uint64_t> in_amounts;
for (auto in : inputs) {
libbitcoin::chain::input bin;
libbitcoin::hash_digest tx_id;
libbitcoin::decode_hash(tx_id, in.txid_);
bin.set_previous_output(libbitcoin::chain::output_point(tx_id, in.out_num_));
bin.set_sequence(libbitcoin::max_input_sequence);
tx.inputs().push_back(bin);
in_amounts.push_back(in.amount_);
}
for (auto out : outputs) {
libbitcoin::chain::output bout;
uint64_t satoshis = out.second * 100000000.0;
bout.set_value(satoshis);
libbitcoin::wallet::payment_address addr(out.first);
if (addr.version() == payment_address_p2sh) {
bout.set_script(libbitcoin::chain::script::to_pay_script_hash_pattern(addr));
} else {
bout.set_script(libbitcoin::chain::script::to_pay_key_hash_pattern(addr));
}
tx.outputs().push_back(bout);
}
return "";
std::string tx_raw = save_tx_data_to_string(tx.to_data(), in_amounts);
return tx_raw;
}
std::string sidechain_net_handler_bitcoin::sign_transaction_raw(const sidechain_transaction_object &sto, bool &complete) {
@ -1578,8 +1996,42 @@ std::string sidechain_net_handler_bitcoin::sign_transaction_psbt(const sidechain
std::string sidechain_net_handler_bitcoin::sign_transaction_standalone(const sidechain_transaction_object &sto, bool &complete) {
complete = false;
complete = true;
return "";
std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain);
uint16_t weight = 0;
std::string prvkey = get_private_key(pubkey);
using namespace libbitcoin;
using namespace libbitcoin::machine;
using namespace libbitcoin::wallet;
libbitcoin::data_chunk data;
libbitcoin::ec_secret key;
libbitcoin::decode_base16(key, prvkey);
std::vector<uint64_t> in_amounts;
read_tx_data_from_string(sto.transaction, data, in_amounts);
libbitcoin::chain::transaction tx;
if (!tx.from_data(data)) {
elog("Failed to decode transaction ${tx}", ("tx", sto.transaction));
return "";
}
std::vector<std::pair<std::string, uint16_t>> son_pubkeys;
for (auto& son: sto.signers) {
std::string pub_key = son.sidechain_public_keys.at(sidechain_type::bitcoin);
son_pubkeys.push_back(std::make_pair(pub_key, son.weight));
if (son.son_id == plugin.get_current_son_id())
weight = son.weight;
}
libbitcoin::chain::script witness_script = get_weighted_multisig_witness_script(son_pubkeys);
vector<endorsement> sigs;
sigs.resize(tx.inputs().size());
for (unsigned int itr = 0; itr < sigs.size(); itr++) {
libbitcoin::chain::script::create_endorsement(sigs.at(itr), key, witness_script, tx, itr, sighash_algorithm::all, script_version::zero, in_amounts[itr]);
}
std::string tx_signature = write_byte_arrays_to_string(sigs);
complete = (sto.current_weight + weight > sto.threshold);
return tx_signature;
}
bool sidechain_net_handler_bitcoin::send_transaction_raw(const sidechain_transaction_object &sto, std::string &sidechain_transaction) {
@ -1632,6 +2084,54 @@ bool sidechain_net_handler_bitcoin::send_transaction_psbt(const sidechain_transa
return false;
} // namespace peerplays_sidechain
bool sidechain_net_handler_bitcoin::send_transaction_standalone(const sidechain_transaction_object &sto, std::string &sidechain_transaction) {
sidechain_transaction = "";
libbitcoin::data_chunk tx_buf;
std::vector<uint64_t> in_amounts;
read_tx_data_from_string(sto.transaction, tx_buf, in_amounts);
libbitcoin::chain::transaction tx;
if (!tx.from_data(tx_buf)) {
elog("Failed to decode transaction ${tx}", ("tx", sto.transaction));
return "";
}
std::vector<std::pair<std::string, uint16_t>> son_pubkeys;
for (auto& son: sto.signers) {
std::string pub_key = son.sidechain_public_keys.at(sidechain_type::bitcoin);
son_pubkeys.push_back(std::make_pair(pub_key, son.weight));
}
libbitcoin::chain::script witness_script = get_weighted_multisig_witness_script(son_pubkeys);
std::vector<libbitcoin::data_stack> scripts;
for (uint32_t idx = 0; idx < tx.inputs().size(); idx++)
scripts.push_back({witness_script.to_data(0)});
for (auto sig_data: sto.signatures) {
if (sig_data.second.size()) {
std::vector<libbitcoin::data_chunk> sigs = read_byte_arrays_from_string(sig_data.second);
FC_ASSERT(sigs.size() == scripts.size());
// place signatures in reverse order
for (uint32_t idx = 0; idx < scripts.size(); idx++)
{
auto& s = scripts.at(idx);
s.insert(s.begin(), sigs[idx]);
}
} else {
for (uint32_t idx = 0; idx < scripts.size(); idx++)
{
auto& s = scripts.at(idx);
s.insert(s.begin(), libbitcoin::data_chunk());
}
}
}
for (uint32_t idx = 0; idx < tx.inputs().size(); idx++)
tx.inputs()[idx].set_witness(scripts[idx]);
return bitcoin_client->sendrawtransaction(libbitcoin::encode_base16(tx.to_data()));
}
void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) {
std::string block = bitcoin_client->getblock(event_data);
if (block != "") {

View file

@ -1,5 +1,5 @@
{
"initial_timestamp": "2020-01-13T06:03:15",
"initial_timestamp": "2020-03-03T03:03:03",
"max_core_supply": "1000000000000000",
"initial_parameters": {
"current_fees": {
@ -333,9 +333,9 @@
"scale": 10000
},
"block_interval": 3,
"maintenance_interval": 86400,
"maintenance_interval": 120,
"maintenance_skip_slots": 3,
"committee_proposal_review_period": 1209600,
"committee_proposal_review_period": 600,
"maximum_transaction_size": 2048,
"maximum_block_size": 1228800000,
"maximum_time_until_expiration": 86400,
@ -388,7 +388,8 @@
"son_pay_time": 86400,
"son_deregister_time": 43200,
"son_heartbeat_frequency": 180,
"son_down_time": 360
"son_down_time": 360,
"son_bitcoin_min_tx_confirmations": 1
}
},
"initial_bts_accounts": [],
@ -527,7 +528,8 @@
"immutable_parameters": {
"min_committee_member_count": 11,
"min_witness_count": 11,
"min_son_count": 5,
"num_special_accounts": 0,
"num_special_assets": 0
}
}
}