Compare commits
21 commits
master
...
feature/SO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fae99b814 | ||
|
|
11722fed0c | ||
|
|
cbdff4e38a | ||
|
|
fa07a7bdec | ||
|
|
e5ba020105 | ||
|
|
d1c4cbaba8 | ||
|
|
4ea8ff3b61 | ||
|
|
3937a3a67b | ||
|
|
ca9e791c70 | ||
|
|
432977b282 | ||
|
|
efedd522ed | ||
|
|
042645ffdd | ||
|
|
6c4d3f2373 | ||
|
|
52895f65bf | ||
|
|
31a9579d71 | ||
|
|
f919b98d93 | ||
|
|
6271116231 | ||
|
|
646fd89441 | ||
|
|
e48ff83381 | ||
|
|
bcd95d4d3b | ||
|
|
ab6114ee21 |
7 changed files with 594 additions and 114 deletions
|
|
@ -16,7 +16,7 @@ endif()
|
||||||
unset(SUPPORT_MULTIPLE_SONS)
|
unset(SUPPORT_MULTIPLE_SONS)
|
||||||
unset(SUPPORT_MULTIPLE_SONS CACHE)
|
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
|
target_include_directories( peerplays_sidechain
|
||||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
#include <fc/crypto/base58.hpp>
|
#include <fc/crypto/base58.hpp>
|
||||||
#include <fc/crypto/elliptic.hpp>
|
#include <fc/crypto/elliptic.hpp>
|
||||||
|
#include <fc/crypto/hex.hpp>
|
||||||
#include <fc/crypto/ripemd160.hpp>
|
#include <fc/crypto/ripemd160.hpp>
|
||||||
#include <fc/crypto/sha256.hpp>
|
#include <fc/crypto/sha256.hpp>
|
||||||
#include <fc/io/raw.hpp>
|
#include <fc/io/raw.hpp>
|
||||||
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
|
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
|
||||||
#include <secp256k1.h>
|
#include <secp256k1.h>
|
||||||
|
|
||||||
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
|
||||||
namespace graphene { namespace peerplays_sidechain {
|
namespace graphene { namespace peerplays_sidechain {
|
||||||
|
|
||||||
static const unsigned char OP_0 = 0x00;
|
static const unsigned char OP_0 = 0x00;
|
||||||
|
|
@ -28,10 +31,13 @@ static const unsigned char OP_16 = 0x60;
|
||||||
|
|
||||||
static const unsigned char OP_IF = 0x63;
|
static const unsigned char OP_IF = 0x63;
|
||||||
static const unsigned char OP_ENDIF = 0x68;
|
static const unsigned char OP_ENDIF = 0x68;
|
||||||
|
static const unsigned char OP_DUP = 0x76;
|
||||||
static const unsigned char OP_SWAP = 0x7c;
|
static const unsigned char OP_SWAP = 0x7c;
|
||||||
static const unsigned char OP_EQUAL = 0x87;
|
static const unsigned char OP_EQUAL = 0x87;
|
||||||
|
static const unsigned char OP_EQUALVERIFY = 0x88;
|
||||||
static const unsigned char OP_ADD = 0x93;
|
static const unsigned char OP_ADD = 0x93;
|
||||||
static const unsigned char OP_GREATERTHAN = 0xa0;
|
static const unsigned char OP_GREATERTHAN = 0xa0;
|
||||||
|
static const unsigned char OP_GREATERTHANOREQUAL = 0xa2;
|
||||||
static const unsigned char OP_HASH160 = 0xa9;
|
static const unsigned char OP_HASH160 = 0xa9;
|
||||||
static const unsigned char OP_CHECKSIG = 0xac;
|
static const unsigned char OP_CHECKSIG = 0xac;
|
||||||
|
|
||||||
|
|
@ -193,7 +199,6 @@ void btc_outpoint::to_bytes(bytes &stream) const {
|
||||||
|
|
||||||
size_t btc_outpoint::fill_from_bytes(const bytes &data, size_t pos) {
|
size_t btc_outpoint::fill_from_bytes(const bytes &data, size_t pos) {
|
||||||
ReadBytesStream str(data, pos);
|
ReadBytesStream str(data, pos);
|
||||||
// TODO: read size?
|
|
||||||
str.read((unsigned char *)hash.data(), hash.data_size());
|
str.read((unsigned char *)hash.data(), hash.data_size());
|
||||||
n = str.readdata32();
|
n = str.readdata32();
|
||||||
return str.current_pos();
|
return str.current_pos();
|
||||||
|
|
@ -311,7 +316,7 @@ void add_data_to_script(bytes &script, const bytes &data) {
|
||||||
str.writedata(data);
|
str.writedata(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_number_to_script(bytes &script, unsigned char data) {
|
void add_number_to_script(bytes &script, uint32_t data) {
|
||||||
WriteBytesStream str(script);
|
WriteBytesStream str(script);
|
||||||
if (data == 0)
|
if (data == 0)
|
||||||
str.put(OP_0);
|
str.put(OP_0);
|
||||||
|
|
@ -347,12 +352,22 @@ void add_number_to_script(bytes &script, unsigned char data) {
|
||||||
str.put(OP_15);
|
str.put(OP_15);
|
||||||
else if (data == 16)
|
else if (data == 16)
|
||||||
str.put(OP_16);
|
str.put(OP_16);
|
||||||
else
|
else {
|
||||||
add_data_to_script(script, {data});
|
std::vector<unsigned char> result;
|
||||||
|
while (data) {
|
||||||
|
result.push_back(data & 0xff);
|
||||||
|
data >>= 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);
|
||||||
|
add_data_to_script(script, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data) {
|
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, uint16_t>> key_data) {
|
||||||
int total_weight = 0;
|
uint32_t total_weight = 0;
|
||||||
bytes result;
|
bytes result;
|
||||||
add_number_to_script(result, 0);
|
add_number_to_script(result, 0);
|
||||||
for (auto &p : key_data) {
|
for (auto &p : key_data) {
|
||||||
|
|
@ -362,19 +377,30 @@ bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> ke
|
||||||
add_data_to_script(result, bytes(raw_data.begin(), raw_data.begin() + raw_data.size()));
|
add_data_to_script(result, bytes(raw_data.begin(), raw_data.begin() + raw_data.size()));
|
||||||
result.push_back(OP_CHECKSIG);
|
result.push_back(OP_CHECKSIG);
|
||||||
result.push_back(OP_IF);
|
result.push_back(OP_IF);
|
||||||
add_number_to_script(result, static_cast<unsigned char>(p.second));
|
add_number_to_script(result, p.second);
|
||||||
result.push_back(OP_ADD);
|
result.push_back(OP_ADD);
|
||||||
result.push_back(OP_ENDIF);
|
result.push_back(OP_ENDIF);
|
||||||
}
|
}
|
||||||
int threshold_weight = 2 * total_weight / 3;
|
uint64_t threshold_weight = 2 * total_weight / 3;
|
||||||
add_number_to_script(result, static_cast<unsigned char>(threshold_weight));
|
add_number_to_script(result, threshold_weight);
|
||||||
result.push_back(OP_GREATERTHAN);
|
result.push_back(OP_GREATERTHANOREQUAL);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The Bech32 character set for encoding. */
|
/** The Bech32 character set for encoding. */
|
||||||
const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||||
|
|
||||||
|
/** The Bech32 character set for decoding. */
|
||||||
|
const int8_t charset_rev[128] = {
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||||
|
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||||
|
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||||
|
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1};
|
||||||
|
|
||||||
/** Concatenate two byte arrays. */
|
/** Concatenate two byte arrays. */
|
||||||
bytes cat(bytes x, const bytes &y) {
|
bytes cat(bytes x, const bytes &y) {
|
||||||
x.insert(x.end(), y.begin(), y.end());
|
x.insert(x.end(), y.begin(), y.end());
|
||||||
|
|
@ -409,6 +435,20 @@ uint32_t polymod(const bytes &values) {
|
||||||
return chk;
|
return chk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Expand a HRP for use in checksum computation. */
|
||||||
|
bytes bech32_expand_hrp(const std::string &hrp) {
|
||||||
|
bytes ret;
|
||||||
|
ret.reserve(hrp.size() + 90);
|
||||||
|
ret.resize(hrp.size() * 2 + 1);
|
||||||
|
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||||
|
unsigned char c = hrp[i];
|
||||||
|
ret[i] = c >> 5;
|
||||||
|
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||||
|
}
|
||||||
|
ret[hrp.size()] = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/** Create a checksum. */
|
/** Create a checksum. */
|
||||||
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||||
bytes enc = cat(expand_hrp(hrp), values);
|
bytes enc = cat(expand_hrp(hrp), values);
|
||||||
|
|
@ -422,8 +462,17 @@ bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Verify a checksum. */
|
||||||
|
bool bech32_verify_checksum(const std::string &hrp, const bytes &values) {
|
||||||
|
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||||
|
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||||
|
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||||
|
// resulting checksum to be 1 instead.
|
||||||
|
return polymod(cat(bech32_expand_hrp(hrp), values)) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
/** Encode a Bech32 string. */
|
/** Encode a Bech32 string. */
|
||||||
std::string bech32(const std::string &hrp, const bytes &values) {
|
std::string bech32_encode(const std::string &hrp, const bytes &values) {
|
||||||
bytes checksum = bech32_checksum(hrp, values);
|
bytes checksum = bech32_checksum(hrp, values);
|
||||||
bytes combined = cat(values, checksum);
|
bytes combined = cat(values, checksum);
|
||||||
std::string ret = hrp + '1';
|
std::string ret = hrp + '1';
|
||||||
|
|
@ -434,6 +483,33 @@ std::string bech32(const std::string &hrp, const bytes &values) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decode a Bech32 string. */
|
||||||
|
bytes bech32_decode(const std::string &str) {
|
||||||
|
if (str.size() > 90)
|
||||||
|
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||||
|
for (unsigned char c : str) {
|
||||||
|
if (c < 33 || c > 126)
|
||||||
|
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||||
|
}
|
||||||
|
size_t pos = str.rfind('1');
|
||||||
|
if (pos == str.npos || pos == 0 || pos + 7 > str.size())
|
||||||
|
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||||
|
std::string hrp = str.substr(0, pos);
|
||||||
|
bytes values(str.size() - 1 - pos);
|
||||||
|
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
|
||||||
|
unsigned char c = str[i + pos + 1];
|
||||||
|
int8_t rev = (c < 33 || c > 126) ? -1 : charset_rev[c];
|
||||||
|
if (rev == -1)
|
||||||
|
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||||
|
values[i] = rev;
|
||||||
|
}
|
||||||
|
if (!bech32_verify_checksum(hrp, values))
|
||||||
|
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||||
|
return bytes(values.begin(), values.end() - 6);
|
||||||
|
}
|
||||||
|
|
||||||
/** Convert from one power-of-2 number base to another. */
|
/** Convert from one power-of-2 number base to another. */
|
||||||
template <int frombits, int tobits, bool pad>
|
template <int frombits, int tobits, bool pad>
|
||||||
bool convertbits(bytes &out, const bytes &in) {
|
bool convertbits(bytes &out, const bytes &in) {
|
||||||
|
|
@ -464,10 +540,23 @@ std::string segwit_addr_encode(const std::string &hrp, uint8_t witver, const byt
|
||||||
bytes enc;
|
bytes enc;
|
||||||
enc.push_back(witver);
|
enc.push_back(witver);
|
||||||
convertbits<8, 5, true>(enc, witprog);
|
convertbits<8, 5, true>(enc, witprog);
|
||||||
std::string ret = bech32(hrp, enc);
|
std::string ret = bech32_encode(hrp, enc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Decode a SegWit address. */
|
||||||
|
bytes segwit_addr_decode(const std::string &addr) {
|
||||||
|
bytes dec = bech32_decode(addr);
|
||||||
|
if (dec.size() < 1)
|
||||||
|
FC_THROW("Invalid bech32 address ${a}", ("a", addr));
|
||||||
|
bytes conv;
|
||||||
|
if (!convertbits<5, 8, false>(conv, bytes(dec.begin() + 1, dec.end())) ||
|
||||||
|
conv.size() < 2 || conv.size() > 40 || dec[0] > 16 || (dec[0] == 0 && conv.size() != 20 && conv.size() != 32)) {
|
||||||
|
FC_THROW("Invalid bech32 address ${a}", ("a", addr));
|
||||||
|
}
|
||||||
|
return conv;
|
||||||
|
}
|
||||||
|
|
||||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) {
|
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) {
|
||||||
// calc script hash
|
// calc script hash
|
||||||
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||||
|
|
@ -476,14 +565,23 @@ std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_networ
|
||||||
case (mainnet):
|
case (mainnet):
|
||||||
return segwit_addr_encode("bc", 0, wp);
|
return segwit_addr_encode("bc", 0, wp);
|
||||||
case (testnet):
|
case (testnet):
|
||||||
case (regtest):
|
|
||||||
return segwit_addr_encode("tb", 0, wp);
|
return segwit_addr_encode("tb", 0, wp);
|
||||||
|
case (regtest):
|
||||||
|
return segwit_addr_encode("bcrt", 0, wp);
|
||||||
default:
|
default:
|
||||||
FC_THROW("Unknown bitcoin network type");
|
FC_THROW("Unknown bitcoin network type");
|
||||||
}
|
}
|
||||||
FC_THROW("Unknown bitcoin network type");
|
FC_THROW("Unknown bitcoin network type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes lock_script_from_pw_address(const std::string &address) {
|
||||||
|
bytes result;
|
||||||
|
result.push_back(OP_0);
|
||||||
|
bytes script_hash = segwit_addr_decode(address);
|
||||||
|
add_data_to_script(result, script_hash);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bytes lock_script_for_redeem_script(const bytes &script) {
|
bytes lock_script_for_redeem_script(const bytes &script) {
|
||||||
bytes result;
|
bytes result;
|
||||||
result.push_back(OP_0);
|
result.push_back(OP_0);
|
||||||
|
|
@ -677,4 +775,85 @@ bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector<
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_weighted_multisig_address(const std::vector<std::pair<std::string, uint16_t>> &public_keys, bitcoin_network network) {
|
||||||
|
std::vector<std::pair<fc::ecc::public_key, uint16_t>> key_data;
|
||||||
|
for (auto p : public_keys) {
|
||||||
|
fc::ecc::public_key_data kd;
|
||||||
|
fc::from_hex(p.first, kd.begin(), kd.size());
|
||||||
|
key_data.push_back(std::make_pair(fc::ecc::public_key(kd), p.second));
|
||||||
|
}
|
||||||
|
bytes redeem_script = generate_redeem_script(key_data);
|
||||||
|
return p2wsh_address_from_redeem_script(redeem_script, network);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes get_weighted_multisig_redeem_script(std::vector<std::pair<std::string, uint16_t>> public_keys) {
|
||||||
|
std::vector<std::pair<fc::ecc::public_key, uint16_t>> key_data;
|
||||||
|
for (auto p : public_keys)
|
||||||
|
{
|
||||||
|
fc::ecc::public_key_data kd;
|
||||||
|
fc::from_hex(p.first, kd.begin(), kd.size());
|
||||||
|
key_data.push_back(std::make_pair(fc::ecc::public_key(kd), p.second));
|
||||||
|
}
|
||||||
|
return generate_redeem_script(key_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bytes_to_hex(const bytes &data)
|
||||||
|
{
|
||||||
|
return fc::to_hex((char*)&data[0], data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes hex_to_bytes(const std::string &hex_str)
|
||||||
|
{
|
||||||
|
bytes res;
|
||||||
|
res.resize(hex_str.size() / 2);
|
||||||
|
fc::from_hex(hex_str, (char*)&res[0], res.size());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_bech_address(const std::string &address)
|
||||||
|
{
|
||||||
|
auto pos = address.find('1', 0);
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
return false;
|
||||||
|
std::string hrp = address.substr(0, pos);
|
||||||
|
return (hrp == "bc") || (hrp == "tb") || (hrp == "bcrt");
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes lock_script_from_legacy_address(const std::string &address) {
|
||||||
|
std::vector<char> data = fc::from_base58(address);
|
||||||
|
bytes addr_data((unsigned char*)&data[0], (unsigned char*)&data[0] + data.size());
|
||||||
|
// p2pkh
|
||||||
|
if( addr_data.size() == 25 && ( addr_data[0] == 0x00 ||
|
||||||
|
addr_data[0] == 0x6f ) ) {
|
||||||
|
// drop checksum
|
||||||
|
addr_data.erase(addr_data.end() - 4);
|
||||||
|
bytes result;
|
||||||
|
result.push_back(OP_DUP);
|
||||||
|
result.push_back(OP_HASH160);
|
||||||
|
add_data_to_script(result, addr_data);
|
||||||
|
result.push_back(OP_EQUALVERIFY);
|
||||||
|
result.push_back(OP_CHECKSIG);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// p2sh
|
||||||
|
if( addr_data.size() == 25 && ( addr_data[0] == 0x05 ||
|
||||||
|
addr_data[0] == 0xc4 ) ) {
|
||||||
|
// drop checksum
|
||||||
|
addr_data.erase(addr_data.end() - 4);
|
||||||
|
bytes result;
|
||||||
|
result.push_back(OP_HASH160);
|
||||||
|
add_data_to_script(result, addr_data);
|
||||||
|
result.push_back(OP_EQUAL);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
FC_THROW("Unsupported bitcoin address type in ${addr}", ("addr", address));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes lock_script_from_address(const std::string &address)
|
||||||
|
{
|
||||||
|
if(is_bech_address(address))
|
||||||
|
return lock_script_from_pw_address(address);
|
||||||
|
return lock_script_from_legacy_address(address);
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace graphene::peerplays_sidechain
|
}} // namespace graphene::peerplays_sidechain
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,14 @@ enum bitcoin_network {
|
||||||
regtest
|
regtest
|
||||||
};
|
};
|
||||||
|
|
||||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data);
|
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, uint16_t>> key_data);
|
||||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet);
|
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = regtest);
|
||||||
bytes lock_script_for_redeem_script(const bytes &script);
|
bytes lock_script_for_redeem_script(const bytes &script);
|
||||||
|
bytes lock_script_from_pw_address(const std::string &address);
|
||||||
|
bytes lock_script_from_address(const std::string &address);
|
||||||
|
|
||||||
|
std::string get_weighted_multisig_address(const std::vector<std::pair<std::string, uint16_t>> &public_keys, bitcoin_network network = regtest);
|
||||||
|
bytes get_weighted_multisig_redeem_script(std::vector<std::pair<std::string, uint16_t>> public_keys);
|
||||||
|
|
||||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||||
std::vector<uint64_t> in_amounts,
|
std::vector<uint64_t> in_amounts,
|
||||||
|
|
@ -64,6 +69,10 @@ bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx,
|
||||||
const std::vector<std::vector<bytes>> &signatures,
|
const std::vector<std::vector<bytes>> &signatures,
|
||||||
const bytes &redeem_script);
|
const bytes &redeem_script);
|
||||||
|
|
||||||
|
|
||||||
|
std::string bytes_to_hex(const bytes& data);
|
||||||
|
bytes hex_to_bytes(const std::string& hex_str);
|
||||||
|
|
||||||
struct btc_outpoint {
|
struct btc_outpoint {
|
||||||
fc::uint256 hash;
|
fc::uint256 hash;
|
||||||
uint32_t n;
|
uint32_t n;
|
||||||
|
|
@ -73,6 +82,19 @@ struct btc_outpoint {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct btc_in {
|
struct btc_in {
|
||||||
|
btc_in() = default;
|
||||||
|
btc_in(const btc_in &) = default;
|
||||||
|
btc_in(btc_in &&) = default;
|
||||||
|
btc_in &operator=(const btc_in &) = default;
|
||||||
|
|
||||||
|
btc_in(const std::string &txid, uint32_t out, uint32_t sequence = 0xffffffff) {
|
||||||
|
prevout.n = out;
|
||||||
|
prevout.hash = fc::uint256(txid);
|
||||||
|
// reverse hash due to the different from_hex algo in bitcoin
|
||||||
|
std::reverse(prevout.hash.data(), prevout.hash.data() + prevout.hash.data_size());
|
||||||
|
nSequence = sequence;
|
||||||
|
}
|
||||||
|
|
||||||
btc_outpoint prevout;
|
btc_outpoint prevout;
|
||||||
bytes scriptSig;
|
bytes scriptSig;
|
||||||
uint32_t nSequence;
|
uint32_t nSequence;
|
||||||
|
|
@ -83,6 +105,16 @@ struct btc_in {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct btc_out {
|
struct btc_out {
|
||||||
|
btc_out() = default;
|
||||||
|
btc_out(const btc_out &) = default;
|
||||||
|
btc_out(btc_out &&) = default;
|
||||||
|
btc_out &operator=(const btc_out &) = default;
|
||||||
|
|
||||||
|
btc_out(const std::string &address, uint64_t amount) :
|
||||||
|
nValue(amount),
|
||||||
|
scriptPubKey(lock_script_from_address(address)) {
|
||||||
|
}
|
||||||
|
|
||||||
int64_t nValue;
|
int64_t nValue;
|
||||||
bytes scriptPubKey;
|
bytes scriptPubKey;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,17 @@
|
||||||
|
|
||||||
namespace graphene { namespace peerplays_sidechain {
|
namespace graphene { namespace peerplays_sidechain {
|
||||||
|
|
||||||
|
enum class network_type {
|
||||||
|
mainnet,
|
||||||
|
testnet,
|
||||||
|
regtest
|
||||||
|
};
|
||||||
|
|
||||||
class btc_txout {
|
class btc_txout {
|
||||||
public:
|
public:
|
||||||
std::string txid_;
|
std::string txid_;
|
||||||
unsigned int out_num_;
|
unsigned int out_num_;
|
||||||
double amount_;
|
uint64_t amount_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class bitcoin_rpc_client {
|
class bitcoin_rpc_client {
|
||||||
|
|
@ -25,6 +31,7 @@ public:
|
||||||
std::string combinepsbt(const vector<std::string> &psbts);
|
std::string combinepsbt(const vector<std::string> &psbts);
|
||||||
std::string createmultisig(const uint32_t nrequired, const std::vector<std::string> public_keys);
|
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 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 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 createwallet(const std::string &wallet_name);
|
||||||
std::string decodepsbt(std::string const &tx_psbt);
|
std::string decodepsbt(std::string const &tx_psbt);
|
||||||
|
|
@ -34,6 +41,7 @@ public:
|
||||||
std::string finalizepsbt(std::string const &tx_psbt);
|
std::string finalizepsbt(std::string const &tx_psbt);
|
||||||
std::string getaddressinfo(const std::string &address);
|
std::string getaddressinfo(const std::string &address);
|
||||||
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
|
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);
|
std::string gettransaction(const std::string &txid, const bool include_watch_only = false);
|
||||||
void importaddress(const std::string &address_or_script);
|
void importaddress(const std::string &address_or_script);
|
||||||
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
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<bitcoin_rpc_client> bitcoin_client;
|
||||||
std::unique_ptr<zmq_listener> listener;
|
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;
|
fc::future<void> on_changed_objects_task;
|
||||||
|
|
||||||
std::string create_primary_wallet_transaction();
|
std::string create_primary_wallet_transaction();
|
||||||
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
|
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_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 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);
|
std::string sign_transaction(const sidechain_transaction_object &sto);
|
||||||
std::string send_transaction(const sidechain_transaction_object &sto);
|
std::string send_transaction(const sidechain_transaction_object &sto);
|
||||||
|
|
||||||
|
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_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_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);
|
std::string create_transaction_standalone(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs);
|
||||||
|
|
@ -124,6 +144,7 @@ private:
|
||||||
|
|
||||||
std::string send_transaction_raw(const sidechain_transaction_object &sto);
|
std::string send_transaction_raw(const sidechain_transaction_object &sto);
|
||||||
std::string send_transaction_psbt(const sidechain_transaction_object &sto);
|
std::string send_transaction_psbt(const sidechain_transaction_object &sto);
|
||||||
|
std::string send_transaction_standalone(const sidechain_transaction_object &sto);
|
||||||
|
|
||||||
void handle_event(const std::string &event_data);
|
void handle_event(const std::string &event_data);
|
||||||
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
|
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <bitcoin/bitcoin.hpp>
|
||||||
|
|
||||||
#include <boost/algorithm/hex.hpp>
|
#include <boost/algorithm/hex.hpp>
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
#include <fc/crypto/base64.hpp>
|
#include <fc/crypto/base64.hpp>
|
||||||
|
#include <fc/crypto/hex.hpp>
|
||||||
#include <fc/log/logger.hpp>
|
#include <fc/log/logger.hpp>
|
||||||
#include <fc/network/ip.hpp>
|
#include <fc/network/ip.hpp>
|
||||||
|
|
||||||
|
|
@ -15,6 +18,8 @@
|
||||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||||
#include <graphene/chain/son_info.hpp>
|
#include <graphene/chain/son_info.hpp>
|
||||||
#include <graphene/chain/son_wallet_object.hpp>
|
#include <graphene/chain/son_wallet_object.hpp>
|
||||||
|
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
|
||||||
|
#include <graphene/utilities/key_conversion.hpp>
|
||||||
|
|
||||||
namespace graphene { namespace peerplays_sidechain {
|
namespace graphene { namespace peerplays_sidechain {
|
||||||
|
|
||||||
|
|
@ -179,6 +184,34 @@ std::string bitcoin_rpc_client::createpsbt(const std::vector<btc_txout> &ins, co
|
||||||
return "";
|
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 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\", "
|
std::string body("{\"jsonrpc\": \"1.0\", \"id\":\"createrawtransaction\", "
|
||||||
"\"method\": \"createrawtransaction\", \"params\": [");
|
"\"method\": \"createrawtransaction\", \"params\": [");
|
||||||
|
|
@ -454,6 +487,33 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t
|
||||||
return "";
|
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 bitcoin_rpc_client::gettransaction(const std::string &txid, const bool include_watch_only) {
|
||||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": "
|
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"gettransaction\", \"method\": "
|
||||||
"\"gettransaction\", \"params\": [\"" +
|
"\"gettransaction\", \"params\": [\"" +
|
||||||
|
|
@ -527,7 +587,9 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent(const uint32_t minconf, c
|
||||||
btc_txout txo;
|
btc_txout txo;
|
||||||
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
|
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
|
||||||
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
|
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);
|
result.push_back(txo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -565,7 +627,9 @@ std::vector<btc_txout> bitcoin_rpc_client::listunspent_by_address_and_amount(con
|
||||||
btc_txout txo;
|
btc_txout txo;
|
||||||
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
|
txo.txid_ = entry.second.get_child("txid").get_value<std::string>();
|
||||||
txo.out_num_ = entry.second.get_child("vout").get_value<unsigned int>();
|
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);
|
result.push_back(txo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -878,6 +942,31 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain
|
||||||
bitcoin_client->loadwallet(wallet);
|
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 = std::unique_ptr<zmq_listener>(new zmq_listener(ip, zmq_port));
|
||||||
listener->event_received.connect([this](const std::string &event_data) {
|
listener->event_received.connect([this](const std::string &event_data) {
|
||||||
std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach();
|
std::thread(&sidechain_net_handler_bitcoin::handle_event, this, event_data).detach();
|
||||||
|
|
@ -936,13 +1025,12 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
|
||||||
|
|
||||||
if (son_sets_equal) {
|
if (son_sets_equal) {
|
||||||
auto active_sons = gpo.active_sons;
|
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) {
|
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 = create_multisig_address(son_pubkeys_bitcoin);
|
||||||
string reply_str = bitcoin_client->createmultisig(nrequired, son_pubkeys_bitcoin);
|
|
||||||
|
|
||||||
std::stringstream active_pw_ss(reply_str);
|
std::stringstream active_pw_ss(reply_str);
|
||||||
boost::property_tree::ptree active_pw_pt;
|
boost::property_tree::ptree active_pw_pt;
|
||||||
|
|
@ -1067,16 +1155,12 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
|
||||||
const chain::global_property_object &gpo = database.get_global_properties();
|
const chain::global_property_object &gpo = database.get_global_properties();
|
||||||
|
|
||||||
auto active_sons = gpo.active_sons;
|
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) {
|
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()) {
|
string reply_str = create_multisig_address(son_pubkeys_bitcoin);
|
||||||
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);
|
|
||||||
|
|
||||||
std::stringstream active_pw_ss(reply_str);
|
std::stringstream active_pw_ss(reply_str);
|
||||||
boost::property_tree::ptree active_pw_pt;
|
boost::property_tree::ptree active_pw_pt;
|
||||||
|
|
@ -1269,8 +1353,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction() {
|
||||||
uint64_t min_fee_rate = 1000;
|
uint64_t min_fee_rate = 1000;
|
||||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
fee_rate = std::max(fee_rate, min_fee_rate);
|
||||||
|
|
||||||
double min_amount = ((double)fee_rate / 100000000.0); // Account only for relay fee for now
|
uint64_t total_amount = 0.0;
|
||||||
double total_amount = 0.0;
|
|
||||||
std::vector<btc_txout> inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0);
|
std::vector<btc_txout> inputs = bitcoin_client->listunspent_by_address_and_amount(prev_pw_address, 0);
|
||||||
|
|
||||||
if (inputs.size() == 0) {
|
if (inputs.size() == 0) {
|
||||||
|
|
@ -1281,14 +1364,14 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction() {
|
||||||
total_amount += utx.amount_;
|
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));
|
elog("Failed not enough BTC to transfer from ${fa}", ("fa", prev_pw_address));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fc::flat_map<std::string, double> outputs;
|
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);
|
return create_transaction(inputs, outputs);
|
||||||
}
|
}
|
||||||
|
|
@ -1329,7 +1412,7 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_
|
||||||
|
|
||||||
outputs[pw_address] = transfer_amount;
|
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) {
|
std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
|
||||||
|
|
@ -1351,8 +1434,7 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
|
||||||
uint64_t min_fee_rate = 1000;
|
uint64_t min_fee_rate = 1000;
|
||||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
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
|
uint64_t total_amount = 0;
|
||||||
double total_amount = 0.0;
|
|
||||||
std::vector<btc_txout> inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0);
|
std::vector<btc_txout> inputs = bitcoin_client->listunspent_by_address_and_amount(pw_address, 0);
|
||||||
|
|
||||||
if (inputs.size() == 0) {
|
if (inputs.size() == 0) {
|
||||||
|
|
@ -1363,7 +1445,7 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
|
||||||
total_amount += utx.amount_;
|
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));
|
elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address));
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -1371,20 +1453,30 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
|
||||||
|
|
||||||
fc::flat_map<std::string, double> outputs;
|
fc::flat_map<std::string, double> outputs;
|
||||||
outputs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0;
|
outputs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0;
|
||||||
if ((total_amount - min_amount) > 0.0) {
|
if ((total_amount - fee_rate) > 0.0) {
|
||||||
outputs[pw_address] = total_amount - min_amount;
|
outputs[pw_address] = double(total_amount - swwo.withdraw_amount.value - fee_rate) / 100000000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return create_transaction(inputs, outputs);
|
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
|
// Creates transaction in any format
|
||||||
// Function to actually create transaction should return transaction string, or empty string in case of failure
|
// 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 sidechain_net_handler_bitcoin::create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
||||||
std::string new_tx = "";
|
std::string new_tx = "";
|
||||||
//new_tx = create_transaction_raw(inputs, outputs);
|
//new_tx = create_transaction_raw(inputs, outputs);
|
||||||
new_tx = create_transaction_psbt(inputs, outputs);
|
//new_tx = create_transaction_psbt(inputs, outputs);
|
||||||
//new_tx = create_transaction_standalone(inputs, outputs);
|
new_tx = create_transaction_standalone(inputs, outputs);
|
||||||
return new_tx;
|
return new_tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1393,14 +1485,155 @@ std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector<
|
||||||
std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) {
|
std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) {
|
||||||
std::string new_tx = "";
|
std::string new_tx = "";
|
||||||
//new_tx = sign_transaction_raw(sto);
|
//new_tx = sign_transaction_raw(sto);
|
||||||
new_tx = sign_transaction_psbt(sto);
|
if (sto.object_id.type() == 30) {
|
||||||
|
new_tx = sign_transaction_psbt(sto);
|
||||||
|
} else {
|
||||||
|
new_tx = sign_transaction_standalone(sto);
|
||||||
|
}
|
||||||
//new_tx = sign_transaction_standalone(sto);
|
//new_tx = sign_transaction_standalone(sto);
|
||||||
return new_tx;
|
return new_tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_transaction_object &sto) {
|
std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_transaction_object &sto) {
|
||||||
//return send_transaction_raw(sto);
|
//return send_transaction_raw(sto);
|
||||||
return send_transaction_psbt(sto);
|
//return send_transaction_psbt(sto);
|
||||||
|
if (sto.object_id.type() == 30) {
|
||||||
|
return send_transaction_psbt(sto);
|
||||||
|
} else {
|
||||||
|
return send_transaction_standalone(sto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
bytes redeem_script = get_weighted_multisig_redeem_script(son_pubkeys);
|
||||||
|
std::string address = get_weighted_multisig_address(son_pubkeys);
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "{\"result\": {\"address\": \"" << address << "\", \"redeemScript\": \"" << bytes_to_hex(redeem_script) << "\""
|
||||||
|
<< "}, \"error\":null}";
|
||||||
|
std::string res = ss.str();
|
||||||
|
|
||||||
|
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) {
|
std::string sidechain_net_handler_bitcoin::create_transaction_raw(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
||||||
|
|
@ -1412,64 +1645,24 @@ std::string sidechain_net_handler_bitcoin::create_transaction_psbt(const std::ve
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sidechain_net_handler_bitcoin::create_transaction_standalone(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
std::string sidechain_net_handler_bitcoin::create_transaction_standalone(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
||||||
// Examples
|
btc_tx tx;
|
||||||
|
tx.nVersion = 2;
|
||||||
|
tx.nLockTime = 0;
|
||||||
|
tx.hasWitness = true;
|
||||||
|
std::vector<uint64_t> in_amounts;
|
||||||
|
for (auto in : inputs) {
|
||||||
|
tx.vin.push_back(btc_in(in.txid_, in.out_num_));
|
||||||
|
in_amounts.push_back(in.amount_);
|
||||||
|
}
|
||||||
|
for (auto out : outputs) {
|
||||||
|
tx.vout.push_back(btc_out(out.first, uint64_t(out.second * 100000000.0)));
|
||||||
|
}
|
||||||
|
|
||||||
// Transaction with no inputs and outputs
|
bytes tx_buf;
|
||||||
//bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" createrawtransaction '[]' '[]'
|
tx.to_bytes(tx_buf);
|
||||||
//02000000000000000000
|
std::string tx_raw = save_tx_data_to_string(tx_buf, in_amounts);
|
||||||
//bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" decoderawtransaction 02000000000000000000
|
|
||||||
//{
|
|
||||||
// "txid": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
|
|
||||||
// "hash": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
|
|
||||||
// "version": 2,
|
|
||||||
// "size": 10,
|
|
||||||
// "vsize": 10,
|
|
||||||
// "weight": 40,
|
|
||||||
// "locktime": 0,
|
|
||||||
// "vin": [
|
|
||||||
// ],
|
|
||||||
// "vout": [
|
|
||||||
// ]
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Transaction with input and output
|
return tx_raw;
|
||||||
//{
|
|
||||||
// "txid": "ff60f48f767bbf70d79efc1347b5554b481f14fda68709839091286e035e669b",
|
|
||||||
// "hash": "ff60f48f767bbf70d79efc1347b5554b481f14fda68709839091286e035e669b",
|
|
||||||
// "version": 2,
|
|
||||||
// "size": 83,
|
|
||||||
// "vsize": 83,
|
|
||||||
// "weight": 332,
|
|
||||||
// "locktime": 0,
|
|
||||||
// "vin": [
|
|
||||||
// {
|
|
||||||
// "txid": "3d322dc2640239a2e68e182b254d19c88e5172a61947f94a105c3f57618092ff",
|
|
||||||
// "vout": 0,
|
|
||||||
// "scriptSig": {
|
|
||||||
// "asm": "",
|
|
||||||
// "hex": ""
|
|
||||||
// },
|
|
||||||
// "sequence": 4294967295
|
|
||||||
// }
|
|
||||||
// ],
|
|
||||||
// "vout": [
|
|
||||||
// {
|
|
||||||
// "value": 1.00000000,
|
|
||||||
// "n": 0,
|
|
||||||
// "scriptPubKey": {
|
|
||||||
// "asm": "OP_HASH160 b87c323018cae236eb03a1f63000c85b672270f6 OP_EQUAL",
|
|
||||||
// "hex": "a914b87c323018cae236eb03a1f63000c85b672270f687",
|
|
||||||
// "reqSigs": 1,
|
|
||||||
// "type": "scripthash",
|
|
||||||
// "addresses": [
|
|
||||||
// "2NA4h6sc9oZ4ogfNKU9Wp6fkqPZLZPqqpgf"
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
//}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sidechain_net_handler_bitcoin::sign_transaction_raw(const sidechain_transaction_object &sto) {
|
std::string sidechain_net_handler_bitcoin::sign_transaction_raw(const sidechain_transaction_object &sto) {
|
||||||
|
|
@ -1564,7 +1757,31 @@ 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) {
|
std::string sidechain_net_handler_bitcoin::sign_transaction_standalone(const sidechain_transaction_object &sto) {
|
||||||
|
|
||||||
return "";
|
std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain);
|
||||||
|
std::string prvkey = get_private_key(pubkey);
|
||||||
|
|
||||||
|
bytes unsigned_tx;
|
||||||
|
std::vector<uint64_t> in_amounts;
|
||||||
|
read_tx_data_from_string(sto.transaction, unsigned_tx, in_amounts);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
bytes witness_script = get_weighted_multisig_redeem_script(son_pubkeys);
|
||||||
|
|
||||||
|
fc::optional<fc::ecc::private_key> btc_private_key = graphene::utilities::wif_to_key(prvkey);
|
||||||
|
if (!btc_private_key)
|
||||||
|
{
|
||||||
|
elog("Invalid private key ${pk}", ("pk", prvkey));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
vector<bytes> sigs = signatures_for_raw_transaction(unsigned_tx, in_amounts, witness_script, *btc_private_key);
|
||||||
|
|
||||||
|
std::string tx_signature = write_byte_arrays_to_string(sigs);
|
||||||
|
ilog("signatures: ${s}", ("s", tx_signature));
|
||||||
|
return tx_signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sidechain_net_handler_bitcoin::send_transaction_raw(const sidechain_transaction_object &sto) {
|
std::string sidechain_net_handler_bitcoin::send_transaction_raw(const sidechain_transaction_object &sto) {
|
||||||
|
|
@ -1613,6 +1830,35 @@ std::string sidechain_net_handler_bitcoin::send_transaction_psbt(const sidechain
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string sidechain_net_handler_bitcoin::send_transaction_standalone(const sidechain_transaction_object &sto) {
|
||||||
|
bytes unsigned_tx;
|
||||||
|
std::vector<uint64_t> in_amounts;
|
||||||
|
read_tx_data_from_string(sto.transaction, unsigned_tx, in_amounts);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
bytes witness_script = get_weighted_multisig_redeem_script(son_pubkeys);
|
||||||
|
|
||||||
|
uint32_t inputs_number = in_amounts.size();
|
||||||
|
vector<bytes> dummy;
|
||||||
|
dummy.resize(inputs_number);
|
||||||
|
vector<vector<bytes>> signatures;
|
||||||
|
for (unsigned idx = 0; idx < sto.signatures.size(); ++idx) {
|
||||||
|
if(sto.signatures[idx].second.empty())
|
||||||
|
signatures.push_back(dummy);
|
||||||
|
else
|
||||||
|
signatures.push_back(read_byte_arrays_from_string(sto.signatures[idx].second));
|
||||||
|
}
|
||||||
|
bytes signed_tx = add_signatures_to_unsigned_tx(unsigned_tx, signatures, witness_script);
|
||||||
|
std::string txstr = bytes_to_hex(signed_tx);
|
||||||
|
std::string res = bitcoin_client->sendrawtransaction(txstr);
|
||||||
|
ilog("send_transaction_standalone: ${tx}, [${res}]", ("tx", txstr)("res", res));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) {
|
void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) {
|
||||||
std::string block = bitcoin_client->getblock(event_data);
|
std::string block = bitcoin_client->getblock(event_data);
|
||||||
if (block != "") {
|
if (block != "") {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"initial_timestamp": "2020-01-13T06:03:15",
|
"initial_timestamp": "2020-03-03T03:03:03",
|
||||||
"max_core_supply": "1000000000000000",
|
"max_core_supply": "1000000000000000",
|
||||||
"initial_parameters": {
|
"initial_parameters": {
|
||||||
"current_fees": {
|
"current_fees": {
|
||||||
|
|
@ -333,9 +333,9 @@
|
||||||
"scale": 10000
|
"scale": 10000
|
||||||
},
|
},
|
||||||
"block_interval": 3,
|
"block_interval": 3,
|
||||||
"maintenance_interval": 86400,
|
"maintenance_interval": 120,
|
||||||
"maintenance_skip_slots": 3,
|
"maintenance_skip_slots": 3,
|
||||||
"committee_proposal_review_period": 1209600,
|
"committee_proposal_review_period": 600,
|
||||||
"maximum_transaction_size": 2048,
|
"maximum_transaction_size": 2048,
|
||||||
"maximum_block_size": 1228800000,
|
"maximum_block_size": 1228800000,
|
||||||
"maximum_time_until_expiration": 86400,
|
"maximum_time_until_expiration": 86400,
|
||||||
|
|
@ -388,7 +388,8 @@
|
||||||
"son_pay_time": 86400,
|
"son_pay_time": 86400,
|
||||||
"son_deregister_time": 43200,
|
"son_deregister_time": 43200,
|
||||||
"son_heartbeat_frequency": 180,
|
"son_heartbeat_frequency": 180,
|
||||||
"son_down_time": 360
|
"son_down_time": 360,
|
||||||
|
"son_bitcoin_min_tx_confirmations": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"initial_bts_accounts": [],
|
"initial_bts_accounts": [],
|
||||||
|
|
@ -527,6 +528,7 @@
|
||||||
"immutable_parameters": {
|
"immutable_parameters": {
|
||||||
"min_committee_member_count": 11,
|
"min_committee_member_count": 11,
|
||||||
"min_witness_count": 11,
|
"min_witness_count": 11,
|
||||||
|
"min_son_count": 5,
|
||||||
"num_special_accounts": 0,
|
"num_special_accounts": 0,
|
||||||
"num_special_assets": 0
|
"num_special_assets": 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
|
||||||
for(auto& key: priv_old)
|
for(auto& key: priv_old)
|
||||||
pub_old.push_back(key.get_public_key());
|
pub_old.push_back(key.get_public_key());
|
||||||
// old key weights
|
// old key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_old;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||||
// redeem script for old PW
|
// redeem script for old PW
|
||||||
|
|
@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
|
||||||
for(auto& key: priv_new)
|
for(auto& key: priv_new)
|
||||||
pub_new.push_back(key.get_public_key());
|
pub_new.push_back(key.get_public_key());
|
||||||
// new key weights
|
// new key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_new;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||||
// redeem script for new PW
|
// redeem script for new PW
|
||||||
|
|
@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign)
|
||||||
for(auto& key: priv_old)
|
for(auto& key: priv_old)
|
||||||
pub_old.push_back(key.get_public_key());
|
pub_old.push_back(key.get_public_key());
|
||||||
// old key weights
|
// old key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_old;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||||
// redeem script for old PW
|
// redeem script for old PW
|
||||||
|
|
@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign)
|
||||||
for(auto& key: priv_new)
|
for(auto& key: priv_new)
|
||||||
pub_new.push_back(key.get_public_key());
|
pub_new.push_back(key.get_public_key());
|
||||||
// new key weights
|
// new key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_new;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||||
// redeem script for new PW
|
// redeem script for new PW
|
||||||
|
|
@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign2)
|
||||||
for(auto& key: priv_old)
|
for(auto& key: priv_old)
|
||||||
pub_old.push_back(key.get_public_key());
|
pub_old.push_back(key.get_public_key());
|
||||||
// old key weights
|
// old key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_old;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||||
// redeem script for old PW
|
// redeem script for old PW
|
||||||
|
|
@ -268,7 +268,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign2)
|
||||||
for(auto& key: priv_new)
|
for(auto& key: priv_new)
|
||||||
pub_new.push_back(key.get_public_key());
|
pub_new.push_back(key.get_public_key());
|
||||||
// new key weights
|
// new key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_new;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||||
// redeem script for new PW
|
// redeem script for new PW
|
||||||
|
|
@ -345,7 +345,7 @@ BOOST_AUTO_TEST_CASE(pw_partially_sign)
|
||||||
for(auto& key: priv_old)
|
for(auto& key: priv_old)
|
||||||
pub_old.push_back(key.get_public_key());
|
pub_old.push_back(key.get_public_key());
|
||||||
// old key weights
|
// old key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_old;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||||
// redeem script for old PW
|
// redeem script for old PW
|
||||||
|
|
@ -370,7 +370,7 @@ BOOST_AUTO_TEST_CASE(pw_partially_sign)
|
||||||
for(auto& key: priv_new)
|
for(auto& key: priv_new)
|
||||||
pub_new.push_back(key.get_public_key());
|
pub_new.push_back(key.get_public_key());
|
||||||
// new key weights
|
// new key weights
|
||||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
std::vector<std::pair<fc::ecc::public_key, uint16_t> > weights_new;
|
||||||
for(unsigned i = 0; i < 15; ++i)
|
for(unsigned i = 0; i < 15; ++i)
|
||||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||||
// redeem script for new PW
|
// redeem script for new PW
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue