SON Weighted Multi Signature Signing (#349)
* Bring in the bitcoin utils code into plugin * Add tx creation, signing and tests * tx deserialization fix * add 10-of-14 multisig address test * Add signing and verification tests and sign_transaction_standalone * Add send_transaction_standalone function * Debug logs and additional tests * Fix for son deletion in the middle * Extend script_builder * Witness script for weighted wallet * btc_weighted_multisig_address implementation * Fix for bad-txns-nonstandard-inputs * Weighted multisignature address test * Create test tx with weighted multisig wallet * Fix the issues with tx signing * End to End test weighted multi sig * 1 or m-of-n deposit address support * Move network_type enum to the base class * btc_one_or_weighted_multisig_address implementation * Simplify redeem script * Fix error in redeem_script * btc_one_or_weighted_multisig_address tests * Refactor sidechain address mapping * CLANG code format * CLANG code format sidechain tests * Integration of deposit and rest of weighted wallets, withdrawal fee fix, whole code refactoring * Move util functions to Utils file * Add proper checks for withdraw fee * Deposit address creation, import deposit/withdraw addresses, some code cleanup Co-authored-by: satyakoneru <15652887+satyakoneru@users.noreply.github.com> Co-authored-by: gladcow <s.gladkov@pbsa.info> Co-authored-by: Srdjan Obucina <obucinac@gmail.com>
This commit is contained in:
parent
fbb4bbd757
commit
b436b790fb
39 changed files with 3491 additions and 1612 deletions
|
|
@ -330,13 +330,13 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_add_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_update_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_delete_operation& op ) {
|
||||
_impacted.insert( op.sidechain_address_account );
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
|
|
|
|||
|
|
@ -10,12 +10,16 @@ namespace graphene { namespace chain {
|
|||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
string deposit_public_key;
|
||||
string deposit_address;
|
||||
string withdraw_public_key;
|
||||
string withdraw_address;
|
||||
|
||||
account_id_type fee_payer()const { return sidechain_address_account; }
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
|
|
@ -24,13 +28,17 @@ namespace graphene { namespace chain {
|
|||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_address_id_type sidechain_address_id;
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
optional<string> deposit_public_key;
|
||||
optional<string> deposit_address;
|
||||
optional<string> withdraw_public_key;
|
||||
optional<string> withdraw_address;
|
||||
|
||||
account_id_type fee_payer()const { return sidechain_address_account; }
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
|
|
@ -39,26 +47,28 @@ namespace graphene { namespace chain {
|
|||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_address_id_type sidechain_address_id;
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
|
||||
account_id_type fee_payer()const { return sidechain_address_account; }
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee)
|
||||
(sidechain_address_account)(sidechain)(deposit_address)(withdraw_address) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee)(payer)
|
||||
(sidechain_address_account)(sidechain)(deposit_public_key)(deposit_address)(withdraw_public_key)(withdraw_address) )
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee)
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee)(payer)
|
||||
(sidechain_address_id)
|
||||
(sidechain_address_account)(sidechain)(deposit_address)(withdraw_address) )
|
||||
(sidechain_address_account)(sidechain)(deposit_public_key)(deposit_address)(withdraw_public_key)(withdraw_address) )
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee)
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee)(payer)
|
||||
(sidechain_address_id)
|
||||
(sidechain_address_account)(sidechain) )
|
||||
|
|
|
|||
|
|
@ -22,17 +22,23 @@ namespace graphene { namespace chain {
|
|||
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
string deposit_public_key;
|
||||
string deposit_address;
|
||||
string withdraw_public_key;
|
||||
string withdraw_address;
|
||||
|
||||
sidechain_address_object() :
|
||||
sidechain(sidechain_type::bitcoin),
|
||||
deposit_public_key(""),
|
||||
deposit_address(""),
|
||||
withdraw_public_key(""),
|
||||
withdraw_address("") {}
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
struct by_sidechain;
|
||||
struct by_deposit_public_key;
|
||||
struct by_withdraw_public_key;
|
||||
struct by_account_and_sidechain;
|
||||
struct by_sidechain_and_deposit_address;
|
||||
using sidechain_address_multi_index_type = multi_index_container<
|
||||
|
|
@ -47,6 +53,12 @@ namespace graphene { namespace chain {
|
|||
ordered_non_unique< tag<by_sidechain>,
|
||||
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>
|
||||
>,
|
||||
ordered_non_unique< tag<by_deposit_public_key>,
|
||||
member<sidechain_address_object, std::string, &sidechain_address_object::deposit_public_key>
|
||||
>,
|
||||
ordered_non_unique< tag<by_withdraw_public_key>,
|
||||
member<sidechain_address_object, std::string, &sidechain_address_object::withdraw_public_key>
|
||||
>,
|
||||
ordered_unique< tag<by_account_and_sidechain>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, account_id_type, &sidechain_address_object::sidechain_address_account>,
|
||||
|
|
@ -66,4 +78,4 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sidechain_address_object, (graphene::db::object),
|
||||
(sidechain_address_account) (sidechain) (deposit_address) (withdraw_address) )
|
||||
(sidechain_address_account) (sidechain) (deposit_public_key) (deposit_address) (withdraw_public_key) (withdraw_address) )
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address
|
|||
const auto& new_sidechain_address_object = db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ){
|
||||
obj.sidechain_address_account = op.sidechain_address_account;
|
||||
obj.sidechain = op.sidechain;
|
||||
obj.deposit_public_key = op.deposit_public_key;
|
||||
obj.deposit_address = op.deposit_address;
|
||||
obj.withdraw_public_key = op.withdraw_public_key;
|
||||
obj.withdraw_address = op.withdraw_address;
|
||||
});
|
||||
return new_sidechain_address_object.id;
|
||||
|
|
@ -40,7 +42,9 @@ object_id_type update_sidechain_address_evaluator::do_apply(const sidechain_addr
|
|||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](sidechain_address_object &sao) {
|
||||
if(op.deposit_public_key.valid()) sao.deposit_public_key = *op.deposit_public_key;
|
||||
if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address;
|
||||
if(op.withdraw_public_key.valid()) sao.withdraw_public_key = *op.withdraw_public_key;
|
||||
if(op.withdraw_address.valid()) sao.withdraw_address = *op.withdraw_address;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
file(GLOB HEADERS "include/graphene/peerplays_sidechain/*.hpp")
|
||||
file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp")
|
||||
|
||||
add_library( peerplays_sidechain
|
||||
peerplays_sidechain_plugin.cpp
|
||||
|
|
@ -6,7 +6,13 @@ add_library( peerplays_sidechain
|
|||
sidechain_net_handler.cpp
|
||||
sidechain_net_handler_bitcoin.cpp
|
||||
sidechain_net_handler_peerplays.cpp
|
||||
bitcoin_utils.cpp
|
||||
bitcoin/bech32.cpp
|
||||
bitcoin/bitcoin_address.cpp
|
||||
bitcoin/bitcoin_script.cpp
|
||||
bitcoin/bitcoin_transaction.cpp
|
||||
bitcoin/segwit_addr.cpp
|
||||
bitcoin/utils.cpp
|
||||
bitcoin/sign_bitcoin_transaction.cpp
|
||||
)
|
||||
|
||||
if (SUPPORT_MULTIPLE_SONS)
|
||||
|
|
|
|||
193
libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp
Normal file
193
libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bech32.hpp>
|
||||
|
||||
// #include <bech32.h>
|
||||
|
||||
namespace {
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
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. */
|
||||
data Cat(data x, const data &y) {
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
|
||||
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||
* bits correspond to earlier values. */
|
||||
uint32_t PolyMod(const data &v) {
|
||||
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
|
||||
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
|
||||
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
|
||||
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
|
||||
|
||||
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
|
||||
// v(x) mod g(x), where g(x) is the Bech32 generator,
|
||||
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
|
||||
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
|
||||
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
|
||||
// fact guarantee detection of up to 4 errors within a window of 89 characters.
|
||||
|
||||
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
|
||||
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
|
||||
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
|
||||
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
|
||||
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
|
||||
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
|
||||
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
|
||||
|
||||
// During the course of the loop below, `c` contains the bitpacked coefficients of the
|
||||
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
|
||||
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
|
||||
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
|
||||
// for `c`.
|
||||
uint32_t c = 1;
|
||||
for (auto v_i : v) {
|
||||
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
|
||||
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
|
||||
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
|
||||
// process. Simplifying:
|
||||
// c'(x) = (f(x) * x + v_i) mod g(x)
|
||||
// ((f(x) mod g(x)) * x + v_i) mod g(x)
|
||||
// (c(x) * x + v_i) mod g(x)
|
||||
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
|
||||
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
|
||||
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
|
||||
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
|
||||
// If we call (x^6 mod g(x)) = k(x), this can be written as
|
||||
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
|
||||
|
||||
// First, determine the value of c0:
|
||||
uint8_t c0 = c >> 25;
|
||||
|
||||
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
|
||||
c = ((c & 0x1ffffff) << 5) ^ v_i;
|
||||
|
||||
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
|
||||
if (c0 & 1)
|
||||
c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
|
||||
if (c0 & 2)
|
||||
c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
|
||||
if (c0 & 4)
|
||||
c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
|
||||
if (c0 & 8)
|
||||
c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
|
||||
if (c0 & 16)
|
||||
c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Convert to lower case. */
|
||||
inline unsigned char LowerCase(unsigned char c) {
|
||||
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
data ExpandHRP(const std::string &hrp) {
|
||||
data 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;
|
||||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool VerifyChecksum(const std::string &hrp, const data &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(ExpandHRP(hrp), values)) == 1;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
data CreateChecksum(const std::string &hrp, const data &values) {
|
||||
data enc = Cat(ExpandHRP(hrp), values);
|
||||
enc.resize(enc.size() + 6); // Append 6 zeroes
|
||||
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
data ret(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
ret[i] = (mod >> (5 * (5 - i))) & 31;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace bech32 {
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string Encode(const std::string &hrp, const data &values) {
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (auto c : combined) {
|
||||
ret += CHARSET[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Bech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string &str) {
|
||||
bool lower = false, upper = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
unsigned char c = str[i];
|
||||
if (c < 33 || c > 126)
|
||||
return {};
|
||||
if (c >= 'a' && c <= 'z')
|
||||
lower = true;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
upper = true;
|
||||
}
|
||||
if (lower && upper)
|
||||
return {};
|
||||
size_t pos = str.rfind('1');
|
||||
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
|
||||
return {};
|
||||
}
|
||||
data 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) {
|
||||
return {};
|
||||
}
|
||||
values[i] = rev;
|
||||
}
|
||||
std::string hrp;
|
||||
for (size_t i = 0; i < pos; ++i) {
|
||||
hrp += LowerCase(str[i]);
|
||||
}
|
||||
if (!VerifyChecksum(hrp, values)) {
|
||||
return {};
|
||||
}
|
||||
return {hrp, data(values.begin(), values.end() - 6)};
|
||||
}
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::bech32
|
||||
|
|
@ -0,0 +1,416 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
bool bitcoin_address::operator==(const bitcoin_address &btc_addr) const {
|
||||
return (this->address == btc_addr.address) &&
|
||||
(this->type == btc_addr.type) &&
|
||||
(this->raw_address == btc_addr.raw_address) &&
|
||||
(this->network_type == btc_addr.network_type);
|
||||
}
|
||||
|
||||
bytes bitcoin_address::get_script() const {
|
||||
switch (type) {
|
||||
case payment_type::NULLDATA:
|
||||
return script_builder() << op::RETURN << raw_address;
|
||||
case payment_type::P2PK:
|
||||
return script_builder() << raw_address << op::CHECKSIG;
|
||||
case payment_type::P2PKH:
|
||||
return script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG;
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
return script_builder() << op::HASH160 << raw_address << op::EQUAL;
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH:
|
||||
return script_builder() << op::_0 << raw_address;
|
||||
default:
|
||||
return raw_address;
|
||||
}
|
||||
}
|
||||
|
||||
payment_type bitcoin_address::determine_type() {
|
||||
if (is_p2pk()) {
|
||||
return payment_type::P2PK;
|
||||
} else if (is_p2wpkh()) {
|
||||
return payment_type::P2WPKH;
|
||||
} else if (is_p2wsh()) {
|
||||
return payment_type::P2WSH;
|
||||
} else if (is_p2pkh()) {
|
||||
return payment_type::P2PKH;
|
||||
} else if (is_p2sh()) {
|
||||
return payment_type::P2SH;
|
||||
} else {
|
||||
return payment_type::NULLDATA;
|
||||
}
|
||||
}
|
||||
|
||||
bytes bitcoin_address::determine_raw_address() {
|
||||
bytes result;
|
||||
switch (type) {
|
||||
case payment_type::P2PK: {
|
||||
result = parse_hex(address);
|
||||
break;
|
||||
}
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH: {
|
||||
std::string prefix(address.compare(0, 4, "bcrt") == 0 ? std::string(address.begin(), address.begin() + 4) : std::string(address.begin(), address.begin() + 2));
|
||||
const auto &decode_bech32 = segwit_addr::decode(prefix, address);
|
||||
result = bytes(decode_bech32.second.begin(), decode_bech32.second.end());
|
||||
break;
|
||||
}
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2SH: {
|
||||
bytes hex_addr = fc::from_base58(address);
|
||||
result = bytes(hex_addr.begin() + 1, hex_addr.begin() + 21);
|
||||
break;
|
||||
}
|
||||
case payment_type::NULLDATA:
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bitcoin_address::check_segwit_address(const size_segwit_address &size) const {
|
||||
if (!address.compare(0, 4, "bcrt") || !address.compare(0, 2, "bc") || !address.compare(0, 2, "tb")) {
|
||||
std::string prefix(!address.compare(0, 4, "bcrt") ? std::string(address.begin(), address.begin() + 4) : std::string(address.begin(), address.begin() + 2));
|
||||
|
||||
const auto &decode_bech32 = segwit_addr::decode(prefix, address);
|
||||
|
||||
if (decode_bech32.first == -1 || decode_bech32.second.size() != size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2pk() const {
|
||||
try {
|
||||
bool prefix = !address.compare(0, 2, "02") || !address.compare(0, 2, "03");
|
||||
if (address.size() == 66 && prefix) {
|
||||
parse_hex(address);
|
||||
return true;
|
||||
}
|
||||
} catch (fc::exception e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2wpkh() const {
|
||||
return check_segwit_address(size_segwit_address::P2WPKH);
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2wsh() const {
|
||||
return check_segwit_address(size_segwit_address::P2WSH);
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2pkh() const {
|
||||
try {
|
||||
bytes hex_addr = fc::from_base58(address);
|
||||
if (hex_addr.size() == 25 && (static_cast<unsigned char>(hex_addr[0]) == 0x00 ||
|
||||
static_cast<unsigned char>(hex_addr[0]) == 0x6f)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (fc::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2sh() const {
|
||||
try {
|
||||
bytes hex_addr = fc::from_base58(address);
|
||||
if (hex_addr.size() == 25 && (static_cast<unsigned char>(hex_addr[0]) == 0x05 ||
|
||||
static_cast<unsigned char>(hex_addr[0]) == 0xc4)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (fc::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
btc_multisig_address::btc_multisig_address(const size_t n_required, const accounts_keys &keys) :
|
||||
keys_required(n_required),
|
||||
witnesses_keys(keys) {
|
||||
create_redeem_script();
|
||||
create_address();
|
||||
type = payment_type::P2SH;
|
||||
}
|
||||
|
||||
size_t btc_multisig_address::count_intersection(const accounts_keys &keys) const {
|
||||
FC_ASSERT(keys.size() > 0);
|
||||
|
||||
int intersections_count = 0;
|
||||
for (auto &key : keys) {
|
||||
auto witness_key = witnesses_keys.find(key.first);
|
||||
if (witness_key == witnesses_keys.end())
|
||||
continue;
|
||||
if (key.second == witness_key->second)
|
||||
intersections_count++;
|
||||
}
|
||||
return intersections_count;
|
||||
}
|
||||
|
||||
void btc_multisig_address::create_redeem_script() {
|
||||
FC_ASSERT(keys_required > 0);
|
||||
FC_ASSERT(keys_required < witnesses_keys.size());
|
||||
redeem_script.clear();
|
||||
redeem_script.push_back(op_num[keys_required - 1]);
|
||||
for (const auto &key : witnesses_keys) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << key.second.key_data.size();
|
||||
auto key_size_hex = parse_hex(ss.str());
|
||||
redeem_script.insert(redeem_script.end(), key_size_hex.begin(), key_size_hex.end());
|
||||
redeem_script.insert(redeem_script.end(), key.second.key_data.begin(), key.second.key_data.end());
|
||||
}
|
||||
redeem_script.push_back(op_num[witnesses_keys.size() - 1]);
|
||||
redeem_script.push_back(OP_CHECKMULTISIG);
|
||||
}
|
||||
|
||||
void btc_multisig_address::create_address() {
|
||||
FC_ASSERT(redeem_script.size() > 0);
|
||||
raw_address.clear();
|
||||
fc::sha256 hash256 = fc::sha256::hash(redeem_script.data(), redeem_script.size());
|
||||
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
|
||||
bytes temp_addr_hash(parse_hex(hash160.str()));
|
||||
|
||||
raw_address.push_back(OP_HASH160);
|
||||
std::stringstream ss;
|
||||
ss << std::hex << temp_addr_hash.size();
|
||||
auto address_size_hex = parse_hex(ss.str());
|
||||
raw_address.insert(raw_address.end(), address_size_hex.begin(), address_size_hex.end());
|
||||
raw_address.insert(raw_address.end(), temp_addr_hash.begin(), temp_addr_hash.end());
|
||||
raw_address.push_back(OP_EQUAL);
|
||||
}
|
||||
|
||||
btc_multisig_segwit_address::btc_multisig_segwit_address(const size_t n_required, const accounts_keys &keys) :
|
||||
btc_multisig_address(n_required, keys) {
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2SH;
|
||||
}
|
||||
|
||||
bool btc_multisig_segwit_address::operator==(const btc_multisig_segwit_address &addr) const {
|
||||
if (address != addr.address || redeem_script != addr.redeem_script ||
|
||||
witnesses_keys != addr.witnesses_keys || witness_script != addr.witness_script ||
|
||||
raw_address != addr.raw_address)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<public_key_type> btc_multisig_segwit_address::get_keys() {
|
||||
std::vector<public_key_type> keys;
|
||||
for (const auto &k : witnesses_keys) {
|
||||
keys.push_back(k.second);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
void btc_multisig_segwit_address::create_witness_script() {
|
||||
const auto redeem_sha256 = fc::sha256::hash(redeem_script.data(), redeem_script.size());
|
||||
witness_script.push_back(OP_0);
|
||||
witness_script.push_back(0x20); // PUSH_32
|
||||
witness_script.insert(witness_script.end(), redeem_sha256.data(), redeem_sha256.data() + redeem_sha256.data_size());
|
||||
}
|
||||
|
||||
void btc_multisig_segwit_address::create_segwit_address() {
|
||||
fc::sha256 hash256 = fc::sha256::hash(witness_script.data(), witness_script.size());
|
||||
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
|
||||
|
||||
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
|
||||
address = fc::to_base58(get_address_bytes(raw_address));
|
||||
}
|
||||
|
||||
bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) {
|
||||
bytes address_bytes(1, TESTNET_SCRIPT); // 1 byte version
|
||||
address_bytes.insert(address_bytes.end(), script_hash.begin(), script_hash.end());
|
||||
fc::sha256 hash256 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
|
||||
address_bytes.insert(address_bytes.end(), hash256.data(), hash256.data() + 4); // 4 byte checksum
|
||||
|
||||
return address_bytes;
|
||||
}
|
||||
|
||||
btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network ntype) {
|
||||
network_type = ntype;
|
||||
create_redeem_script(keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2WSH;
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
|
||||
script_builder builder;
|
||||
uint32_t total_weight = 0;
|
||||
builder << uint32_t(0);
|
||||
for (auto &p : keys_data) {
|
||||
total_weight += p.second;
|
||||
builder << op::SWAP;
|
||||
builder << p.first.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << uint32_t(p.second);
|
||||
builder << op::ADD;
|
||||
builder << op::ENDIF;
|
||||
}
|
||||
uint32_t threshold_weight = 2 * total_weight / 3;
|
||||
builder << threshold_weight;
|
||||
builder << op::GREATERTHANOREQUAL;
|
||||
|
||||
redeem_script_ = builder;
|
||||
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_witness_script() {
|
||||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
|
||||
witness_script_ = builder;
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
break;
|
||||
}
|
||||
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
|
||||
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
}
|
||||
|
||||
btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data,
|
||||
const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data,
|
||||
network ntype) {
|
||||
network_type = ntype;
|
||||
create_redeem_script(user_key_data, nrequired, keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2WSH;
|
||||
}
|
||||
void btc_one_or_m_of_n_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data,
|
||||
const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data) {
|
||||
script_builder builder;
|
||||
builder << op::IF;
|
||||
builder << user_key_data.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::ELSE;
|
||||
builder << static_cast<uint32_t>(nrequired);
|
||||
for (auto &key : keys_data) {
|
||||
builder << key.serialize();
|
||||
}
|
||||
builder << static_cast<uint32_t>(keys_data.size());
|
||||
builder << op::CHECKMULTISIG;
|
||||
builder << op::ENDIF;
|
||||
redeem_script_ = builder;
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
void btc_one_or_m_of_n_multisig_address::create_witness_script() {
|
||||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
witness_script_ = builder;
|
||||
}
|
||||
void btc_one_or_m_of_n_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
break;
|
||||
}
|
||||
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
|
||||
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
}
|
||||
|
||||
btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data,
|
||||
const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
bitcoin_address::network ntype) {
|
||||
network_type = ntype;
|
||||
create_redeem_script(user_key_data, keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2WSH;
|
||||
}
|
||||
|
||||
void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
|
||||
script_builder builder;
|
||||
builder << user_key_data.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << op::_1;
|
||||
builder << op::ELSE;
|
||||
uint32_t total_weight = 0;
|
||||
builder << uint32_t(0);
|
||||
for (auto &p : keys_data) {
|
||||
total_weight += p.second;
|
||||
builder << op::SWAP;
|
||||
builder << p.first.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << uint32_t(p.second);
|
||||
builder << op::ADD;
|
||||
builder << op::ENDIF;
|
||||
}
|
||||
uint32_t threshold_weight = 2 * total_weight / 3;
|
||||
builder << threshold_weight;
|
||||
builder << op::GREATERTHANOREQUAL;
|
||||
builder << op::ENDIF;
|
||||
redeem_script_ = builder;
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
|
||||
void btc_one_or_weighted_multisig_address::create_witness_script() {
|
||||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
witness_script_ = builder;
|
||||
}
|
||||
|
||||
void btc_one_or_weighted_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
break;
|
||||
}
|
||||
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
|
||||
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
script_builder &script_builder::operator<<(op opcode) {
|
||||
const auto op_byte = static_cast<uint8_t>(opcode);
|
||||
if (op_byte < 0 || op_byte > 0xff)
|
||||
throw std::runtime_error("script_builder::operator<<(OP): invalid opcode");
|
||||
script.push_back(op_byte);
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(uint32_t number) {
|
||||
if (number == 0) {
|
||||
script.push_back(static_cast<uint8_t>(op::_0));
|
||||
} else if (number <= 16) {
|
||||
script.push_back(static_cast<uint8_t>(op::_1) + number - 1);
|
||||
} else {
|
||||
bytes pack_buf;
|
||||
while (number) {
|
||||
pack_buf.push_back(number & 0xff);
|
||||
number >>= 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. So, the result can
|
||||
// be 5 bytes max.
|
||||
if (pack_buf.back() & 0x80)
|
||||
pack_buf.push_back(0);
|
||||
*this << pack_buf;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(size_t size) {
|
||||
write_compact_size(script, size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const bytes &sc) {
|
||||
write_compact_size(script, sc.size());
|
||||
script.insert(script.end(), sc.begin(), sc.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const fc::sha256 &hash) {
|
||||
write_compact_size(script, hash.data_size());
|
||||
script.insert(script.end(), hash.data(), hash.data() + hash.data_size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const fc::ripemd160 &hash) {
|
||||
write_compact_size(script, hash.data_size());
|
||||
script.insert(script.end(), hash.data(), hash.data() + hash.data_size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const fc::ecc::public_key_data &pubkey_data) {
|
||||
write_compact_size(script, pubkey_data.size());
|
||||
script.insert(script.end(), pubkey_data.begin(), pubkey_data.begin() + pubkey_data.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
bool out_point::operator==(const out_point &op) const {
|
||||
if (this->hash == op.hash &&
|
||||
this->n == op.n) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_in::operator==(const tx_in &ti) const {
|
||||
if (this->prevout == ti.prevout &&
|
||||
this->scriptSig == ti.scriptSig &&
|
||||
this->nSequence == ti.nSequence) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::operator==(const tx_out &to) const {
|
||||
if (this->value == to.value &&
|
||||
this->scriptPubKey == to.scriptPubKey) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2wsh() const {
|
||||
if (scriptPubKey.size() == 34 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x20)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2wpkh() const {
|
||||
if (scriptPubKey.size() == 22 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x14)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2pkh() const {
|
||||
if (scriptPubKey.size() == 25 && scriptPubKey[0] == static_cast<char>(0x76) && scriptPubKey[1] == static_cast<char>(0xa9) &&
|
||||
scriptPubKey[2] == static_cast<char>(0x14) && scriptPubKey[23] == static_cast<char>(0x88) && scriptPubKey[24] == static_cast<char>(0xac)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2sh() const {
|
||||
if (scriptPubKey.size() == 23 && scriptPubKey[0] == static_cast<char>(0xa9) &&
|
||||
scriptPubKey[1] == static_cast<char>(0x14) && scriptPubKey[22] == static_cast<char>(0x87)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2pk() const {
|
||||
if (scriptPubKey.size() == 35 && scriptPubKey[0] == static_cast<char>(0x21) && scriptPubKey[34] == static_cast<char>(0xac)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes tx_out::get_data_or_script() const {
|
||||
if (is_p2pkh()) {
|
||||
return bytes(scriptPubKey.begin() + 3, scriptPubKey.begin() + 23);
|
||||
} else if (is_p2sh() || is_p2wpkh()) {
|
||||
return bytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 22);
|
||||
} else if (is_p2wsh()) {
|
||||
return bytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 34);
|
||||
} else if (is_p2pk()) {
|
||||
return bytes(scriptPubKey.begin() + 1, scriptPubKey.begin() + 34);
|
||||
}
|
||||
return scriptPubKey;
|
||||
}
|
||||
|
||||
bool bitcoin_transaction::operator!=(const bitcoin_transaction &bt) const {
|
||||
if (this->nVersion != bt.nVersion ||
|
||||
this->vin != bt.vin ||
|
||||
this->vout != bt.vout ||
|
||||
this->nLockTime != bt.nLockTime) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fc::sha256 bitcoin_transaction::get_hash() const {
|
||||
const auto bytes = pack(*this, true);
|
||||
const auto hash = fc::sha256::hash(fc::sha256::hash(bytes.data(), bytes.size()));
|
||||
std::reverse(hash.data(), hash.data() + hash.data_size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
fc::sha256 bitcoin_transaction::get_txid() const {
|
||||
const auto bytes = pack(*this, false);
|
||||
const auto hash = fc::sha256::hash(fc::sha256::hash(bytes.data(), bytes.size()));
|
||||
std::reverse(hash.data(), hash.data() + hash.data_size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
size_t bitcoin_transaction::get_vsize() const {
|
||||
static const auto witness_scale_factor = 4;
|
||||
|
||||
fc::datastream<size_t> no_wit_ds;
|
||||
pack(no_wit_ds, *this, false);
|
||||
|
||||
fc::datastream<size_t> wit_ds;
|
||||
pack(wit_ds, *this, true);
|
||||
|
||||
const size_t weight = no_wit_ds.tellp() * (witness_scale_factor - 1) + wit_ds.tellp();
|
||||
const size_t vsize = (weight + witness_scale_factor - 1) / witness_scale_factor;
|
||||
|
||||
return vsize;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::set_version(int32_t version) {
|
||||
tx.nVersion = version;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::set_locktime(uint32_t lock_time) {
|
||||
tx.nLockTime = lock_time;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in(const fc::sha256 &txid, uint32_t n_out, const bytes &script_code, bool front, uint32_t sequence) {
|
||||
add_in(payment_type::P2WSH, txid, n_out, script_code, front, sequence);
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in(payment_type type, const fc::sha256 &txid, uint32_t n_out, const bytes &script_code, bool front, uint32_t sequence) {
|
||||
out_point prevout;
|
||||
prevout.hash = txid;
|
||||
prevout.n = n_out;
|
||||
|
||||
tx_in txin;
|
||||
txin.prevout = prevout;
|
||||
txin.nSequence = sequence;
|
||||
|
||||
add_in(type, txin, script_code, front);
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in(payment_type type, tx_in txin, const bytes &script_code, bool front) {
|
||||
switch (type) {
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
FC_ASSERT(script_code != bytes());
|
||||
txin.scriptSig = script_code;
|
||||
break;
|
||||
default: {
|
||||
if (txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000")) { //coinbase
|
||||
FC_ASSERT(script_code != bytes());
|
||||
txin.scriptSig = script_code;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (front) {
|
||||
tx.vin.insert(tx.vin.begin(), txin);
|
||||
} else {
|
||||
tx.vin.push_back(txin);
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const std::string &base58_address, bool front) {
|
||||
// TODO: add checks
|
||||
const auto address_bytes = fc::from_base58(base58_address);
|
||||
add_out(type, amount, bytes(address_bytes.begin() + 1, address_bytes.begin() + 1 + 20), front);
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const fc::ecc::public_key_data &pubkey, bool front) {
|
||||
FC_ASSERT(is_payment_to_pubkey(type));
|
||||
|
||||
if (type == payment_type::P2PK) {
|
||||
const auto pubkey_bytes = bytes(pubkey.begin(), pubkey.begin() + pubkey.size());
|
||||
add_out(type, amount, pubkey_bytes, front);
|
||||
} else {
|
||||
const auto hash256 = fc::sha256::hash(pubkey.begin(), pubkey.size());
|
||||
const auto hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
|
||||
add_out(type, amount, bytes(hash160.data(), hash160.data() + hash160.data_size()), front);
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const bytes &script_code, bool front) {
|
||||
tx_out out;
|
||||
out.value = amount;
|
||||
out.scriptPubKey = get_script_pubkey(type, script_code);
|
||||
|
||||
if (front) {
|
||||
tx.vout.insert(tx.vout.begin(), out);
|
||||
} else {
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out_all_type(const uint64_t &amount, const bitcoin_address &address, bool front) {
|
||||
switch (address.get_type()) {
|
||||
case payment_type::P2PK: {
|
||||
bytes raw_address(address.get_raw_address());
|
||||
fc::ecc::public_key_data public_key;
|
||||
std::copy(raw_address.begin(), raw_address.end(), public_key.begin());
|
||||
add_out(address.get_type(), amount, public_key, front);
|
||||
break;
|
||||
}
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH: {
|
||||
add_out(address.get_type(), amount, address.get_address(), front);
|
||||
break;
|
||||
}
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH: {
|
||||
add_out(address.get_type(), amount, address.get_raw_address(), front);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool bitcoin_transaction_builder::is_payment_to_pubkey(payment_type type) {
|
||||
switch (type) {
|
||||
case payment_type::P2PK:
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bytes bitcoin_transaction_builder::get_script_pubkey(payment_type type, const bytes &script_code) {
|
||||
switch (type) {
|
||||
case payment_type::NULLDATA:
|
||||
return script_builder() << op::RETURN << script_code;
|
||||
case payment_type::P2PK:
|
||||
return script_builder() << script_code << op::CHECKSIG;
|
||||
case payment_type::P2PKH:
|
||||
return script_builder() << op::DUP << op::HASH160 << script_code << op::EQUALVERIFY << op::CHECKSIG;
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
return script_builder() << op::HASH160 << script_code << op::EQUAL;
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH:
|
||||
return script_builder() << op::_0 << script_code;
|
||||
default:
|
||||
return script_code;
|
||||
}
|
||||
}
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/* Copyright (c) 2017 Pieter Wuille
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bech32.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template <int frombits, int tobits, bool pad>
|
||||
bool convertbits(data &out, const data &in) {
|
||||
int acc = 0;
|
||||
int bits = 0;
|
||||
const int maxv = (1 << tobits) - 1;
|
||||
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
for (size_t i = 0; i < in.size(); ++i) {
|
||||
int value = in[i];
|
||||
acc = ((acc << frombits) | value) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
out.push_back((acc >> bits) & maxv);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits)
|
||||
out.push_back((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace segwit_addr {
|
||||
|
||||
/** Decode a SegWit address. */
|
||||
std::pair<int, data> decode(const std::string &hrp, const std::string &addr) {
|
||||
std::pair<std::string, data> dec = bech32::Decode(addr);
|
||||
if (dec.first != hrp || dec.second.size() < 1)
|
||||
return std::make_pair(-1, data());
|
||||
data conv;
|
||||
if (!convertbits<5, 8, false>(conv, data(dec.second.begin() + 1, dec.second.end())) ||
|
||||
conv.size() < 2 || conv.size() > 40 || dec.second[0] > 16 || (dec.second[0] == 0 && conv.size() != 20 && conv.size() != 32)) {
|
||||
return std::make_pair(-1, data());
|
||||
}
|
||||
return std::make_pair(dec.second[0], conv);
|
||||
}
|
||||
|
||||
/** Encode a SegWit address. */
|
||||
std::string encode(const std::string &hrp, int witver, const data &witprog) {
|
||||
data enc;
|
||||
enc.push_back(witver);
|
||||
convertbits<8, 5, true>(enc, witprog);
|
||||
std::string ret = bech32::Encode(hrp, enc);
|
||||
if (decode(hrp, ret).first == -1)
|
||||
return "";
|
||||
return ret;
|
||||
}
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::segwit_addr
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
const secp256k1_context_t *btc_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptCode, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness) {
|
||||
fc::datastream<size_t> ps;
|
||||
if (is_witness)
|
||||
pack_tx_witness_signature(ps, scriptCode, tx, in_index, amount, hash_type);
|
||||
else
|
||||
pack_tx_signature(ps, scriptCode, tx, in_index, hash_type);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (!vec.empty()) {
|
||||
fc::datastream<char *> ds(vec.data(), vec.size());
|
||||
if (is_witness)
|
||||
pack_tx_witness_signature(ds, scriptCode, tx, in_index, amount, hash_type);
|
||||
else
|
||||
pack_tx_signature(ds, scriptCode, tx, in_index, hash_type);
|
||||
}
|
||||
|
||||
return fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign) {
|
||||
bytes sig;
|
||||
sig.resize(72);
|
||||
int sig_len = sig.size();
|
||||
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(
|
||||
context_sign,
|
||||
reinterpret_cast<unsigned char *>(hash.data()),
|
||||
reinterpret_cast<unsigned char *>(sig.data()),
|
||||
&sig_len,
|
||||
reinterpret_cast<const unsigned char *>(privkey.data()),
|
||||
secp256k1_nonce_function_rfc6979,
|
||||
nullptr)); // TODO: replace assert with exception
|
||||
|
||||
sig.resize(sig_len);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const bytes &privkey,
|
||||
const secp256k1_context_t *context_sign, int hash_type) {
|
||||
FC_ASSERT(tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size());
|
||||
FC_ASSERT(!privkey.empty());
|
||||
|
||||
std::vector<bytes> signatures;
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
const auto sighash = get_signature_hash(tx, redeem_scripts[i], static_cast<int64_t>(amounts[i]), i, hash_type, true);
|
||||
auto sig = privkey_sign(privkey, sighash, context_sign);
|
||||
sig.push_back(static_cast<uint8_t>(hash_type));
|
||||
|
||||
signatures.push_back(sig);
|
||||
}
|
||||
return signatures;
|
||||
}
|
||||
|
||||
void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts, bool use_mulisig_workaround) {
|
||||
FC_ASSERT(tx.vin.size() == redeem_scripts.size());
|
||||
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
if (use_mulisig_workaround)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), bytes()); // Bitcoin workaround CHECKMULTISIG bug
|
||||
tx.vin[i].scriptWitness.push_back(redeem_scripts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context) {
|
||||
std::vector<unsigned char> sig_temp(sig.begin(), sig.end());
|
||||
std::vector<unsigned char> pubkey_temp(pubkey.begin(), pubkey.end());
|
||||
std::vector<unsigned char> msg_temp(msg.begin(), msg.end());
|
||||
|
||||
int result = secp256k1_ecdsa_verify(context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size());
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context_t *context) {
|
||||
FC_ASSERT(redeem_scripts.size() == amounts.size());
|
||||
|
||||
using data = std::pair<size_t, bytes>;
|
||||
struct comp {
|
||||
bool operator()(const data &lhs, const data &rhs) const {
|
||||
return lhs.first < rhs.first;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::vector<bytes>> new_stacks;
|
||||
|
||||
for (size_t i = 0; i < redeem_scripts.size(); i++) {
|
||||
const std::vector<bytes> &keys = get_pubkey_from_redeemScript(redeem_scripts[i]);
|
||||
const auto &sighash = get_signature_hash(tx, redeem_scripts[i], static_cast<int64_t>(amounts[i]), i, 1, true).str();
|
||||
bytes sighash_temp(parse_hex(sighash));
|
||||
|
||||
std::vector<bytes> stack(tx.vin[i].scriptWitness);
|
||||
std::vector<bool> marker(tx.vin[i].scriptWitness.size(), false);
|
||||
std::set<data, comp> sigs;
|
||||
|
||||
for (size_t j = 0; j < keys.size(); j++) {
|
||||
for (size_t l = 0; l < stack.size(); l++) {
|
||||
if (!verify_sig(stack[l], keys[j], sighash_temp, context) || marker[l])
|
||||
continue;
|
||||
sigs.insert(std::make_pair(j, stack[l]));
|
||||
marker[l] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bytes> temp_sig;
|
||||
for (auto s : sigs) {
|
||||
temp_sig.push_back(s.second);
|
||||
}
|
||||
new_stacks.push_back(temp_sig);
|
||||
}
|
||||
return new_stacks;
|
||||
}
|
||||
|
||||
void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set) {
|
||||
for (unsigned int i = 0; i < signature_set.size(); i++) {
|
||||
std::vector<bytes> signatures = signature_set[i];
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.push_back(signatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void add_signatures_to_transaction_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set) {
|
||||
for (unsigned int i = 0; i < signature_set.size(); i++) {
|
||||
std::vector<bytes> signatures = signature_set[i];
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void add_signatures_to_transaction_user_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set) {
|
||||
add_signatures_to_transaction_weighted_multisig(tx, signature_set);
|
||||
for (size_t itr = 0; itr < tx.vin.size(); itr++) {
|
||||
tx.vin[0].scriptWitness.push_back(bytes());
|
||||
}
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
99
libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp
Normal file
99
libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/utils.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
fc::ecc::public_key_data create_public_key_data(const std::vector<char> &public_key) {
|
||||
FC_ASSERT(public_key.size() == 33);
|
||||
fc::ecc::public_key_data key;
|
||||
for (size_t i = 0; i < 33; i++) {
|
||||
key.at(i) = public_key[i];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bytes get_privkey_bytes(const std::string &privkey_base58) {
|
||||
const auto privkey = fc::from_base58(privkey_base58);
|
||||
// Remove version and hash
|
||||
return bytes(privkey.cbegin() + 1, privkey.cbegin() + 1 + 32);
|
||||
}
|
||||
|
||||
bytes parse_hex(const std::string &str) {
|
||||
bytes vec(str.size() / 2);
|
||||
fc::from_hex(str, vec.data(), vec.size());
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<bytes> get_pubkey_from_redeemScript(bytes script) {
|
||||
FC_ASSERT(script.size() >= 37);
|
||||
|
||||
script.erase(script.begin());
|
||||
script.erase(script.end() - 2, script.end());
|
||||
|
||||
std::vector<bytes> result;
|
||||
uint64_t count = script.size() / 34;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
result.push_back(bytes(script.begin() + (34 * i) + 1, script.begin() + (34 * (i + 1))));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes public_key_data_to_bytes(const fc::ecc::public_key_data &key) {
|
||||
bytes result;
|
||||
result.resize(key.size());
|
||||
std::copy(key.begin(), key.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<bytes> 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_transaction_signatures(const std::vector<bytes> &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;
|
||||
}
|
||||
|
||||
void read_transaction_data(const std::string &string_buf, std::string &tx_hex, std::vector<uint64_t> &in_amounts, std::string &redeem_script) {
|
||||
std::stringstream ss(string_buf);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
tx_hex = json.get<std::string>("tx_hex");
|
||||
in_amounts.clear();
|
||||
for (auto &v : json.get_child("in_amounts"))
|
||||
in_amounts.push_back(fc::to_uint64(v.second.data()));
|
||||
redeem_script = json.get<std::string>("redeem_script");
|
||||
}
|
||||
|
||||
std::string write_transaction_data(const std::string &tx, const std::vector<uint64_t> &in_amounts, const std::string &redeem_script) {
|
||||
std::string res = "{\"tx_hex\":\"" + tx + "\",\"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 += "],\"redeem_script\":\"" + redeem_script + "\"}";
|
||||
return res;
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -1,680 +0,0 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <fc/crypto/elliptic.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>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
static const unsigned char OP_0 = 0x00;
|
||||
static const unsigned char OP_1 = 0x51;
|
||||
static const unsigned char OP_2 = 0x52;
|
||||
static const unsigned char OP_3 = 0x53;
|
||||
static const unsigned char OP_4 = 0x54;
|
||||
static const unsigned char OP_5 = 0x55;
|
||||
static const unsigned char OP_6 = 0x56;
|
||||
static const unsigned char OP_7 = 0x57;
|
||||
static const unsigned char OP_8 = 0x58;
|
||||
static const unsigned char OP_9 = 0x59;
|
||||
static const unsigned char OP_10 = 0x5a;
|
||||
static const unsigned char OP_11 = 0x5b;
|
||||
static const unsigned char OP_12 = 0x5c;
|
||||
static const unsigned char OP_13 = 0x5d;
|
||||
static const unsigned char OP_14 = 0x5e;
|
||||
static const unsigned char OP_15 = 0x5f;
|
||||
static const unsigned char OP_16 = 0x60;
|
||||
|
||||
static const unsigned char OP_IF = 0x63;
|
||||
static const unsigned char OP_ENDIF = 0x68;
|
||||
static const unsigned char OP_SWAP = 0x7c;
|
||||
static const unsigned char OP_EQUAL = 0x87;
|
||||
static const unsigned char OP_ADD = 0x93;
|
||||
static const unsigned char OP_GREATERTHAN = 0xa0;
|
||||
static const unsigned char OP_HASH160 = 0xa9;
|
||||
static const unsigned char OP_CHECKSIG = 0xac;
|
||||
|
||||
class WriteBytesStream {
|
||||
public:
|
||||
WriteBytesStream(bytes &buffer) :
|
||||
storage_(buffer) {
|
||||
}
|
||||
|
||||
void write(const unsigned char *d, size_t s) {
|
||||
storage_.insert(storage_.end(), d, d + s);
|
||||
}
|
||||
|
||||
bool put(unsigned char c) {
|
||||
storage_.push_back(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
void writedata8(uint8_t obj) {
|
||||
write((unsigned char *)&obj, 1);
|
||||
}
|
||||
|
||||
void writedata16(uint16_t obj) {
|
||||
obj = htole16(obj);
|
||||
write((unsigned char *)&obj, 2);
|
||||
}
|
||||
|
||||
void writedata32(uint32_t obj) {
|
||||
obj = htole32(obj);
|
||||
write((unsigned char *)&obj, 4);
|
||||
}
|
||||
|
||||
void writedata64(uint64_t obj) {
|
||||
obj = htole64(obj);
|
||||
write((unsigned char *)&obj, 8);
|
||||
}
|
||||
|
||||
void write_compact_int(uint64_t val) {
|
||||
if (val < 253) {
|
||||
writedata8(val);
|
||||
} else if (val <= std::numeric_limits<unsigned short>::max()) {
|
||||
writedata8(253);
|
||||
writedata16(val);
|
||||
} else if (val <= std::numeric_limits<unsigned int>::max()) {
|
||||
writedata8(254);
|
||||
writedata32(val);
|
||||
} else {
|
||||
writedata8(255);
|
||||
writedata64(val);
|
||||
}
|
||||
}
|
||||
|
||||
void writedata(const bytes &data) {
|
||||
write_compact_int(data.size());
|
||||
write(&data[0], data.size());
|
||||
}
|
||||
|
||||
private:
|
||||
bytes &storage_;
|
||||
};
|
||||
|
||||
class ReadBytesStream {
|
||||
public:
|
||||
ReadBytesStream(const bytes &buffer, size_t pos = 0) :
|
||||
storage_(buffer),
|
||||
pos_(pos),
|
||||
end_(buffer.size()) {
|
||||
}
|
||||
|
||||
size_t current_pos() const {
|
||||
return pos_;
|
||||
}
|
||||
void set_pos(size_t pos) {
|
||||
if (pos > end_)
|
||||
FC_THROW("Invalid position in BTC tx buffer");
|
||||
pos_ = pos;
|
||||
}
|
||||
|
||||
inline bool read(unsigned char *d, size_t s) {
|
||||
if (end_ - pos_ >= s) {
|
||||
memcpy(d, &storage_[pos_], s);
|
||||
pos_ += s;
|
||||
return true;
|
||||
}
|
||||
FC_THROW("invalid bitcoin tx buffer");
|
||||
}
|
||||
|
||||
inline bool get(unsigned char &c) {
|
||||
if (pos_ < end_) {
|
||||
c = storage_[pos_++];
|
||||
return true;
|
||||
}
|
||||
FC_THROW("invalid bitcoin tx buffer");
|
||||
}
|
||||
|
||||
uint8_t readdata8() {
|
||||
uint8_t obj;
|
||||
read((unsigned char *)&obj, 1);
|
||||
return obj;
|
||||
}
|
||||
uint16_t readdata16() {
|
||||
uint16_t obj;
|
||||
read((unsigned char *)&obj, 2);
|
||||
return le16toh(obj);
|
||||
}
|
||||
uint32_t readdata32() {
|
||||
uint32_t obj;
|
||||
read((unsigned char *)&obj, 4);
|
||||
return le32toh(obj);
|
||||
}
|
||||
uint64_t readdata64() {
|
||||
uint64_t obj;
|
||||
read((unsigned char *)&obj, 8);
|
||||
return le64toh(obj);
|
||||
}
|
||||
|
||||
uint64_t read_compact_int() {
|
||||
uint8_t size = readdata8();
|
||||
uint64_t ret = 0;
|
||||
if (size < 253) {
|
||||
ret = size;
|
||||
} else if (size == 253) {
|
||||
ret = readdata16();
|
||||
if (ret < 253)
|
||||
FC_THROW("non-canonical ReadCompactSize()");
|
||||
} else if (size == 254) {
|
||||
ret = readdata32();
|
||||
if (ret < 0x10000u)
|
||||
FC_THROW("non-canonical ReadCompactSize()");
|
||||
} else {
|
||||
ret = readdata64();
|
||||
if (ret < 0x100000000ULL)
|
||||
FC_THROW("non-canonical ReadCompactSize()");
|
||||
}
|
||||
if (ret > (uint64_t)0x02000000)
|
||||
FC_THROW("ReadCompactSize(): size too large");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void readdata(bytes &data) {
|
||||
size_t s = read_compact_int();
|
||||
data.clear();
|
||||
data.resize(s);
|
||||
read(&data[0], s);
|
||||
}
|
||||
|
||||
private:
|
||||
const bytes &storage_;
|
||||
size_t pos_;
|
||||
size_t end_;
|
||||
};
|
||||
|
||||
void btc_outpoint::to_bytes(bytes &stream) const {
|
||||
WriteBytesStream str(stream);
|
||||
// TODO: write size?
|
||||
str.write((unsigned char *)hash.data(), hash.data_size());
|
||||
str.writedata32(n);
|
||||
}
|
||||
|
||||
size_t btc_outpoint::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
ReadBytesStream str(data, pos);
|
||||
// TODO: read size?
|
||||
str.read((unsigned char *)hash.data(), hash.data_size());
|
||||
n = str.readdata32();
|
||||
return str.current_pos();
|
||||
}
|
||||
|
||||
void btc_in::to_bytes(bytes &stream) const {
|
||||
prevout.to_bytes(stream);
|
||||
WriteBytesStream str(stream);
|
||||
str.writedata(scriptSig);
|
||||
str.writedata32(nSequence);
|
||||
}
|
||||
|
||||
size_t btc_in::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
pos = prevout.fill_from_bytes(data, pos);
|
||||
ReadBytesStream str(data, pos);
|
||||
str.readdata(scriptSig);
|
||||
nSequence = str.readdata32();
|
||||
return str.current_pos();
|
||||
}
|
||||
|
||||
void btc_out::to_bytes(bytes &stream) const {
|
||||
WriteBytesStream str(stream);
|
||||
str.writedata64(nValue);
|
||||
str.writedata(scriptPubKey);
|
||||
}
|
||||
|
||||
size_t btc_out::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
ReadBytesStream str(data, pos);
|
||||
nValue = str.readdata64();
|
||||
str.readdata(scriptPubKey);
|
||||
return str.current_pos();
|
||||
}
|
||||
|
||||
void btc_tx::to_bytes(bytes &stream) const {
|
||||
WriteBytesStream str(stream);
|
||||
str.writedata32(nVersion);
|
||||
if (hasWitness) {
|
||||
// write dummy inputs and flag
|
||||
str.write_compact_int(0);
|
||||
unsigned char flags = 1;
|
||||
str.put(flags);
|
||||
}
|
||||
str.write_compact_int(vin.size());
|
||||
for (const auto &in : vin)
|
||||
in.to_bytes(stream);
|
||||
str.write_compact_int(vout.size());
|
||||
for (const auto &out : vout)
|
||||
out.to_bytes(stream);
|
||||
if (hasWitness) {
|
||||
for (const auto &in : vin) {
|
||||
str.write_compact_int(in.scriptWitness.size());
|
||||
for (const auto &stack_item : in.scriptWitness)
|
||||
str.writedata(stack_item);
|
||||
}
|
||||
}
|
||||
str.writedata32(nLockTime);
|
||||
}
|
||||
|
||||
size_t btc_tx::fill_from_bytes(const bytes &data, size_t pos) {
|
||||
ReadBytesStream ds(data, pos);
|
||||
nVersion = ds.readdata32();
|
||||
unsigned char flags = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
hasWitness = false;
|
||||
/* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */
|
||||
size_t vin_size = ds.read_compact_int();
|
||||
vin.resize(vin_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &in : vin)
|
||||
pos = in.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
if (vin_size == 0) {
|
||||
/* We read a dummy or an empty vin. */
|
||||
ds.get(flags);
|
||||
if (flags != 0) {
|
||||
size_t vin_size = ds.read_compact_int();
|
||||
vin.resize(vin_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &in : vin)
|
||||
pos = in.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
size_t vout_size = ds.read_compact_int();
|
||||
vout.resize(vout_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &out : vout)
|
||||
pos = out.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
hasWitness = true;
|
||||
}
|
||||
} else {
|
||||
/* We read a non-empty vin. Assume a normal vout follows. */
|
||||
size_t vout_size = ds.read_compact_int();
|
||||
vout.resize(vout_size);
|
||||
pos = ds.current_pos();
|
||||
for (auto &out : vout)
|
||||
pos = out.fill_from_bytes(data, pos);
|
||||
ds.set_pos(pos);
|
||||
}
|
||||
if (hasWitness) {
|
||||
/* The witness flag is present, and we support witnesses. */
|
||||
for (auto &in : vin) {
|
||||
unsigned int size = ds.read_compact_int();
|
||||
in.scriptWitness.resize(size);
|
||||
for (auto &stack_item : in.scriptWitness)
|
||||
ds.readdata(stack_item);
|
||||
}
|
||||
}
|
||||
nLockTime = ds.readdata32();
|
||||
return ds.current_pos();
|
||||
}
|
||||
|
||||
void add_data_to_script(bytes &script, const bytes &data) {
|
||||
WriteBytesStream str(script);
|
||||
str.writedata(data);
|
||||
}
|
||||
|
||||
void add_number_to_script(bytes &script, unsigned char data) {
|
||||
WriteBytesStream str(script);
|
||||
if (data == 0)
|
||||
str.put(OP_0);
|
||||
else if (data == 1)
|
||||
str.put(OP_1);
|
||||
else if (data == 2)
|
||||
str.put(OP_2);
|
||||
else if (data == 3)
|
||||
str.put(OP_3);
|
||||
else if (data == 4)
|
||||
str.put(OP_4);
|
||||
else if (data == 5)
|
||||
str.put(OP_5);
|
||||
else if (data == 6)
|
||||
str.put(OP_6);
|
||||
else if (data == 7)
|
||||
str.put(OP_7);
|
||||
else if (data == 8)
|
||||
str.put(OP_8);
|
||||
else if (data == 9)
|
||||
str.put(OP_9);
|
||||
else if (data == 10)
|
||||
str.put(OP_10);
|
||||
else if (data == 11)
|
||||
str.put(OP_11);
|
||||
else if (data == 12)
|
||||
str.put(OP_12);
|
||||
else if (data == 13)
|
||||
str.put(OP_13);
|
||||
else if (data == 14)
|
||||
str.put(OP_14);
|
||||
else if (data == 15)
|
||||
str.put(OP_15);
|
||||
else if (data == 16)
|
||||
str.put(OP_16);
|
||||
else
|
||||
add_data_to_script(script, {data});
|
||||
}
|
||||
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data) {
|
||||
int total_weight = 0;
|
||||
bytes result;
|
||||
add_number_to_script(result, 0);
|
||||
for (auto &p : key_data) {
|
||||
total_weight += p.second;
|
||||
result.push_back(OP_SWAP);
|
||||
auto raw_data = p.first.serialize();
|
||||
add_data_to_script(result, bytes(raw_data.begin(), raw_data.begin() + raw_data.size()));
|
||||
result.push_back(OP_CHECKSIG);
|
||||
result.push_back(OP_IF);
|
||||
add_number_to_script(result, static_cast<unsigned char>(p.second));
|
||||
result.push_back(OP_ADD);
|
||||
result.push_back(OP_ENDIF);
|
||||
}
|
||||
int threshold_weight = 2 * total_weight / 3;
|
||||
add_number_to_script(result, static_cast<unsigned char>(threshold_weight));
|
||||
result.push_back(OP_GREATERTHAN);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
bytes cat(bytes x, const bytes &y) {
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
bytes expand_hrp(const std::string &hrp) {
|
||||
bytes ret;
|
||||
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;
|
||||
}
|
||||
|
||||
/** Find the polynomial with value coefficients mod the generator as 30-bit. */
|
||||
uint32_t polymod(const bytes &values) {
|
||||
uint32_t chk = 1;
|
||||
for (size_t i = 0; i < values.size(); ++i) {
|
||||
uint8_t top = chk >> 25;
|
||||
chk = (chk & 0x1ffffff) << 5 ^ values[i] ^
|
||||
(-((top >> 0) & 1) & 0x3b6a57b2UL) ^
|
||||
(-((top >> 1) & 1) & 0x26508e6dUL) ^
|
||||
(-((top >> 2) & 1) & 0x1ea119faUL) ^
|
||||
(-((top >> 3) & 1) & 0x3d4233ddUL) ^
|
||||
(-((top >> 4) & 1) & 0x2a1462b3UL);
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||
bytes enc = cat(expand_hrp(hrp), values);
|
||||
enc.resize(enc.size() + 6);
|
||||
uint32_t mod = polymod(enc) ^ 1;
|
||||
bytes ret;
|
||||
ret.resize(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
ret[i] = (mod >> (5 * (5 - i))) & 31;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string bech32(const std::string &hrp, const bytes &values) {
|
||||
bytes checksum = bech32_checksum(hrp, values);
|
||||
bytes combined = cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (size_t i = 0; i < combined.size(); ++i) {
|
||||
ret += charset[combined[i]];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template <int frombits, int tobits, bool pad>
|
||||
bool convertbits(bytes &out, const bytes &in) {
|
||||
int acc = 0;
|
||||
int bits = 0;
|
||||
const int maxv = (1 << tobits) - 1;
|
||||
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
for (size_t i = 0; i < in.size(); ++i) {
|
||||
int value = in[i];
|
||||
acc = ((acc << frombits) | value) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
out.push_back((acc >> bits) & maxv);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits)
|
||||
out.push_back((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Encode a SegWit address. */
|
||||
std::string segwit_addr_encode(const std::string &hrp, uint8_t witver, const bytes &witprog) {
|
||||
bytes enc;
|
||||
enc.push_back(witver);
|
||||
convertbits<8, 5, true>(enc, witprog);
|
||||
std::string ret = bech32(hrp, enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) {
|
||||
// calc script hash
|
||||
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||
bytes wp(sh.data(), sh.data() + sh.data_size());
|
||||
switch (network) {
|
||||
case (mainnet):
|
||||
return segwit_addr_encode("bc", 0, wp);
|
||||
case (testnet):
|
||||
case (regtest):
|
||||
return segwit_addr_encode("tb", 0, wp);
|
||||
default:
|
||||
FC_THROW("Unknown bitcoin network type");
|
||||
}
|
||||
FC_THROW("Unknown bitcoin network type");
|
||||
}
|
||||
|
||||
bytes lock_script_for_redeem_script(const bytes &script) {
|
||||
bytes result;
|
||||
result.push_back(OP_0);
|
||||
fc::sha256 h = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||
bytes shash(h.data(), h.data() + h.data_size());
|
||||
add_data_to_script(result, shash);
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes hash_prevouts(const btc_tx &unsigned_tx) {
|
||||
fc::sha256::encoder hasher;
|
||||
for (const auto &in : unsigned_tx.vin) {
|
||||
bytes data;
|
||||
in.prevout.to_bytes(data);
|
||||
hasher.write(reinterpret_cast<const char *>(&data[0]), data.size());
|
||||
}
|
||||
fc::sha256 res = fc::sha256::hash(hasher.result());
|
||||
return bytes(res.data(), res.data() + res.data_size());
|
||||
}
|
||||
|
||||
bytes hash_sequence(const btc_tx &unsigned_tx) {
|
||||
fc::sha256::encoder hasher;
|
||||
for (const auto &in : unsigned_tx.vin) {
|
||||
hasher.write(reinterpret_cast<const char *>(&in.nSequence), sizeof(in.nSequence));
|
||||
}
|
||||
fc::sha256 res = fc::sha256::hash(hasher.result());
|
||||
return bytes(res.data(), res.data() + res.data_size());
|
||||
}
|
||||
|
||||
bytes hash_outputs(const btc_tx &unsigned_tx) {
|
||||
fc::sha256::encoder hasher;
|
||||
for (const auto &out : unsigned_tx.vout) {
|
||||
bytes data;
|
||||
out.to_bytes(data);
|
||||
hasher.write(reinterpret_cast<const char *>(&data[0]), data.size());
|
||||
}
|
||||
fc::sha256 res = fc::sha256::hash(hasher.result());
|
||||
return bytes(res.data(), res.data() + res.data_size());
|
||||
}
|
||||
|
||||
const secp256k1_context_t *btc_get_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
bytes der_sign(const fc::ecc::private_key &priv_key, const fc::sha256 &digest) {
|
||||
fc::ecc::signature result;
|
||||
int size = result.size();
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(btc_get_context(),
|
||||
(unsigned char *)digest.data(),
|
||||
(unsigned char *)result.begin(),
|
||||
&size,
|
||||
(unsigned char *)priv_key.get_secret().data(),
|
||||
secp256k1_nonce_function_rfc6979,
|
||||
nullptr));
|
||||
return bytes(result.begin(), result.begin() + size);
|
||||
}
|
||||
|
||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const bytes &redeem_script,
|
||||
const fc::ecc::private_key &priv_key) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
|
||||
FC_ASSERT(tx.vin.size() == in_amounts.size(), "Incorrect input amounts data");
|
||||
|
||||
std::vector<bytes> results;
|
||||
auto cur_amount = in_amounts.begin();
|
||||
// pre-calc reused values
|
||||
bytes hashPrevouts = hash_prevouts(tx);
|
||||
bytes hashSequence = hash_sequence(tx);
|
||||
bytes hashOutputs = hash_outputs(tx);
|
||||
// calc digest for every input according to BIP143
|
||||
// implement SIGHASH_ALL scheme
|
||||
for (const auto &in : tx.vin) {
|
||||
fc::sha256::encoder hasher;
|
||||
hasher.write(reinterpret_cast<const char *>(&tx.nVersion), sizeof(tx.nVersion));
|
||||
hasher.write(reinterpret_cast<const char *>(&hashPrevouts[0]), hashPrevouts.size());
|
||||
hasher.write(reinterpret_cast<const char *>(&hashSequence[0]), hashSequence.size());
|
||||
bytes data;
|
||||
in.prevout.to_bytes(data);
|
||||
hasher.write(reinterpret_cast<const char *>(&data[0]), data.size());
|
||||
bytes serializedScript;
|
||||
WriteBytesStream stream(serializedScript);
|
||||
stream.writedata(redeem_script);
|
||||
hasher.write(reinterpret_cast<const char *>(&serializedScript[0]), serializedScript.size());
|
||||
uint64_t amount = *cur_amount++;
|
||||
hasher.write(reinterpret_cast<const char *>(&amount), sizeof(amount));
|
||||
hasher.write(reinterpret_cast<const char *>(&in.nSequence), sizeof(in.nSequence));
|
||||
hasher.write(reinterpret_cast<const char *>(&hashOutputs[0]), hashOutputs.size());
|
||||
hasher.write(reinterpret_cast<const char *>(&tx.nLockTime), sizeof(tx.nLockTime));
|
||||
// add sigtype SIGHASH_ALL
|
||||
uint32_t sigtype = 1;
|
||||
hasher.write(reinterpret_cast<const char *>(&sigtype), sizeof(sigtype));
|
||||
|
||||
fc::sha256 digest = fc::sha256::hash(hasher.result());
|
||||
//std::vector<char> res = priv_key.sign(digest);
|
||||
//bytes s_data(res.begin(), res.end());
|
||||
bytes s_data = der_sign(priv_key, digest);
|
||||
s_data.push_back(1);
|
||||
results.push_back(s_data);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
bytes sign_pw_transfer_transaction(const bytes &unsigned_tx, std::vector<uint64_t> in_amounts, const bytes &redeem_script, const std::vector<fc::optional<fc::ecc::private_key>> &priv_keys) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
bytes dummy_data;
|
||||
for (auto key : priv_keys) {
|
||||
if (key) {
|
||||
std::vector<bytes> signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_script, *key);
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]);
|
||||
} else {
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.push_back(dummy_data);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &in : tx.vin) {
|
||||
in.scriptWitness.push_back(redeem_script);
|
||||
}
|
||||
|
||||
tx.hasWitness = true;
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes add_dummy_signatures_for_pw_transfer(const bytes &unsigned_tx,
|
||||
const bytes &redeem_script,
|
||||
unsigned int key_count) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
|
||||
bytes dummy_data;
|
||||
for (auto &in : tx.vin) {
|
||||
for (unsigned i = 0; i < key_count; i++)
|
||||
in.scriptWitness.push_back(dummy_data);
|
||||
in.scriptWitness.push_back(redeem_script);
|
||||
}
|
||||
|
||||
tx.hasWitness = true;
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes partially_sign_pw_transfer_transaction(const bytes &partially_signed_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const fc::ecc::private_key &priv_key,
|
||||
unsigned int key_idx) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(partially_signed_tx);
|
||||
FC_ASSERT(tx.vin.size() > 0);
|
||||
bytes redeem_script = tx.vin[0].scriptWitness.back();
|
||||
std::vector<bytes> signatures = signatures_for_raw_transaction(partially_signed_tx, in_amounts, redeem_script, priv_key);
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
unsigned witness_idx = tx.vin[0].scriptWitness.size() - 2 - key_idx;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness[witness_idx] = signatures[i];
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector<std::vector<bytes>> &signature_set, const bytes &redeem_script) {
|
||||
btc_tx tx;
|
||||
tx.fill_from_bytes(unsigned_tx);
|
||||
bytes dummy_data;
|
||||
for (unsigned int i = 0; i < signature_set.size(); i++) {
|
||||
std::vector<bytes> signatures = signature_set[i];
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]);
|
||||
}
|
||||
|
||||
for (auto &in : tx.vin) {
|
||||
in.scriptWitness.push_back(redeem_script);
|
||||
}
|
||||
|
||||
tx.hasWitness = true;
|
||||
bytes ret;
|
||||
tx.to_bytes(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Bech32 is a string encoding format used in newer address types.
|
||||
// The output consists of a human-readable part (alphanumeric), a
|
||||
// separator character (1), and a base32 data section, the last
|
||||
// 6 characters of which are a checksum.
|
||||
//
|
||||
// For more information, see BIP 173.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace bech32 {
|
||||
|
||||
/** Encode a Bech32 string. Returns the empty string in case of failure. */
|
||||
std::string Encode(const std::string &hrp, const std::vector<uint8_t> &values);
|
||||
|
||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string &str);
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::bech32
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/utils.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15
|
||||
|
||||
class bitcoin_address {
|
||||
|
||||
public:
|
||||
enum network {
|
||||
mainnet,
|
||||
testnet,
|
||||
regtest
|
||||
};
|
||||
|
||||
bitcoin_address() = default;
|
||||
|
||||
bitcoin_address(const std::string &addr, network ntype = network::regtest) :
|
||||
address(addr),
|
||||
type(determine_type()),
|
||||
raw_address(determine_raw_address()),
|
||||
network_type(ntype) {
|
||||
}
|
||||
|
||||
bool operator==(const bitcoin_address &btc_addr) const;
|
||||
|
||||
payment_type get_type() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string get_address() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
bytes get_raw_address() const {
|
||||
return raw_address;
|
||||
}
|
||||
|
||||
bytes get_script() const;
|
||||
|
||||
network get_network_type() const {
|
||||
return network_type;
|
||||
}
|
||||
|
||||
private:
|
||||
enum size_segwit_address { P2WSH = 32,
|
||||
P2WPKH = 20 };
|
||||
|
||||
payment_type determine_type();
|
||||
|
||||
bytes determine_raw_address();
|
||||
|
||||
bool check_segwit_address(const size_segwit_address &size) const;
|
||||
|
||||
bool is_p2pk() const;
|
||||
|
||||
bool is_p2wpkh() const;
|
||||
|
||||
bool is_p2wsh() const;
|
||||
|
||||
bool is_p2pkh() const;
|
||||
|
||||
bool is_p2sh() const;
|
||||
|
||||
public:
|
||||
std::string address;
|
||||
|
||||
payment_type type;
|
||||
|
||||
bytes raw_address;
|
||||
|
||||
network network_type;
|
||||
};
|
||||
|
||||
class btc_multisig_address : public bitcoin_address {
|
||||
|
||||
public:
|
||||
btc_multisig_address() = default;
|
||||
|
||||
btc_multisig_address(const size_t n_required, const accounts_keys &keys);
|
||||
|
||||
size_t count_intersection(const accounts_keys &keys) const;
|
||||
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script();
|
||||
|
||||
void create_address();
|
||||
|
||||
public:
|
||||
enum address_types { MAINNET_SCRIPT = 5,
|
||||
TESTNET_SCRIPT = 196 };
|
||||
|
||||
enum { OP_0 = 0x00,
|
||||
OP_EQUAL = 0x87,
|
||||
OP_HASH160 = 0xa9,
|
||||
OP_CHECKMULTISIG = 0xae };
|
||||
|
||||
bytes redeem_script;
|
||||
|
||||
size_t keys_required = 0;
|
||||
|
||||
accounts_keys witnesses_keys;
|
||||
};
|
||||
|
||||
// multisig segwit address (P2WSH)
|
||||
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||
class btc_multisig_segwit_address : public btc_multisig_address {
|
||||
|
||||
public:
|
||||
btc_multisig_segwit_address() = default;
|
||||
|
||||
btc_multisig_segwit_address(const size_t n_required, const accounts_keys &keys);
|
||||
|
||||
bool operator==(const btc_multisig_segwit_address &addr) const;
|
||||
|
||||
bytes get_witness_script() const {
|
||||
return witness_script;
|
||||
}
|
||||
|
||||
std::vector<public_key_type> get_keys();
|
||||
|
||||
private:
|
||||
void create_witness_script();
|
||||
|
||||
void create_segwit_address();
|
||||
|
||||
bytes get_address_bytes(const bytes &script_hash);
|
||||
|
||||
public:
|
||||
bytes witness_script;
|
||||
};
|
||||
|
||||
class btc_weighted_multisig_address : public bitcoin_address {
|
||||
|
||||
public:
|
||||
btc_weighted_multisig_address() = default;
|
||||
|
||||
btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
bytes get_witness_script() const {
|
||||
return witness_script_;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data);
|
||||
void create_witness_script();
|
||||
void create_segwit_address();
|
||||
|
||||
public:
|
||||
bytes redeem_script_;
|
||||
bytes witness_script_;
|
||||
};
|
||||
|
||||
class btc_one_or_m_of_n_multisig_address : public bitcoin_address {
|
||||
public:
|
||||
btc_one_or_m_of_n_multisig_address() = default;
|
||||
btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
bytes get_witness_script() const {
|
||||
return witness_script_;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script(const fc::ecc::public_key &user_key_data, const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data);
|
||||
void create_witness_script();
|
||||
void create_segwit_address();
|
||||
|
||||
public:
|
||||
bytes redeem_script_;
|
||||
bytes witness_script_;
|
||||
};
|
||||
|
||||
class btc_one_or_weighted_multisig_address : public bitcoin_address {
|
||||
public:
|
||||
btc_one_or_weighted_multisig_address() = default;
|
||||
btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
bytes get_witness_script() const {
|
||||
return witness_script_;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data);
|
||||
void create_witness_script();
|
||||
void create_segwit_address();
|
||||
|
||||
public:
|
||||
bytes redeem_script_;
|
||||
bytes witness_script_;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
||||
FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::bitcoin_address::network,
|
||||
(mainnet)(testnet)(regtest))
|
||||
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::bitcoin_address, (address)(type)(raw_address)(network_type));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_multisig_address, (graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script)(keys_required)(witnesses_keys));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_multisig_segwit_address, (graphene::peerplays_sidechain::bitcoin::btc_multisig_address), (witness_script));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_weighted_multisig_address,
|
||||
(graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script_)(witness_script_));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_one_or_m_of_n_multisig_address,
|
||||
(graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script_)(witness_script_));
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
enum class op {
|
||||
// push value
|
||||
_0 = 0x00,
|
||||
_1 = 0x51,
|
||||
_2 = 0x52,
|
||||
_3 = 0x53,
|
||||
_4 = 0x54,
|
||||
_5 = 0x55,
|
||||
_6 = 0x56,
|
||||
_7 = 0x57,
|
||||
_8 = 0x58,
|
||||
_9 = 0x59,
|
||||
_10 = 0x5a,
|
||||
_11 = 0x5b,
|
||||
_12 = 0x5c,
|
||||
_13 = 0x5d,
|
||||
_14 = 0x5e,
|
||||
_15 = 0x5f,
|
||||
_16 = 0x60,
|
||||
|
||||
// control
|
||||
IF = 0x63,
|
||||
NOTIF = 0x64,
|
||||
ELSE = 0x67,
|
||||
ENDIF = 0x68,
|
||||
RETURN = 0x6a,
|
||||
|
||||
// stack ops
|
||||
DUP = 0x76,
|
||||
SWAP = 0x7c,
|
||||
|
||||
// bit logic
|
||||
EQUAL = 0x87,
|
||||
EQUALVERIFY = 0x88,
|
||||
ADD = 0x93,
|
||||
GREATERTHAN = 0xa0,
|
||||
GREATERTHANOREQUAL = 0xa2,
|
||||
|
||||
// crypto
|
||||
HASH160 = 0xa9,
|
||||
CHECKSIG = 0xac,
|
||||
CHECKMULTISIG = 0xae,
|
||||
// Locktime
|
||||
CHECKLOCKTIMEVERIFY = 0xb1,
|
||||
CHECKSEQUENCEVERIFY = 0xb2,
|
||||
};
|
||||
|
||||
class script_builder {
|
||||
|
||||
public:
|
||||
script_builder &operator<<(op opcode);
|
||||
|
||||
script_builder &operator<<(uint32_t number);
|
||||
|
||||
script_builder &operator<<(size_t size);
|
||||
|
||||
script_builder &operator<<(const bytes &sc);
|
||||
|
||||
script_builder &operator<<(const fc::sha256 &hash);
|
||||
|
||||
script_builder &operator<<(const fc::ripemd160 &hash);
|
||||
|
||||
script_builder &operator<<(const fc::ecc::public_key_data &pubkey_data);
|
||||
|
||||
operator bytes() const {
|
||||
return std::move(script);
|
||||
}
|
||||
|
||||
private:
|
||||
bytes script;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
struct out_point {
|
||||
fc::sha256 hash;
|
||||
uint32_t n = 0;
|
||||
out_point() = default;
|
||||
out_point(fc::sha256 _hash, uint32_t _n) :
|
||||
hash(_hash),
|
||||
n(_n) {
|
||||
}
|
||||
bool operator==(const out_point &op) const;
|
||||
};
|
||||
|
||||
struct tx_in {
|
||||
|
||||
bool operator==(const tx_in &ti) const;
|
||||
|
||||
out_point prevout;
|
||||
bytes scriptSig;
|
||||
uint32_t nSequence = 0xffffffff;
|
||||
std::vector<bytes> scriptWitness;
|
||||
};
|
||||
|
||||
struct tx_out {
|
||||
int64_t value = 0;
|
||||
bytes scriptPubKey;
|
||||
|
||||
bool operator==(const tx_out &to) const;
|
||||
|
||||
bool is_p2wsh() const;
|
||||
|
||||
bool is_p2wpkh() const;
|
||||
|
||||
bool is_p2pkh() const;
|
||||
|
||||
bool is_p2sh() const;
|
||||
|
||||
bool is_p2pk() const;
|
||||
|
||||
bytes get_data_or_script() const;
|
||||
};
|
||||
|
||||
struct bitcoin_transaction {
|
||||
|
||||
bool operator!=(const bitcoin_transaction &bt) const;
|
||||
|
||||
int32_t nVersion = 1;
|
||||
std::vector<tx_in> vin;
|
||||
std::vector<tx_out> vout;
|
||||
uint32_t nLockTime = 0;
|
||||
|
||||
fc::sha256 get_hash() const;
|
||||
fc::sha256 get_txid() const;
|
||||
size_t get_vsize() const;
|
||||
};
|
||||
|
||||
class bitcoin_transaction_builder {
|
||||
|
||||
public:
|
||||
bitcoin_transaction_builder() = default;
|
||||
|
||||
bitcoin_transaction_builder(const bitcoin_transaction _tx) :
|
||||
tx(_tx) {
|
||||
}
|
||||
|
||||
void set_version(int32_t version);
|
||||
|
||||
void set_locktime(uint32_t lock_time);
|
||||
|
||||
void add_in(const fc::sha256 &txid, uint32_t n_out,
|
||||
const bytes &script_code, bool front = false, uint32_t sequence = 0xffffffff);
|
||||
|
||||
void add_in(payment_type type, const fc::sha256 &txid, uint32_t n_out,
|
||||
const bytes &script_code, bool front = false, uint32_t sequence = 0xffffffff);
|
||||
|
||||
void add_in(payment_type type, tx_in txin, const bytes &script_code, bool front = false);
|
||||
|
||||
void add_out(payment_type type, int64_t amount, const std::string &base58_address, bool front = false);
|
||||
|
||||
void add_out(payment_type type, int64_t amount, const fc::ecc::public_key_data &pubkey, bool front = false);
|
||||
|
||||
void add_out(payment_type type, int64_t amount, const bytes &script_code, bool front = false);
|
||||
|
||||
void add_out_all_type(const uint64_t &amount, const bitcoin_address &address, bool front = false);
|
||||
|
||||
bitcoin_transaction get_transaction() const {
|
||||
return tx;
|
||||
}
|
||||
|
||||
private:
|
||||
inline bool is_payment_to_pubkey(payment_type type);
|
||||
|
||||
bytes get_script_pubkey(payment_type type, const bytes &script_code);
|
||||
|
||||
bitcoin_transaction tx;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::out_point, (hash)(n))
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::tx_in, (prevout)(scriptSig)(nSequence)(scriptWitness))
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::tx_out, (value)(scriptPubKey))
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::bitcoin_transaction, (nVersion)(vin)(vout)(nLockTime))
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (c) 2017 Pieter Wuille
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace segwit_addr {
|
||||
|
||||
/** Decode a SegWit address. Returns (witver, witprog). witver = -1 means failure. */
|
||||
std::pair<int, std::vector<uint8_t>> decode(const std::string &hrp, const std::string &addr);
|
||||
|
||||
/** Encode a SegWit address. Empty string means failure. */
|
||||
std::string encode(const std::string &hrp, int witver, const std::vector<uint8_t> &witprog);
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::segwit_addr
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
inline void write_compact_size(bytes &vec, size_t size) {
|
||||
bytes sb;
|
||||
sb.reserve(2);
|
||||
if (size < 253) {
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(size));
|
||||
} else if (size <= std::numeric_limits<unsigned short>::max()) {
|
||||
uint16_t tmp = htole16(static_cast<uint16_t>(size));
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(253));
|
||||
sb.insert(sb.end(), reinterpret_cast<const char *>(tmp), reinterpret_cast<const char *>(tmp) + sizeof(tmp));
|
||||
} else if (size <= std::numeric_limits<unsigned int>::max()) {
|
||||
uint32_t tmp = htole32(static_cast<uint32_t>(size));
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(254));
|
||||
sb.insert(sb.end(), reinterpret_cast<const char *>(tmp), reinterpret_cast<const char *>(tmp) + sizeof(tmp));
|
||||
} else {
|
||||
uint64_t tmp = htole64(static_cast<uint64_t>(size));
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(255));
|
||||
sb.insert(sb.end(), reinterpret_cast<const char *>(tmp), reinterpret_cast<const char *>(tmp) + sizeof(tmp));
|
||||
}
|
||||
vec.insert(vec.end(), sb.begin(), sb.end());
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack_compact_size(Stream &s, size_t size) {
|
||||
if (size < 253) {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(size));
|
||||
} else if (size <= std::numeric_limits<unsigned short>::max()) {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(253));
|
||||
fc::raw::pack(s, htole16(static_cast<uint16_t>(size)));
|
||||
} else if (size <= std::numeric_limits<unsigned int>::max()) {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(254));
|
||||
fc::raw::pack(s, htole32(static_cast<uint32_t>(size)));
|
||||
} else {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(255));
|
||||
fc::raw::pack(s, htole64(static_cast<uint64_t>(size)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline uint64_t unpack_compact_size(Stream &s) {
|
||||
uint8_t size;
|
||||
uint64_t size_ret;
|
||||
|
||||
fc::raw::unpack(s, size);
|
||||
|
||||
if (size < 253) {
|
||||
size_ret = size;
|
||||
} else if (size == 253) {
|
||||
uint16_t tmp;
|
||||
fc::raw::unpack(s, tmp);
|
||||
size_ret = le16toh(tmp);
|
||||
if (size_ret < 253)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
||||
} else if (size == 254) {
|
||||
uint32_t tmp;
|
||||
fc::raw::unpack(s, tmp);
|
||||
size_ret = le32toh(tmp);
|
||||
if (size_ret < 0x10000u)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
||||
} else {
|
||||
uint32_t tmp;
|
||||
fc::raw::unpack(s, tmp);
|
||||
size_ret = le64toh(tmp);
|
||||
if (size_ret < 0x100000000ULL)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
||||
}
|
||||
|
||||
if (size_ret > 0x08000000)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "unpack_compact_size(): size too large");
|
||||
|
||||
return size_ret;
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, int64_t &u, uint32_t _max_depth = FC_PACK_MAX_DEPTH) {
|
||||
s.read((char *)&u, sizeof(u));
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, int32_t &u, uint32_t _max_depth = FC_PACK_MAX_DEPTH) {
|
||||
s.read((char *)&u, sizeof(u));
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const std::vector<char> &v) {
|
||||
pack_compact_size(s, v.size());
|
||||
if (!v.empty())
|
||||
s.write(v.data(), v.size());
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, std::vector<char> &v) {
|
||||
const auto size = unpack_compact_size(s);
|
||||
v.resize(size);
|
||||
if (size)
|
||||
s.read(v.data(), size);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
inline void pack(Stream &s, const T &val) {
|
||||
fc::raw::pack(s, val);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
inline void unpack(Stream &s, T &val) {
|
||||
fc::raw::unpack(s, val);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const out_point &op) {
|
||||
fc::sha256 reversed(op.hash);
|
||||
std::reverse(reversed.data(), reversed.data() + reversed.data_size());
|
||||
s.write(reversed.data(), reversed.data_size());
|
||||
pack(s, op.n);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, out_point &op) {
|
||||
uint64_t hash_size = op.hash.data_size();
|
||||
std::unique_ptr<char[]> hash_data(new char[hash_size]);
|
||||
s.read(hash_data.get(), hash_size);
|
||||
std::reverse(hash_data.get(), hash_data.get() + hash_size);
|
||||
|
||||
op.hash = fc::sha256(hash_data.get(), hash_size);
|
||||
unpack(s, op.n);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const tx_in &in) {
|
||||
pack(s, in.prevout);
|
||||
pack(s, in.scriptSig);
|
||||
pack(s, in.nSequence);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, tx_in &in) {
|
||||
unpack(s, in.prevout);
|
||||
unpack(s, in.scriptSig);
|
||||
unpack(s, in.nSequence);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const tx_out &out) {
|
||||
pack(s, out.value);
|
||||
pack(s, out.scriptPubKey);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, tx_out &out) {
|
||||
unpack(s, out.value);
|
||||
unpack(s, out.scriptPubKey);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const bitcoin_transaction &tx, bool with_witness = true) {
|
||||
uint8_t flags = 0;
|
||||
|
||||
if (with_witness) {
|
||||
for (const auto &in : tx.vin) {
|
||||
if (!in.scriptWitness.empty()) {
|
||||
flags |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pack(s, tx.nVersion);
|
||||
|
||||
if (flags) {
|
||||
pack_compact_size(s, 0);
|
||||
pack(s, flags);
|
||||
}
|
||||
|
||||
pack_compact_size(s, tx.vin.size());
|
||||
for (const auto &in : tx.vin)
|
||||
pack(s, in);
|
||||
|
||||
pack_compact_size(s, tx.vout.size());
|
||||
for (const auto &out : tx.vout)
|
||||
pack(s, out);
|
||||
|
||||
if (flags & 1) {
|
||||
for (const auto in : tx.vin) {
|
||||
pack_compact_size(s, in.scriptWitness.size());
|
||||
for (const auto &sc : in.scriptWitness)
|
||||
pack(s, sc);
|
||||
}
|
||||
}
|
||||
|
||||
pack(s, tx.nLockTime);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, bitcoin_transaction &tx) {
|
||||
uint8_t flags = 0;
|
||||
|
||||
unpack(s, tx.nVersion);
|
||||
|
||||
auto vin_size = unpack_compact_size(s);
|
||||
if (vin_size == 0) {
|
||||
unpack(s, flags);
|
||||
vin_size = unpack_compact_size(s);
|
||||
}
|
||||
|
||||
tx.vin.reserve(vin_size);
|
||||
for (size_t i = 0; i < vin_size; i++) {
|
||||
tx_in in;
|
||||
unpack(s, in);
|
||||
tx.vin.push_back(in);
|
||||
}
|
||||
|
||||
const auto vout_size = unpack_compact_size(s);
|
||||
tx.vout.reserve(vout_size);
|
||||
for (size_t i = 0; i < vout_size; i++) {
|
||||
tx_out out;
|
||||
unpack(s, out);
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
if (flags & 1) {
|
||||
for (auto &in : tx.vin) {
|
||||
uint64_t stack_size = unpack_compact_size(s);
|
||||
in.scriptWitness.reserve(stack_size);
|
||||
for (uint64_t i = 0; i < stack_size; i++) {
|
||||
std::vector<char> script;
|
||||
unpack(s, script);
|
||||
in.scriptWitness.push_back(script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unpack(s, tx.nLockTime);
|
||||
}
|
||||
|
||||
inline std::vector<char> pack(const bitcoin_transaction &v, bool with_witness = true) {
|
||||
fc::datastream<size_t> ps;
|
||||
pack(ps, v, with_witness);
|
||||
std::vector<char> vec(ps.tellp());
|
||||
|
||||
if (!vec.empty()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
pack(ds, v, with_witness);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline bitcoin_transaction unpack(const std::vector<char> &s) {
|
||||
try {
|
||||
bitcoin_transaction tmp;
|
||||
if (!s.empty()) {
|
||||
fc::datastream<const char *> ds(s.data(), size_t(s.size()));
|
||||
unpack(ds, tmp);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
FC_RETHROW_EXCEPTIONS(warn, "error unpacking ${type}", ("type", "transaction"))
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack_tx_signature(Stream &s, const std::vector<char> &scriptPubKey, const bitcoin_transaction &tx, unsigned int in_index, int hash_type) {
|
||||
pack(s, tx.nVersion);
|
||||
|
||||
pack_compact_size(s, tx.vin.size());
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
const auto &in = tx.vin[i];
|
||||
pack(s, in.prevout);
|
||||
if (i == in_index)
|
||||
pack(s, scriptPubKey);
|
||||
else
|
||||
pack_compact_size(s, 0); // Blank signature
|
||||
pack(s, in.nSequence);
|
||||
}
|
||||
|
||||
pack_compact_size(s, tx.vout.size());
|
||||
for (const auto &out : tx.vout)
|
||||
pack(s, out);
|
||||
|
||||
pack(s, tx.nLockTime);
|
||||
pack(s, hash_type);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack_tx_witness_signature(Stream &s, const std::vector<char> &scriptCode, const bitcoin_transaction &tx, unsigned int in_index, int64_t amount, int hash_type) {
|
||||
|
||||
fc::sha256 hash_prevouts;
|
||||
fc::sha256 hash_sequence;
|
||||
fc::sha256 hash_output;
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for (const auto in : tx.vin)
|
||||
pack(ps, in.prevout);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (vec.size()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
for (const auto in : tx.vin)
|
||||
pack(ds, in.prevout);
|
||||
}
|
||||
|
||||
hash_prevouts = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for (const auto in : tx.vin)
|
||||
pack(ps, in.nSequence);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (vec.size()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
for (const auto in : tx.vin)
|
||||
pack(ds, in.nSequence);
|
||||
}
|
||||
|
||||
hash_sequence = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
};
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for (const auto out : tx.vout)
|
||||
pack(ps, out);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (vec.size()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
for (const auto out : tx.vout)
|
||||
pack(ds, out);
|
||||
}
|
||||
|
||||
hash_output = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
pack(s, tx.nVersion);
|
||||
pack(s, hash_prevouts);
|
||||
pack(s, hash_sequence);
|
||||
|
||||
pack(s, tx.vin[in_index].prevout);
|
||||
pack(s, scriptCode);
|
||||
pack(s, amount);
|
||||
pack(s, tx.vin[in_index].nSequence);
|
||||
|
||||
pack(s, hash_output);
|
||||
pack(s, tx.nLockTime);
|
||||
pack(s, hash_type);
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
class bitcoin_transaction;
|
||||
|
||||
const secp256k1_context_t *btc_context();
|
||||
|
||||
fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptPubKey, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness);
|
||||
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign = nullptr);
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const bytes &privkey,
|
||||
const secp256k1_context_t *context_sign = nullptr, int hash_type = 1);
|
||||
|
||||
void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts, bool use_mulisig_workaround = true);
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context);
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context_t *context);
|
||||
|
||||
void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
void add_signatures_to_transaction_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
void add_signatures_to_transaction_user_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
class bitcoin_transaction;
|
||||
|
||||
using bytes = std::vector<char>;
|
||||
using accounts_keys = std::map<graphene::chain::son_id_type, graphene::chain::public_key_type>;
|
||||
using full_btc_transaction = std::pair<bitcoin_transaction, uint64_t>;
|
||||
|
||||
enum class payment_type {
|
||||
NULLDATA,
|
||||
P2PK,
|
||||
P2PKH,
|
||||
P2SH,
|
||||
P2WPKH,
|
||||
P2WSH,
|
||||
P2SH_WPKH,
|
||||
P2SH_WSH
|
||||
};
|
||||
|
||||
enum class sidechain_proposal_type {
|
||||
ISSUE_BTC,
|
||||
SEND_BTC_TRANSACTION,
|
||||
REVERT_BTC_TRANSACTION
|
||||
};
|
||||
|
||||
struct prev_out {
|
||||
bool operator!=(const prev_out &obj) const {
|
||||
if (this->hash_tx != obj.hash_tx ||
|
||||
this->n_vout != obj.n_vout ||
|
||||
this->amount != obj.amount) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash_tx;
|
||||
uint32_t n_vout;
|
||||
uint64_t amount;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
||||
FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH));
|
||||
FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::sidechain_proposal_type, (ISSUE_BTC)(SEND_BTC_TRANSACTION)(REVERT_BTC_TRANSACTION));
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::prev_out, (hash_tx)(n_vout)(amount));
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
fc::ecc::public_key_data create_public_key_data(const std::vector<char> &public_key);
|
||||
|
||||
bytes get_privkey_bytes(const std::string &privkey_base58);
|
||||
|
||||
bytes parse_hex(const std::string &str);
|
||||
|
||||
std::vector<bytes> get_pubkey_from_redeemScript(bytes script);
|
||||
|
||||
bytes public_key_data_to_bytes(const fc::ecc::public_key_data &key);
|
||||
|
||||
std::vector<bytes> read_byte_arrays_from_string(const std::string &string_buf);
|
||||
|
||||
std::string write_transaction_signatures(const std::vector<bytes> &data);
|
||||
|
||||
void read_transaction_data(const std::string &string_buf, std::string &tx_hex, std::vector<uint64_t> &in_amounts, std::string &redeem_script);
|
||||
|
||||
std::string write_transaction_data(const std::string &tx, const std::vector<uint64_t> &in_amounts, const std::string &redeem_script);
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
#pragma once
|
||||
#include <fc/optional.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
enum bitcoin_network {
|
||||
mainnet,
|
||||
testnet,
|
||||
regtest
|
||||
};
|
||||
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data);
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet);
|
||||
bytes lock_script_for_redeem_script(const bytes &script);
|
||||
|
||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const bytes &redeem_script,
|
||||
const fc::ecc::private_key &priv_key);
|
||||
|
||||
/*
|
||||
* unsigned_tx - tx, all inputs of which are spends of the PW P2SH address
|
||||
* returns signed transaction
|
||||
*/
|
||||
bytes sign_pw_transfer_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const bytes &redeem_script,
|
||||
const std::vector<fc::optional<fc::ecc::private_key>> &priv_keys);
|
||||
|
||||
///
|
||||
////// \brief Adds dummy signatures instead of real signatures
|
||||
////// \param unsigned_tx
|
||||
////// \param redeem_script
|
||||
////// \param key_count
|
||||
////// \return can be used as partially signed tx
|
||||
bytes add_dummy_signatures_for_pw_transfer(const bytes &unsigned_tx,
|
||||
const bytes &redeem_script,
|
||||
unsigned int key_count);
|
||||
|
||||
///
|
||||
/// \brief replaces dummy sgnatures in partially signed tx with real tx
|
||||
/// \param partially_signed_tx
|
||||
/// \param in_amounts
|
||||
/// \param priv_key
|
||||
/// \param key_idx
|
||||
/// \return
|
||||
///
|
||||
bytes partially_sign_pw_transfer_transaction(const bytes &partially_signed_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
const fc::ecc::private_key &priv_key,
|
||||
unsigned int key_idx);
|
||||
|
||||
///
|
||||
/// \brief Creates ready to publish bitcoin transaction from unsigned tx and
|
||||
/// full set of the signatures. This is alternative way to create tx
|
||||
/// with partially_sign_pw_transfer_transaction
|
||||
/// \param unsigned_tx
|
||||
/// \param signatures
|
||||
/// \param redeem_script
|
||||
/// \return
|
||||
///
|
||||
bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx,
|
||||
const std::vector<std::vector<bytes>> &signatures,
|
||||
const bytes &redeem_script);
|
||||
|
||||
struct btc_outpoint {
|
||||
fc::uint256 hash;
|
||||
uint32_t n;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
struct btc_in {
|
||||
btc_outpoint prevout;
|
||||
bytes scriptSig;
|
||||
uint32_t nSequence;
|
||||
std::vector<bytes> scriptWitness;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
struct btc_out {
|
||||
int64_t nValue;
|
||||
bytes scriptPubKey;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
struct btc_tx {
|
||||
std::vector<btc_in> vin;
|
||||
std::vector<btc_out> vout;
|
||||
int32_t nVersion;
|
||||
uint32_t nLockTime;
|
||||
bool hasWitness;
|
||||
|
||||
void to_bytes(bytes &stream) const;
|
||||
size_t fill_from_bytes(const bytes &data, size_t pos = 0);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -31,6 +31,7 @@ public:
|
|||
const son_object get_current_son_object();
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
bool is_son_deleted(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public:
|
|||
|
||||
virtual bool process_proposal(const proposal_object &po) = 0;
|
||||
virtual void process_primary_wallet() = 0;
|
||||
virtual void process_sidechain_addresses() = 0;
|
||||
virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0;
|
||||
virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0;
|
||||
virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class btc_txout {
|
|||
public:
|
||||
std::string txid_;
|
||||
unsigned int out_num_;
|
||||
double amount_;
|
||||
uint64_t amount_;
|
||||
};
|
||||
|
||||
class bitcoin_rpc_client {
|
||||
|
|
@ -35,16 +35,16 @@ public:
|
|||
std::string getaddressinfo(const std::string &address);
|
||||
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
|
||||
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, const std::string &label = "", const bool rescan = true, const bool p2sh = false);
|
||||
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
||||
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
||||
std::string loadwallet(const std::string &filename);
|
||||
std::string sendrawtransaction(const std::string &tx_hex);
|
||||
std::string signrawtransactionwithwallet(const std::string &tx_hash);
|
||||
std::string unloadwallet(const std::string &filename);
|
||||
std::string walletlock();
|
||||
//std::string walletlock();
|
||||
std::string walletprocesspsbt(std::string const &tx_psbt);
|
||||
bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
|
||||
//bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
|
||||
|
||||
private:
|
||||
fc::http::reply send_post_request(std::string body, bool show_log = false);
|
||||
|
|
@ -87,6 +87,7 @@ public:
|
|||
|
||||
bool process_proposal(const proposal_object &po);
|
||||
void process_primary_wallet();
|
||||
void process_sidechain_addresses();
|
||||
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
|
|
@ -106,26 +107,18 @@ private:
|
|||
|
||||
fc::future<void> on_changed_objects_task;
|
||||
|
||||
std::string create_primary_wallet_address(const std::vector<son_info> &son_pubkeys);
|
||||
|
||||
std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address);
|
||||
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_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 &redeem_script);
|
||||
std::string sign_transaction(const sidechain_transaction_object &sto);
|
||||
std::string send_transaction(const sidechain_transaction_object &sto);
|
||||
|
||||
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);
|
||||
|
||||
std::string sign_transaction_raw(const sidechain_transaction_object &sto);
|
||||
std::string sign_transaction_psbt(const sidechain_transaction_object &sto);
|
||||
std::string sign_transaction_standalone(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);
|
||||
|
||||
void handle_event(const std::string &event_data);
|
||||
std::string get_redeemscript_for_userdeposit(const std::string &user_address);
|
||||
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
|
||||
void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public:
|
|||
|
||||
bool process_proposal(const proposal_object &po);
|
||||
void process_primary_wallet();
|
||||
void process_sidechain_addresses();
|
||||
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public:
|
|||
const son_object get_current_son_object();
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
bool is_son_deleted(son_id_type son_id);
|
||||
bool is_son_delete_op_valid(const chain::operation &op);
|
||||
bool is_son_down_op_valid(const chain::operation &op);
|
||||
bool is_valid_son_proposal(const chain::proposal_object &proposal);
|
||||
|
|
@ -268,6 +269,15 @@ bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) {
|
|||
return (it != active_son_ids.end());
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_son_deleted(son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_son_delete_op_valid(const chain::operation &op) {
|
||||
son_delete_operation delete_op = op.get<son_delete_operation>();
|
||||
return plugin.database().is_son_dereg_valid(delete_op.son_id);
|
||||
|
|
@ -375,6 +385,9 @@ void peerplays_sidechain_plugin_impl::son_processing() {
|
|||
("scheduled_son_id", scheduled_son_id)("now", now));
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_son_deleted(son_id)) {
|
||||
continue;
|
||||
}
|
||||
current_son_id = son_id;
|
||||
|
||||
// These tasks are executed by
|
||||
|
|
@ -646,6 +659,10 @@ bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) {
|
|||
return my->is_active_son(son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_son_deleted(son_id_type son_id) {
|
||||
return my->is_son_deleted(son_id);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) {
|
||||
return my->get_private_key(son_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ void sidechain_net_handler::process_proposals() {
|
|||
|
||||
void sidechain_net_handler::process_active_sons_change() {
|
||||
process_primary_wallet();
|
||||
process_sidechain_addresses();
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_deposits() {
|
||||
|
|
@ -326,7 +327,7 @@ void sidechain_net_handler::process_deposits() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) {
|
||||
if(swdo.id == object_id_type(0, 0, 0))
|
||||
if (swdo.id == object_id_type(0, 0, 0))
|
||||
return;
|
||||
|
||||
ilog("Deposit to process: ${swdo}", ("swdo", swdo));
|
||||
|
|
@ -379,7 +380,7 @@ void sidechain_net_handler::process_withdrawals() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) {
|
||||
if(swwo.id == object_id_type(0, 0, 0))
|
||||
if (swwo.id == object_id_type(0, 0, 0))
|
||||
return;
|
||||
|
||||
ilog("Withdraw to process: ${swwo}", ("swwo", swwo));
|
||||
|
|
@ -426,7 +427,7 @@ void sidechain_net_handler::process_sidechain_transactions() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
if(sto.id == object_id_type(0, 0, 0))
|
||||
if (sto.id == object_id_type(0, 0, 0))
|
||||
return;
|
||||
|
||||
ilog("Sidechain transaction to process: ${sto}", ("sto", sto.id));
|
||||
|
|
@ -461,7 +462,7 @@ void sidechain_net_handler::send_sidechain_transactions() {
|
|||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
if(sto.id == object_id_type(0, 0, 0))
|
||||
if (sto.id == object_id_type(0, 0, 0))
|
||||
return;
|
||||
|
||||
ilog("Sidechain transaction to send: ${sto}", ("sto", sto.id));
|
||||
|
|
@ -490,28 +491,4 @@ void sidechain_net_handler::send_sidechain_transactions() {
|
|||
});
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::process_proposal(const proposal_object &po) {
|
||||
FC_ASSERT(false, "process_proposal not implemented");
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_primary_wallet() {
|
||||
FC_ASSERT(false, "process_primary_wallet not implemented");
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
FC_ASSERT(false, "process_deposit not implemented");
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::process_withdrawal(const son_wallet_withdraw_object &swwo) {
|
||||
FC_ASSERT(false, "process_withdrawal not implemented");
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::process_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
FC_ASSERT(false, "process_sidechain_transaction not implemented");
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::send_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
FC_ASSERT(false, "send_sidechain_transaction not implemented");
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@
|
|||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
|
|
@ -44,7 +49,7 @@ std::string bitcoin_rpc_client::addmultisigaddress(const uint32_t nrequired, con
|
|||
pubkeys = pubkeys + std::string("\"") + pubkey + std::string("\"");
|
||||
}
|
||||
params = params + pubkeys + std::string("]");
|
||||
body = body + params + std::string(", null, \"p2sh-segwit\"] }");
|
||||
body = body + params + std::string(", null, \"bech32\"] }");
|
||||
|
||||
const auto reply = send_post_request(body);
|
||||
|
||||
|
|
@ -457,8 +462,10 @@ std::string bitcoin_rpc_client::getblock(const std::string &block_hash, int32_t
|
|||
|
||||
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\": [\"" +
|
||||
txid + "\"] }");
|
||||
"\"gettransaction\", \"params\": [");
|
||||
|
||||
std::string params = "\"" + txid + "\", " + (include_watch_only ? "true" : "false");
|
||||
body = body + params + "] }";
|
||||
|
||||
const auto reply = send_post_request(body);
|
||||
|
||||
|
|
@ -481,10 +488,15 @@ std::string bitcoin_rpc_client::gettransaction(const std::string &txid, const bo
|
|||
return "";
|
||||
}
|
||||
|
||||
void bitcoin_rpc_client::importaddress(const std::string &address_or_script) {
|
||||
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", "
|
||||
"\"method\": \"importaddress\", \"params\": [" +
|
||||
std::string("\"") + address_or_script + std::string("\"") + std::string("] }"));
|
||||
void bitcoin_rpc_client::importaddress(const std::string &address_or_script, const std::string &label, const bool rescan, const bool p2sh) {
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"importaddress\", "
|
||||
"\"method\": \"importaddress\", \"params\": [");
|
||||
|
||||
std::string params = "\"" + address_or_script + "\", " +
|
||||
"\"" + label + "\", " +
|
||||
(rescan ? "true" : "false") + ", " +
|
||||
(p2sh ? "true" : "false");
|
||||
body = body + params + "] }";
|
||||
|
||||
const auto reply = send_post_request(body);
|
||||
|
||||
|
|
@ -528,7 +540,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -566,7 +580,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);
|
||||
}
|
||||
}
|
||||
|
|
@ -685,32 +701,32 @@ std::string bitcoin_rpc_client::unloadwallet(const std::string &filename) {
|
|||
return "";
|
||||
}
|
||||
|
||||
std::string bitcoin_rpc_client::walletlock() {
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": "
|
||||
"\"walletlock\", \"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::walletlock() {
|
||||
// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletlock\", \"method\": "
|
||||
// "\"walletlock\", \"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::walletprocesspsbt(std::string const &tx_psbt) {
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletprocesspsbt\", \"method\": "
|
||||
|
|
@ -738,31 +754,31 @@ std::string bitcoin_rpc_client::walletprocesspsbt(std::string const &tx_psbt) {
|
|||
return "";
|
||||
}
|
||||
|
||||
bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) {
|
||||
std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": "
|
||||
"\"walletpassphrase\", \"params\": [\"" +
|
||||
passphrase + "\", " + std::to_string(timeout) + "] }");
|
||||
|
||||
const auto reply = send_post_request(body);
|
||||
|
||||
if (reply.body.empty()) {
|
||||
wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
|
||||
if (reply.status == 200) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
//bool bitcoin_rpc_client::walletpassphrase(const std::string &passphrase, uint32_t timeout) {
|
||||
// std::string body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"walletpassphrase\", \"method\": "
|
||||
// "\"walletpassphrase\", \"params\": [\"" +
|
||||
// passphrase + "\", " + std::to_string(timeout) + "] }");
|
||||
//
|
||||
// const auto reply = send_post_request(body);
|
||||
//
|
||||
// if (reply.body.empty()) {
|
||||
// wlog("Bitcoin RPC call ${function} failed", ("function", __FUNCTION__));
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// std::stringstream ss(std::string(reply.body.begin(), reply.body.end()));
|
||||
// boost::property_tree::ptree json;
|
||||
// boost::property_tree::read_json(ss, json);
|
||||
//
|
||||
// if (reply.status == 200) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// 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 false;
|
||||
//}
|
||||
|
||||
fc::http::reply bitcoin_rpc_client::send_post_request(std::string body, bool show_log) {
|
||||
fc::http::connection conn;
|
||||
|
|
@ -954,8 +970,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
|
|||
son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin));
|
||||
}
|
||||
|
||||
uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1;
|
||||
string reply_str = bitcoin_client->createmultisig(nrequired, son_pubkeys_bitcoin);
|
||||
string reply_str = create_primary_wallet_address(active_sons);
|
||||
|
||||
std::stringstream active_pw_ss(reply_str);
|
||||
boost::property_tree::ptree active_pw_pt;
|
||||
|
|
@ -1010,7 +1025,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
|
|||
uint64_t swdo_amount = swdo->sidechain_amount.value;
|
||||
uint64_t swdo_vout = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1));
|
||||
|
||||
std::string tx_str = bitcoin_client->gettransaction(swdo_txid);
|
||||
std::string tx_str = bitcoin_client->gettransaction(swdo_txid, true);
|
||||
std::stringstream tx_ss(tx_str);
|
||||
boost::property_tree::ptree tx_json;
|
||||
boost::property_tree::read_json(tx_ss, tx_json);
|
||||
|
|
@ -1025,6 +1040,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
|
|||
|
||||
for (auto &input : tx_json.get_child("result.details")) {
|
||||
tx_address = input.second.get<std::string>("address");
|
||||
if ((tx_address == swdo_address) && (input.second.get<std::string>("category") == "receive")) {
|
||||
std::string tx_amount_s = input.second.get<std::string>("amount");
|
||||
tx_amount_s.erase(std::remove(tx_amount_s.begin(), tx_amount_s.end(), '.'), tx_amount_s.end());
|
||||
tx_amount = std::stoll(tx_amount_s);
|
||||
|
|
@ -1032,6 +1048,7 @@ bool sidechain_net_handler_bitcoin::process_proposal(const proposal_object &po)
|
|||
tx_vout = std::stoll(tx_vout_s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
should_approve = (swdo_txid == tx_txid) &&
|
||||
(swdo_address == tx_address) &&
|
||||
|
|
@ -1104,16 +1121,7 @@ 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;
|
||||
for (const son_info &si : active_sons) {
|
||||
son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin));
|
||||
}
|
||||
|
||||
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_primary_wallet_address(active_sons);
|
||||
|
||||
std::stringstream active_pw_ss(reply_str);
|
||||
boost::property_tree::ptree active_pw_pt;
|
||||
|
|
@ -1165,6 +1173,50 @@ void sidechain_net_handler_bitcoin::process_primary_wallet() {
|
|||
}
|
||||
}
|
||||
|
||||
void sidechain_net_handler_bitcoin::process_sidechain_addresses() {
|
||||
using namespace bitcoin;
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
std::vector<std::pair<fc::ecc::public_key, uint16_t>> pubkeys;
|
||||
for (auto &son : gpo.active_sons) {
|
||||
std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin);
|
||||
auto pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str)));
|
||||
pubkeys.push_back(std::make_pair(pubkey, son.weight));
|
||||
}
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
[&](const sidechain_address_object &sao) {
|
||||
auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key)));
|
||||
|
||||
btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys);
|
||||
|
||||
sidechain_address_update_operation op;
|
||||
op.payer = plugin.get_current_son_object().son_account;
|
||||
op.sidechain_address_id = sao.id;
|
||||
op.sidechain_address_account = sao.sidechain_address_account;
|
||||
op.sidechain = sao.sidechain;
|
||||
op.deposit_public_key = sao.deposit_public_key;
|
||||
op.deposit_address = addr.get_address();
|
||||
op.withdraw_public_key = sao.withdraw_public_key;
|
||||
op.withdraw_address = sao.withdraw_address;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op);
|
||||
trx.validate();
|
||||
try {
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_bitcoin::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::sidechain_transaction_create_operation>::value, swdo.id)) {
|
||||
|
|
@ -1244,20 +1296,6 @@ bool sidechain_net_handler_bitcoin::process_withdrawal(const son_wallet_withdraw
|
|||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::process_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
//// Uncomment to get signing in order from sto.signers
|
||||
//son_id_type invalid_signer = son_id_type(0xFFFFFFFF);
|
||||
//son_id_type next_signer = invalid_signer;
|
||||
//for (auto &signer : sto.signers) {
|
||||
// if (signer.second == false) {
|
||||
// next_signer = signer.first;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//if ((next_signer == invalid_signer) || (next_signer != plugin.get_current_son_id())) {
|
||||
// return "";
|
||||
//}
|
||||
|
||||
return sign_transaction(sto);
|
||||
}
|
||||
|
||||
|
|
@ -1265,12 +1303,35 @@ std::string sidechain_net_handler_bitcoin::send_sidechain_transaction(const side
|
|||
return send_transaction(sto);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const std::vector<son_info> &son_pubkeys) {
|
||||
using namespace bitcoin;
|
||||
|
||||
std::vector<std::pair<fc::ecc::public_key, uint16_t>> pubkey_weights;
|
||||
for (auto &son : son_pubkeys) {
|
||||
std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin);
|
||||
auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str)));
|
||||
pubkey_weights.push_back(std::make_pair(pub_key, son.weight));
|
||||
}
|
||||
|
||||
btc_weighted_multisig_address addr(pubkey_weights);
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "{\"result\": {\"address\": \"" << addr.get_address() << "\", \"redeemScript\": \"" << fc::to_hex(addr.get_redeem_script()) << "\""
|
||||
<< "}, \"error\":null}";
|
||||
|
||||
std::string res = ss.str();
|
||||
ilog("Weighted Multisig Address = ${a}", ("a", res));
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address) {
|
||||
|
||||
std::stringstream prev_sw_ss(prev_swo.addresses.find(sidechain_type::bitcoin)->second);
|
||||
boost::property_tree::ptree prev_sw_pt;
|
||||
boost::property_tree::read_json(prev_sw_ss, prev_sw_pt);
|
||||
std::string prev_pw_address = prev_sw_pt.get<std::string>("address");
|
||||
std::string prev_redeem_script = prev_sw_pt.get<std::string>("redeemScript");
|
||||
|
||||
if (prev_pw_address == new_sw_address) {
|
||||
wlog("BTC previous and new primary wallet addresses are same. No funds moving needed [from ${prev_sw} to ${new_sw_address}]", ("prev_swo", prev_swo.id)("active_sw", new_sw_address));
|
||||
|
|
@ -1281,8 +1342,7 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con
|
|||
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) {
|
||||
|
|
@ -1293,16 +1353,16 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_transaction(con
|
|||
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[new_sw_address] = total_amount - min_amount;
|
||||
outputs[new_sw_address] = double(total_amount - fee_rate) / 100000000.0;
|
||||
|
||||
return create_transaction(inputs, outputs);
|
||||
return create_transaction(inputs, outputs, prev_redeem_script);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_wallet_deposit_object &swdo) {
|
||||
|
|
@ -1311,7 +1371,8 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_
|
|||
if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
//Get redeem script for deposit address
|
||||
std::string redeem_script = get_redeemscript_for_userdeposit(swdo.sidechain_to);
|
||||
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
|
||||
|
||||
std::stringstream ss(pw_address_json);
|
||||
|
|
@ -1336,12 +1397,13 @@ std::string sidechain_net_handler_bitcoin::create_deposit_transaction(const son_
|
|||
btc_txout utxo;
|
||||
utxo.txid_ = txid;
|
||||
utxo.out_num_ = std::stoul(nvout);
|
||||
utxo.amount_ = swdo.sidechain_amount.value;
|
||||
|
||||
inputs.push_back(utxo);
|
||||
|
||||
outputs[pw_address] = transfer_amount;
|
||||
|
||||
return create_transaction(inputs, outputs);
|
||||
return create_transaction(inputs, outputs, redeem_script);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const son_wallet_withdraw_object &swwo) {
|
||||
|
|
@ -1358,13 +1420,13 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
|
|||
boost::property_tree::read_json(ss, json);
|
||||
|
||||
std::string pw_address = json.get<std::string>("address");
|
||||
std::string redeem_script = json.get<std::string>("redeemScript");
|
||||
|
||||
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
|
||||
uint64_t min_fee_rate = 1000;
|
||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
||||
|
||||
double min_amount = ((double)(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) {
|
||||
|
|
@ -1375,254 +1437,119 @@ std::string sidechain_net_handler_bitcoin::create_withdrawal_transaction(const s
|
|||
total_amount += utx.amount_;
|
||||
}
|
||||
|
||||
if (min_amount > total_amount) {
|
||||
if ((swwo.withdraw_amount.value > total_amount) || (fee_rate >= swwo.withdraw_amount.value)) {
|
||||
elog("Failed not enough BTC to spend for ${pw}", ("pw", pw_address));
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
outputs[swwo.withdraw_address] = (swwo.withdraw_amount.value - fee_rate) / 100000000.0;
|
||||
if ((total_amount - swwo.withdraw_amount.value) > 0.0) {
|
||||
outputs[pw_address] = double(total_amount - swwo.withdraw_amount.value) / 100000000.0;
|
||||
}
|
||||
|
||||
return create_transaction(inputs, outputs);
|
||||
return create_transaction(inputs, outputs, redeem_script);
|
||||
}
|
||||
|
||||
// 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);
|
||||
return new_tx;
|
||||
std::string sidechain_net_handler_bitcoin::create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs, std::string &redeem_script) {
|
||||
using namespace bitcoin;
|
||||
|
||||
bitcoin_transaction_builder tb;
|
||||
std::vector<uint64_t> in_amounts;
|
||||
|
||||
tb.set_version(2);
|
||||
// Set vins
|
||||
for (auto in : inputs) {
|
||||
tb.add_in(fc::sha256(in.txid_), in.out_num_, bitcoin::bytes());
|
||||
in_amounts.push_back(in.amount_);
|
||||
}
|
||||
// Set vouts
|
||||
for (auto out : outputs) {
|
||||
uint64_t satoshis = out.second * 100000000.0;
|
||||
tb.add_out_all_type(satoshis, out.first);
|
||||
}
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
std::string hex_tx = fc::to_hex(pack(tx));
|
||||
std::string tx_raw = write_transaction_data(hex_tx, in_amounts, redeem_script);
|
||||
ilog("Raw transaction ${tx}", ("tx", tx_raw));
|
||||
return tx_raw;
|
||||
}
|
||||
|
||||
// Adds signature to transaction
|
||||
// Function to actually add signature should return transaction with added signature string, or empty string in case of failure
|
||||
std::string sidechain_net_handler_bitcoin::sign_transaction(const sidechain_transaction_object &sto) {
|
||||
std::string new_tx = "";
|
||||
//new_tx = sign_transaction_raw(sto);
|
||||
new_tx = sign_transaction_psbt(sto);
|
||||
//new_tx = sign_transaction_standalone(sto);
|
||||
return new_tx;
|
||||
using namespace bitcoin;
|
||||
std::string pubkey = plugin.get_current_son_object().sidechain_public_keys.at(sidechain);
|
||||
std::string prvkey = get_private_key(pubkey);
|
||||
std::vector<uint64_t> in_amounts;
|
||||
std::string tx_hex;
|
||||
std::string redeem_script;
|
||||
|
||||
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 "";
|
||||
}
|
||||
const auto secret = btc_private_key->get_secret();
|
||||
bitcoin::bytes privkey_signing(secret.data(), secret.data() + secret.data_size());
|
||||
|
||||
read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script);
|
||||
|
||||
ilog("Sign transaction retreived: ${s}", ("s", tx_hex));
|
||||
|
||||
bitcoin_transaction tx = unpack(parse_hex(tx_hex));
|
||||
std::vector<bitcoin::bytes> redeem_scripts(tx.vin.size(), parse_hex(redeem_script));
|
||||
auto sigs = sign_witness_transaction_part(tx, redeem_scripts, in_amounts, privkey_signing, btc_context(), 1);
|
||||
std::string tx_signature = write_transaction_signatures(sigs);
|
||||
|
||||
ilog("Signatures: son-id = ${son}, pkey = ${prvkey}, tx_signature = ${s}", ("son", plugin.get_current_son_id())("prvkey", prvkey)("s", tx_signature));
|
||||
|
||||
return tx_signature;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_transaction_object &sto) {
|
||||
//return send_transaction_raw(sto);
|
||||
return send_transaction_psbt(sto);
|
||||
}
|
||||
using namespace bitcoin;
|
||||
std::vector<uint64_t> in_amounts;
|
||||
std::string tx_hex;
|
||||
std::string redeem_script;
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_transaction_raw(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
||||
return bitcoin_client->createrawtransaction(inputs, outputs);
|
||||
}
|
||||
read_transaction_data(sto.transaction, tx_hex, in_amounts, redeem_script);
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_transaction_psbt(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
||||
return bitcoin_client->createpsbt(inputs, outputs);
|
||||
}
|
||||
ilog("Send transaction retreived: ${s}", ("s", tx_hex));
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::create_transaction_standalone(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs) {
|
||||
// Examples
|
||||
bitcoin_transaction tx = unpack(parse_hex(tx_hex));
|
||||
|
||||
// Transaction with no inputs and outputs
|
||||
//bitcoin-core.cli -rpcuser=1 -rpcpassword=1 -rpcwallet="" createrawtransaction '[]' '[]'
|
||||
//02000000000000000000
|
||||
//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": [
|
||||
// ]
|
||||
//}
|
||||
std::vector<bitcoin::bytes> redeem_scripts(tx.vin.size(), parse_hex(redeem_script));
|
||||
|
||||
// Transaction with input and output
|
||||
//{
|
||||
// "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) {
|
||||
if (sto.transaction.empty()) {
|
||||
elog("Signing failed, tx string is empty");
|
||||
return "";
|
||||
uint32_t inputs_number = in_amounts.size();
|
||||
vector<bitcoin::bytes> dummy;
|
||||
dummy.resize(inputs_number);
|
||||
//Organise weighted address signatures
|
||||
//Add dummies for empty signatures
|
||||
vector<vector<bitcoin::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));
|
||||
}
|
||||
|
||||
if (!wallet_password.empty()) {
|
||||
bitcoin_client->walletpassphrase(wallet_password, 5);
|
||||
//Add empty sig for user signature for Deposit transaction
|
||||
if (sto.object_id.type() == son_wallet_deposit_object::type_id) {
|
||||
add_signatures_to_transaction_user_weighted_multisig(tx, signatures);
|
||||
} else {
|
||||
add_signatures_to_transaction_weighted_multisig(tx, signatures);
|
||||
}
|
||||
//Add redeemscripts to vins and make tx ready for sending
|
||||
sign_witness_transaction_finalize(tx, redeem_scripts, false);
|
||||
std::string final_tx_hex = fc::to_hex(pack(tx));
|
||||
std::string res = bitcoin_client->sendrawtransaction(final_tx_hex);
|
||||
|
||||
std::string reply_str = bitcoin_client->signrawtransactionwithwallet(sto.transaction);
|
||||
ilog("Send transaction: ${tx}, [${res}]", ("tx", final_tx_hex)("res", res));
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
boost::property_tree::ptree json_res = json.get_child("result");
|
||||
|
||||
if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) {
|
||||
elog("Failed to process raw transaction ${tx}", ("tx", sto.transaction));
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string new_tx_raw = json_res.get<std::string>("hex");
|
||||
bool complete_raw = json_res.get<bool>("complete");
|
||||
|
||||
if (complete_raw) {
|
||||
return new_tx_raw;
|
||||
}
|
||||
return new_tx_raw;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::sign_transaction_psbt(const sidechain_transaction_object &sto) {
|
||||
if (sto.transaction.empty()) {
|
||||
elog("Signing failed, tx string is empty");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (!wallet_password.empty()) {
|
||||
bitcoin_client->walletpassphrase(wallet_password, 5);
|
||||
}
|
||||
|
||||
std::string reply_str = bitcoin_client->walletprocesspsbt(sto.transaction);
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
boost::property_tree::ptree json_res = json.get_child("result");
|
||||
|
||||
if ((json_res.count("psbt") == 0) || (json_res.count("complete") == 0)) {
|
||||
elog("Failed to process psbt transaction ${tx}", ("tx", sto.transaction));
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string new_tx_psbt = json_res.get<std::string>("psbt");
|
||||
bool complete_psbt = json_res.get<bool>("complete");
|
||||
|
||||
if (!complete_psbt) {
|
||||
// Try to combine and finalize
|
||||
vector<std::string> psbts;
|
||||
for (auto signature : sto.signatures) {
|
||||
if (!signature.second.empty()) {
|
||||
psbts.push_back(signature.second);
|
||||
}
|
||||
}
|
||||
psbts.push_back(new_tx_psbt);
|
||||
|
||||
std::string reply_str = bitcoin_client->combinepsbt(psbts);
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
if (json.count("error") && json.get_child("error").empty()) {
|
||||
|
||||
std::string new_tx_psbt = json.get<std::string>("result");
|
||||
|
||||
std::string reply_str_fin = bitcoin_client->finalizepsbt(new_tx_psbt);
|
||||
std::stringstream ss_fin(reply_str_fin);
|
||||
boost::property_tree::ptree json_fin;
|
||||
boost::property_tree::read_json(ss_fin, json_fin);
|
||||
boost::property_tree::ptree json_res = json_fin.get_child("result");
|
||||
|
||||
if (json_res.count("hex") && json_res.count("complete")) {
|
||||
complete_psbt = json_res.get<bool>("complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new_tx_psbt;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::sign_transaction_standalone(const sidechain_transaction_object &sto) {
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::send_transaction_raw(const sidechain_transaction_object &sto) {
|
||||
return bitcoin_client->sendrawtransaction(sto.transaction);
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::send_transaction_psbt(const sidechain_transaction_object &sto) {
|
||||
vector<std::string> psbts;
|
||||
for (auto signature : sto.signatures) {
|
||||
if (!signature.second.empty()) {
|
||||
psbts.push_back(signature.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::string reply_str = bitcoin_client->combinepsbt(psbts);
|
||||
|
||||
std::stringstream ss(reply_str);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
|
||||
if (json.count("error") && !json.get_child("error").empty()) {
|
||||
elog("Failed to combine psbt transactions from ${sto}", ("sto", sto));
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string new_tx_psbt = json.get<std::string>("result");
|
||||
|
||||
std::string reply_str_fin = bitcoin_client->finalizepsbt(new_tx_psbt);
|
||||
std::stringstream ss_fin(reply_str_fin);
|
||||
boost::property_tree::ptree json_fin;
|
||||
boost::property_tree::read_json(ss_fin, json_fin);
|
||||
boost::property_tree::ptree json_res = json_fin.get_child("result");
|
||||
|
||||
if ((json_res.count("hex") == 0) || (json_res.count("complete") == 0)) {
|
||||
elog("Failed to finalize psbt transaction ${tx}", ("tx", new_tx_psbt));
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string new_tx_raw = json_res.get<std::string>("hex");
|
||||
bool complete_raw = json_res.get<bool>("complete");
|
||||
|
||||
if (complete_raw) {
|
||||
return bitcoin_client->sendrawtransaction(new_tx_raw);
|
||||
}
|
||||
|
||||
return "";
|
||||
return res;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data) {
|
||||
|
|
@ -1633,6 +1560,7 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data)
|
|||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address>();
|
||||
|
||||
for (const auto &v : vins) {
|
||||
// !!! EXTRACT DEPOSIT ADDRESS FROM SIDECHAIN ADDRESS OBJECT
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, v.address));
|
||||
if (addr_itr == sidechain_addresses_idx.end())
|
||||
continue;
|
||||
|
|
@ -1661,6 +1589,31 @@ void sidechain_net_handler_bitcoin::handle_event(const std::string &event_data)
|
|||
}
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(const std::string &user_address) {
|
||||
using namespace bitcoin;
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, user_address));
|
||||
if (addr_itr == sidechain_addresses_idx.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto &idx = database.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx.rbegin();
|
||||
if (obj == idx.rend() || obj->addresses.find(sidechain_type::bitcoin) == obj->addresses.end()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::vector<std::pair<fc::ecc::public_key, uint16_t>> pubkey_weights;
|
||||
for (auto &son : obj->sons) {
|
||||
std::string pub_key_str = son.sidechain_public_keys.at(sidechain_type::bitcoin);
|
||||
auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(pub_key_str)));
|
||||
pubkey_weights.push_back(std::make_pair(pub_key, son.weight));
|
||||
}
|
||||
auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key)));
|
||||
btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights);
|
||||
return fc::to_hex(deposit_addr.get_redeem_script());
|
||||
}
|
||||
|
||||
std::vector<info_for_vin> sidechain_net_handler_bitcoin::extract_info_from_block(const std::string &_block) {
|
||||
std::stringstream ss(_block);
|
||||
boost::property_tree::ptree block;
|
||||
|
|
@ -1716,28 +1669,23 @@ void sidechain_net_handler_bitcoin::on_changed_objects_cb(const vector<object_id
|
|||
boost::property_tree::ptree pw_pt;
|
||||
boost::property_tree::read_json(pw_ss, pw_pt);
|
||||
|
||||
if (!wallet_password.empty()) {
|
||||
bitcoin_client->walletpassphrase(wallet_password, 5);
|
||||
}
|
||||
|
||||
std::string pw_address = "";
|
||||
if (pw_pt.count("address")) {
|
||||
pw_address = pw_pt.get<std::string>("address");
|
||||
std::string pw_address = pw_pt.get<std::string>("address");
|
||||
bitcoin_client->importaddress(pw_address);
|
||||
}
|
||||
|
||||
std::string pw_redeem_script = "";
|
||||
if (pw_pt.count("redeemScript")) {
|
||||
pw_redeem_script = pw_pt.get<std::string>("redeemScript");
|
||||
bitcoin_client->importaddress(pw_redeem_script);
|
||||
std::string pw_redeem_script = pw_pt.get<std::string>("redeemScript");
|
||||
bitcoin_client->importaddress(pw_redeem_script, "", true, true);
|
||||
}
|
||||
|
||||
vector<string> son_pubkeys_bitcoin;
|
||||
for (const son_info &si : swo->sons) {
|
||||
son_pubkeys_bitcoin.push_back(si.sidechain_public_keys.at(sidechain_type::bitcoin));
|
||||
}
|
||||
uint32_t nrequired = son_pubkeys_bitcoin.size() * 2 / 3 + 1;
|
||||
bitcoin_client->addmultisigaddress(nrequired, son_pubkeys_bitcoin);
|
||||
}
|
||||
if (id.is<sidechain_address_object>()) {
|
||||
const auto &sai = database.get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
auto sao = sai.find(id);
|
||||
if (sao != sai.end()) {
|
||||
bitcoin_client->importaddress(sao->deposit_address);
|
||||
bitcoin_client->importaddress(sao->withdraw_address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,10 @@ void sidechain_net_handler_peerplays::process_primary_wallet() {
|
|||
return;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_peerplays::process_sidechain_addresses() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1361,7 +1361,7 @@ class wallet_api
|
|||
/**
|
||||
* Update a SON object owned by the given account.
|
||||
*
|
||||
* @param witness The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON.
|
||||
* @param owner_account The name of the SON's owner account. Also accepts the ID of the owner account or the ID of the SON.
|
||||
* @param url Same as for create_son. The empty string makes it remain the same.
|
||||
* @param block_signing_key The new block signing public key. The empty string makes it remain the same.
|
||||
* @param sidechain_public_keys The new set of sidechain public keys. The empty string makes it remain the same.
|
||||
|
|
@ -1451,14 +1451,18 @@ class wallet_api
|
|||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @param sidechain a sidechain to whom address belongs
|
||||
* @param deposit_public_key sidechain public key used for deposit address
|
||||
* @param deposit_address sidechain address for deposits
|
||||
* @param withdraw_public_key sidechain public key used for withdraw address
|
||||
* @param withdraw_address sidechain address for withdrawals
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction adding sidechain address
|
||||
*/
|
||||
signed_transaction add_sidechain_address(string account,
|
||||
sidechain_type sidechain,
|
||||
string deposit_public_key,
|
||||
string deposit_address,
|
||||
string withdraw_public_key,
|
||||
string withdraw_address,
|
||||
bool broadcast = false);
|
||||
|
||||
|
|
@ -1468,14 +1472,18 @@ class wallet_api
|
|||
*
|
||||
* @param account the name or id of the account who owns the address
|
||||
* @param sidechain a sidechain to whom address belongs
|
||||
* @param deposit_public_key sidechain public key used for deposit address
|
||||
* @param deposit_address sidechain address for deposits
|
||||
* @param withdraw_public_key sidechain public key used for withdraw address
|
||||
* @param withdraw_address sidechain address for withdrawals
|
||||
* @param broadcast true to broadcast the transaction on the network
|
||||
* @returns the signed transaction updating sidechain address
|
||||
*/
|
||||
signed_transaction update_sidechain_address(string account,
|
||||
sidechain_type sidechain,
|
||||
string deposit_public_key,
|
||||
string deposit_address,
|
||||
string withdraw_public_key,
|
||||
string withdraw_address,
|
||||
bool broadcast = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -2097,16 +2097,21 @@ public:
|
|||
|
||||
signed_transaction add_sidechain_address(string account,
|
||||
sidechain_type sidechain,
|
||||
string deposit_public_key,
|
||||
string deposit_address,
|
||||
string withdraw_public_key,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
account_id_type sidechain_address_account_id = get_account_id(account);
|
||||
|
||||
sidechain_address_add_operation op;
|
||||
op.payer = sidechain_address_account_id;
|
||||
op.sidechain_address_account = sidechain_address_account_id;
|
||||
op.sidechain = sidechain;
|
||||
op.deposit_public_key = deposit_public_key;
|
||||
op.deposit_address = deposit_address;
|
||||
op.withdraw_public_key = withdraw_public_key;
|
||||
op.withdraw_address = withdraw_address;
|
||||
|
||||
signed_transaction tx;
|
||||
|
|
@ -2119,7 +2124,9 @@ public:
|
|||
|
||||
signed_transaction update_sidechain_address(string account,
|
||||
sidechain_type sidechain,
|
||||
string deposit_public_key,
|
||||
string deposit_address,
|
||||
string withdraw_public_key,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{ try {
|
||||
|
|
@ -2129,10 +2136,13 @@ public:
|
|||
FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain));
|
||||
|
||||
sidechain_address_update_operation op;
|
||||
op.payer = sidechain_address_account_id;
|
||||
op.sidechain_address_id = sao->id;
|
||||
op.sidechain_address_account = sidechain_address_account_id;
|
||||
op.sidechain = sidechain;
|
||||
op.deposit_public_key = deposit_public_key;
|
||||
op.deposit_address = deposit_address;
|
||||
op.withdraw_public_key = withdraw_public_key;
|
||||
op.withdraw_address = withdraw_address;
|
||||
|
||||
signed_transaction tx;
|
||||
|
|
@ -2153,6 +2163,7 @@ public:
|
|||
FC_THROW("No sidechain address for account ${account} and sidechain ${sidechain}", ("account", sidechain_address_account_id)("sidechain", sidechain));
|
||||
|
||||
sidechain_address_delete_operation op;
|
||||
op.payer = sidechain_address_account_id;
|
||||
op.sidechain_address_id = sao->id;
|
||||
op.sidechain_address_account = sidechain_address_account_id;
|
||||
op.sidechain = sidechain;
|
||||
|
|
@ -4816,20 +4827,24 @@ vector<optional<son_wallet_object>> wallet_api::get_son_wallets(uint32_t limit)
|
|||
|
||||
signed_transaction wallet_api::add_sidechain_address(string account,
|
||||
sidechain_type sidechain,
|
||||
string deposit_public_key,
|
||||
string deposit_address,
|
||||
string withdraw_public_key,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->add_sidechain_address(account, sidechain, deposit_address, withdraw_address, broadcast);
|
||||
return my->add_sidechain_address(account, sidechain, deposit_public_key, deposit_address, withdraw_public_key, withdraw_address, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::update_sidechain_address(string account,
|
||||
sidechain_type sidechain,
|
||||
string deposit_public_key,
|
||||
string deposit_address,
|
||||
string withdraw_public_key,
|
||||
string withdraw_address,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->update_sidechain_address(account, sidechain, deposit_address, withdraw_address, broadcast);
|
||||
return my->update_sidechain_address(account, sidechain, deposit_public_key, deposit_address, withdraw_public_key, withdraw_address, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::delete_sidechain_address(string account,
|
||||
|
|
|
|||
316
tests/peerplays_sidechain/bitcoin_address_tests.cpp
Normal file
316
tests/peerplays_sidechain/bitcoin_address_tests.cpp
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
|
||||
using namespace graphene::peerplays_sidechain::bitcoin;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(bitcoin_address_tests)
|
||||
|
||||
fc::ecc::public_key_data create_public_key_data(const std::vector<char> &public_key) {
|
||||
FC_ASSERT(public_key.size() == 33);
|
||||
fc::ecc::public_key_data key;
|
||||
for (size_t i = 0; i < 33; i++) {
|
||||
key.at(i) = public_key[i];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(addresses_type_test) {
|
||||
// public_key
|
||||
std::string compressed("03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22");
|
||||
BOOST_CHECK(bitcoin_address(compressed).get_type() == payment_type::P2PK);
|
||||
|
||||
std::string uncompressed("04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904");
|
||||
BOOST_CHECK(bitcoin_address(uncompressed).get_type() == payment_type::NULLDATA);
|
||||
|
||||
// segwit_address
|
||||
std::string p2wpkh_mainnet("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
|
||||
BOOST_CHECK(bitcoin_address(p2wpkh_mainnet).get_type() == payment_type::P2WPKH);
|
||||
|
||||
std::string p2wpkh_testnet("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx");
|
||||
BOOST_CHECK(bitcoin_address(p2wpkh_testnet).get_type() == payment_type::P2WPKH);
|
||||
|
||||
std::string p2wsh("bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9");
|
||||
BOOST_CHECK(bitcoin_address(p2wsh).get_type() == payment_type::P2WSH);
|
||||
|
||||
// base58
|
||||
std::string p2pkh_mainnet("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem");
|
||||
BOOST_CHECK(bitcoin_address(p2pkh_mainnet).get_type() == payment_type::P2PKH);
|
||||
|
||||
std::string p2pkh_testnet("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn");
|
||||
BOOST_CHECK(bitcoin_address(p2pkh_testnet).get_type() == payment_type::P2PKH);
|
||||
|
||||
std::string p2sh_mainnet("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX");
|
||||
BOOST_CHECK(bitcoin_address(p2sh_mainnet).get_type() == payment_type::P2SH);
|
||||
|
||||
std::string p2sh_testnet("2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc");
|
||||
BOOST_CHECK(bitcoin_address(p2sh_testnet).get_type() == payment_type::P2SH);
|
||||
|
||||
std::string p2sh_regtest1("2NAL3YhMF4VcbRQdectN8XPMJipvATGefTZ");
|
||||
BOOST_CHECK(bitcoin_address(p2sh_regtest1).get_type() == payment_type::P2SH);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(addresses_raw_test) {
|
||||
// public_key
|
||||
std::string compressed("03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22");
|
||||
bytes standard_compressed(parse_hex(compressed));
|
||||
BOOST_CHECK(bitcoin_address(compressed).get_raw_address() == standard_compressed);
|
||||
|
||||
std::string uncompressed("04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904");
|
||||
BOOST_CHECK(bitcoin_address(uncompressed).get_raw_address() == bytes());
|
||||
|
||||
// segwit_address
|
||||
std::string p2wpkh_mainnet("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4");
|
||||
bytes standard_p2wpkh_mainnet(parse_hex("751e76e8199196d454941c45d1b3a323f1433bd6"));
|
||||
BOOST_CHECK(bitcoin_address(p2wpkh_mainnet).get_raw_address() == standard_p2wpkh_mainnet);
|
||||
|
||||
std::string p2wpkh_testnet("tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx");
|
||||
bytes standard_p2wpkh_testnet(parse_hex("751e76e8199196d454941c45d1b3a323f1433bd6"));
|
||||
BOOST_CHECK(bitcoin_address(p2wpkh_testnet).get_raw_address() == standard_p2wpkh_testnet);
|
||||
|
||||
std::string p2wsh("bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9");
|
||||
bytes standard_p2wsh(parse_hex("c7a1f1a4d6b4c1802a59631966a18359de779e8a6a65973735a3ccdfdabc407d"));
|
||||
BOOST_CHECK(bitcoin_address(p2wsh).get_raw_address() == standard_p2wsh);
|
||||
|
||||
// base58
|
||||
std::string p2pkh_mainnet("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem");
|
||||
bytes standard_p2pkh_mainnet(parse_hex("47376c6f537d62177a2c41c4ca9b45829ab99083"));
|
||||
BOOST_CHECK(bitcoin_address(p2pkh_mainnet).get_raw_address() == standard_p2pkh_mainnet);
|
||||
|
||||
std::string p2pkh_testnet("mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn");
|
||||
bytes standard_p2pkh_testnet(parse_hex("243f1394f44554f4ce3fd68649c19adc483ce924"));
|
||||
BOOST_CHECK(bitcoin_address(p2pkh_testnet).get_raw_address() == standard_p2pkh_testnet);
|
||||
|
||||
std::string p2sh_mainnet("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX");
|
||||
bytes standard_p2sh_mainnet(parse_hex("8f55563b9a19f321c211e9b9f38cdf686ea07845"));
|
||||
BOOST_CHECK(bitcoin_address(p2sh_mainnet).get_raw_address() == standard_p2sh_mainnet);
|
||||
|
||||
std::string p2sh_testnet("2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc");
|
||||
bytes standard_p2sh_testnet(parse_hex("4e9f39ca4688ff102128ea4ccda34105324305b0"));
|
||||
BOOST_CHECK(bitcoin_address(p2sh_testnet).get_raw_address() == standard_p2sh_testnet);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_multisig_address_test) {
|
||||
|
||||
std::vector<char> public_key1 = parse_hex("03db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010");
|
||||
std::vector<char> public_key2 = parse_hex("0320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b4417983");
|
||||
std::vector<char> public_key3 = parse_hex("033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f");
|
||||
|
||||
std::vector<char> address = parse_hex("a91460cb986f0926e7c4ca1984ca9f56767da2af031e87");
|
||||
std::vector<char> redeem_script = parse_hex("522103db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010210320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b441798321033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f53ae");
|
||||
|
||||
fc::ecc::public_key_data key1 = create_public_key_data(public_key1);
|
||||
fc::ecc::public_key_data key2 = create_public_key_data(public_key2);
|
||||
fc::ecc::public_key_data key3 = create_public_key_data(public_key3);
|
||||
|
||||
btc_multisig_address cma(2, {{son_id_type(1), public_key_type(key1)}, {son_id_type(2), public_key_type(key2)}, {son_id_type(3), public_key_type(key3)}});
|
||||
|
||||
BOOST_CHECK(address == cma.raw_address);
|
||||
BOOST_CHECK(redeem_script == cma.redeem_script);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_segwit_address_test) {
|
||||
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||
std::vector<char> public_key1 = parse_hex("03b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb");
|
||||
std::vector<char> public_key2 = parse_hex("03dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba6");
|
||||
std::vector<char> public_key3 = parse_hex("033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be1");
|
||||
|
||||
std::vector<char> witness_script = parse_hex("0020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1");
|
||||
|
||||
fc::ecc::public_key_data key1 = create_public_key_data(public_key1);
|
||||
fc::ecc::public_key_data key2 = create_public_key_data(public_key2);
|
||||
fc::ecc::public_key_data key3 = create_public_key_data(public_key3);
|
||||
|
||||
btc_multisig_segwit_address address(2, {{son_id_type(1), public_key_type(key1)}, {son_id_type(2), public_key_type(key2)}, {son_id_type(3), public_key_type(key3)}});
|
||||
BOOST_CHECK(address.get_witness_script() == witness_script);
|
||||
BOOST_CHECK(address.get_address() == "2NGU4ogScHEHEpReUzi9RB2ha58KAFnkFyk");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_segwit_address_10_of_14_test) {
|
||||
std::vector<char> public_key1 = parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275");
|
||||
std::vector<char> public_key2 = parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4");
|
||||
std::vector<char> public_key3 = parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61");
|
||||
std::vector<char> public_key4 = parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866");
|
||||
std::vector<char> public_key5 = parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666");
|
||||
std::vector<char> public_key6 = parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf");
|
||||
std::vector<char> public_key7 = parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f");
|
||||
std::vector<char> public_key8 = parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84");
|
||||
std::vector<char> public_key9 = parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf");
|
||||
std::vector<char> public_key10 = parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5");
|
||||
std::vector<char> public_key11 = parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da");
|
||||
std::vector<char> public_key12 = parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1");
|
||||
std::vector<char> public_key13 = parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8");
|
||||
std::vector<char> public_key14 = parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8");
|
||||
|
||||
std::vector<char> witness_script = parse_hex("0020b70a52b55974d3c8c1a2055bf23e2d6421942135c7be1f786ad8cbce2f532cef");
|
||||
std::vector<char> redeem_script = parse_hex("5a2103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae85eae");
|
||||
|
||||
fc::ecc::public_key_data key1 = create_public_key_data(public_key1);
|
||||
fc::ecc::public_key_data key2 = create_public_key_data(public_key2);
|
||||
fc::ecc::public_key_data key3 = create_public_key_data(public_key3);
|
||||
fc::ecc::public_key_data key4 = create_public_key_data(public_key4);
|
||||
fc::ecc::public_key_data key5 = create_public_key_data(public_key5);
|
||||
fc::ecc::public_key_data key6 = create_public_key_data(public_key6);
|
||||
fc::ecc::public_key_data key7 = create_public_key_data(public_key7);
|
||||
fc::ecc::public_key_data key8 = create_public_key_data(public_key8);
|
||||
fc::ecc::public_key_data key9 = create_public_key_data(public_key9);
|
||||
fc::ecc::public_key_data key10 = create_public_key_data(public_key10);
|
||||
fc::ecc::public_key_data key11 = create_public_key_data(public_key11);
|
||||
fc::ecc::public_key_data key12 = create_public_key_data(public_key12);
|
||||
fc::ecc::public_key_data key13 = create_public_key_data(public_key13);
|
||||
fc::ecc::public_key_data key14 = create_public_key_data(public_key14);
|
||||
|
||||
btc_multisig_segwit_address address(10, {
|
||||
{son_id_type(1), public_key_type(key1)},
|
||||
{son_id_type(2), public_key_type(key2)},
|
||||
{son_id_type(3), public_key_type(key3)},
|
||||
{son_id_type(4), public_key_type(key4)},
|
||||
{son_id_type(5), public_key_type(key5)},
|
||||
{son_id_type(6), public_key_type(key6)},
|
||||
{son_id_type(7), public_key_type(key7)},
|
||||
{son_id_type(8), public_key_type(key8)},
|
||||
{son_id_type(9), public_key_type(key9)},
|
||||
{son_id_type(10), public_key_type(key10)},
|
||||
{son_id_type(11), public_key_type(key11)},
|
||||
{son_id_type(12), public_key_type(key12)},
|
||||
{son_id_type(13), public_key_type(key13)},
|
||||
{son_id_type(14), public_key_type(key14)},
|
||||
});
|
||||
BOOST_CHECK(address.get_witness_script() == witness_script);
|
||||
BOOST_CHECK(address.get_redeem_script() == redeem_script);
|
||||
BOOST_CHECK(address.get_address() == "2MwhYhBrZeb6mTf1kaWcYfLBCCQoMqQybFZ");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_segwit_address_11_of_15_test) {
|
||||
std::vector<char> public_key1 = parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275");
|
||||
std::vector<char> public_key2 = parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4");
|
||||
std::vector<char> public_key3 = parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61");
|
||||
std::vector<char> public_key4 = parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866");
|
||||
std::vector<char> public_key5 = parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666");
|
||||
std::vector<char> public_key6 = parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf");
|
||||
std::vector<char> public_key7 = parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f");
|
||||
std::vector<char> public_key8 = parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84");
|
||||
std::vector<char> public_key9 = parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf");
|
||||
std::vector<char> public_key10 = parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5");
|
||||
std::vector<char> public_key11 = parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da");
|
||||
std::vector<char> public_key12 = parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1");
|
||||
std::vector<char> public_key13 = parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8");
|
||||
std::vector<char> public_key14 = parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8");
|
||||
std::vector<char> public_key15 = parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73");
|
||||
|
||||
fc::ecc::public_key_data key1 = create_public_key_data(public_key1);
|
||||
fc::ecc::public_key_data key2 = create_public_key_data(public_key2);
|
||||
fc::ecc::public_key_data key3 = create_public_key_data(public_key3);
|
||||
fc::ecc::public_key_data key4 = create_public_key_data(public_key4);
|
||||
fc::ecc::public_key_data key5 = create_public_key_data(public_key5);
|
||||
fc::ecc::public_key_data key6 = create_public_key_data(public_key6);
|
||||
fc::ecc::public_key_data key7 = create_public_key_data(public_key7);
|
||||
fc::ecc::public_key_data key8 = create_public_key_data(public_key8);
|
||||
fc::ecc::public_key_data key9 = create_public_key_data(public_key9);
|
||||
fc::ecc::public_key_data key10 = create_public_key_data(public_key10);
|
||||
fc::ecc::public_key_data key11 = create_public_key_data(public_key11);
|
||||
fc::ecc::public_key_data key12 = create_public_key_data(public_key12);
|
||||
fc::ecc::public_key_data key13 = create_public_key_data(public_key13);
|
||||
fc::ecc::public_key_data key14 = create_public_key_data(public_key14);
|
||||
fc::ecc::public_key_data key15 = create_public_key_data(public_key15);
|
||||
|
||||
std::vector<char> witness_script = parse_hex("00205fcdce3c62b477553b8dc957a935adda8305cbd47a408f07d2cb867d588279eb");
|
||||
std::vector<char> redeem_script = parse_hex("5b2103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae");
|
||||
|
||||
btc_multisig_segwit_address address(11, {{son_id_type(1), public_key_type(key1)}, {son_id_type(2), public_key_type(key2)}, {son_id_type(3), public_key_type(key3)}, {son_id_type(4), public_key_type(key4)}, {son_id_type(5), public_key_type(key5)}, {son_id_type(6), public_key_type(key6)}, {son_id_type(7), public_key_type(key7)}, {son_id_type(8), public_key_type(key8)}, {son_id_type(9), public_key_type(key9)}, {son_id_type(10), public_key_type(key10)}, {son_id_type(11), public_key_type(key11)}, {son_id_type(12), public_key_type(key12)}, {son_id_type(13), public_key_type(key13)}, {son_id_type(14), public_key_type(key14)}, {son_id_type(15), public_key_type(key15)}});
|
||||
|
||||
BOOST_CHECK(address.get_witness_script() == witness_script);
|
||||
BOOST_CHECK(address.get_redeem_script() == redeem_script);
|
||||
BOOST_CHECK(address.get_address() == "2NAL3YhMF4VcbRQdectN8XPMJipvATGefTZ");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(btc_weighted_multisig_address_test) {
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for (uint32_t i = 0; i < 15; ++i) {
|
||||
const char *seed = reinterpret_cast<const char *>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for (auto &key : priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, uint16_t>> weights;
|
||||
for (uint16_t i = 0; i < 15; ++i)
|
||||
weights.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
|
||||
btc_weighted_multisig_address addr(weights, btc_weighted_multisig_address::network::testnet);
|
||||
BOOST_CHECK(addr.get_address() == "tb1qaeuy4c0qnudq5u2c8pndd7zyudal3g5eew7y9396592udxdcje4surl6cm");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_1_or_2_of_15_segwit_address_test) {
|
||||
auto public_key1 = fc::ecc::public_key(create_public_key_data(parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275")));
|
||||
auto public_key2 = fc::ecc::public_key(create_public_key_data(parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4")));
|
||||
auto public_key3 = fc::ecc::public_key(create_public_key_data(parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61")));
|
||||
auto public_key4 = fc::ecc::public_key(create_public_key_data(parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866")));
|
||||
auto public_key5 = fc::ecc::public_key(create_public_key_data(parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666")));
|
||||
auto public_key6 = fc::ecc::public_key(create_public_key_data(parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf")));
|
||||
auto public_key7 = fc::ecc::public_key(create_public_key_data(parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f")));
|
||||
auto public_key8 = fc::ecc::public_key(create_public_key_data(parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84")));
|
||||
auto public_key9 = fc::ecc::public_key(create_public_key_data(parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf")));
|
||||
auto public_key10 = fc::ecc::public_key(create_public_key_data(parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5")));
|
||||
auto public_key11 = fc::ecc::public_key(create_public_key_data(parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da")));
|
||||
auto public_key12 = fc::ecc::public_key(create_public_key_data(parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1")));
|
||||
auto public_key13 = fc::ecc::public_key(create_public_key_data(parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8")));
|
||||
auto public_key14 = fc::ecc::public_key(create_public_key_data(parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8")));
|
||||
auto public_key15 = fc::ecc::public_key(create_public_key_data(parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73")));
|
||||
|
||||
auto user_key = fc::ecc::public_key(create_public_key_data(parse_hex("0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd")));
|
||||
|
||||
btc_one_or_m_of_n_multisig_address address(user_key, 2, {public_key1, public_key2, public_key3, public_key4, public_key5, public_key6, public_key7, public_key8, public_key9, public_key10, public_key11, public_key12, public_key13, public_key14, public_key15});
|
||||
|
||||
std::vector<char> redeem_script = parse_hex("63210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac67522103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef2752102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f421025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe986621037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f6662102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf8421023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e521024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c12102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae82102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd735fae68");
|
||||
|
||||
BOOST_CHECK(address.get_address() == "bcrt1qp8vplzrn7alzpjq8cw4ynd6xqzassmefrh48dzsj0krvkq85dywq9txkqr");
|
||||
BOOST_CHECK(address.get_redeem_script() == redeem_script);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(create_1_or_multiweighted_seggwit_address_test) {
|
||||
auto public_key1 = fc::ecc::public_key(create_public_key_data(parse_hex("03456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275")));
|
||||
auto public_key2 = fc::ecc::public_key(create_public_key_data(parse_hex("02d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4")));
|
||||
auto public_key3 = fc::ecc::public_key(create_public_key_data(parse_hex("025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61")));
|
||||
auto public_key4 = fc::ecc::public_key(create_public_key_data(parse_hex("0228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866")));
|
||||
auto public_key5 = fc::ecc::public_key(create_public_key_data(parse_hex("037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666")));
|
||||
auto public_key6 = fc::ecc::public_key(create_public_key_data(parse_hex("02ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccf")));
|
||||
auto public_key7 = fc::ecc::public_key(create_public_key_data(parse_hex("0317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7f")));
|
||||
auto public_key8 = fc::ecc::public_key(create_public_key_data(parse_hex("0266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84")));
|
||||
auto public_key9 = fc::ecc::public_key(create_public_key_data(parse_hex("023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccf")));
|
||||
auto public_key10 = fc::ecc::public_key(create_public_key_data(parse_hex("0229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5")));
|
||||
auto public_key11 = fc::ecc::public_key(create_public_key_data(parse_hex("024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8da")));
|
||||
auto public_key12 = fc::ecc::public_key(create_public_key_data(parse_hex("03df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1")));
|
||||
auto public_key13 = fc::ecc::public_key(create_public_key_data(parse_hex("02bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8")));
|
||||
auto public_key14 = fc::ecc::public_key(create_public_key_data(parse_hex("0287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8")));
|
||||
auto public_key15 = fc::ecc::public_key(create_public_key_data(parse_hex("02053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73")));
|
||||
|
||||
auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex("0368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cd")));
|
||||
|
||||
// key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, uint16_t>> weights;
|
||||
weights.push_back(std::make_pair(public_key1, 1));
|
||||
weights.push_back(std::make_pair(public_key2, 1));
|
||||
weights.push_back(std::make_pair(public_key3, 1));
|
||||
weights.push_back(std::make_pair(public_key4, 1));
|
||||
weights.push_back(std::make_pair(public_key5, 1));
|
||||
weights.push_back(std::make_pair(public_key6, 1));
|
||||
weights.push_back(std::make_pair(public_key7, 1));
|
||||
weights.push_back(std::make_pair(public_key8, 1));
|
||||
weights.push_back(std::make_pair(public_key9, 1));
|
||||
weights.push_back(std::make_pair(public_key10, 1));
|
||||
weights.push_back(std::make_pair(public_key11, 1));
|
||||
weights.push_back(std::make_pair(public_key12, 1));
|
||||
weights.push_back(std::make_pair(public_key13, 1));
|
||||
weights.push_back(std::make_pair(public_key14, 1));
|
||||
weights.push_back(std::make_pair(public_key15, 1));
|
||||
|
||||
btc_one_or_weighted_multisig_address addr(user_pub_key, weights);
|
||||
auto redeem_script = parse_hex("210368dc31b2b547c74f52abfc67c7fc768c68115d8ab96430d9bb4996fa660121cdac635167007c2103456772301e221026269d3095ab5cb623fc239835b583ae4632f99a15107ef275ac635193687c2102d67c26cf20153fe7625ca1454222d3b3aeb53b122d8a0f7d32a3dd4b2c2016f4ac635193687c21025f7cfda933516fd590c5a34ad4a68e3143b6f4155a64b3aab2c55fb851150f61ac635193687c210228155bb1ddcd11c7f14a2752565178023aa963f84ea6b6a052bddebad6fe9866ac635193687c21037500441cfb4484da377073459511823b344f1ef0d46bac1efd4c7c466746f666ac635193687c2102ef0d79bfdb99ab0be674b1d5d06c24debd74bffdc28d466633d6668cc281cccfac635193687c210317941e4219548682fb8d8e172f0a8ce4d83ce21272435c85d598558c8e060b7fac635193687c210266065b27f7e3d3ad45b471b1cd4e02de73fc4737dc2679915a45e293c5adcf84ac635193687c21023821cc3da7be9e8cdceb8f146e9ddd78a9519875ecc5b42fe645af690544bccfac635193687c210229ff2b2106b76c27c393e82d71c20eec32bcf1f0cf1a9aca8a237269a67ff3e5ac635193687c21024d113381cc09deb8a6da62e0470644d1a06de82be2725b5052668c8845a4a8daac635193687c2103df2462a5a2f681a3896f61964a65566ff77448be9a55a6da18506fd9c6c051c1ac635193687c2102bafba3096f546cc5831ce1e49ba7142478a659f2d689bbc70ed37235255172a8ac635193687c210287bcbd4f5d357f89a86979b386402445d7e9a5dccfd16146d1d2ab0dc2c32ae8ac635193687c2102053859d76aa375d6f343a60e3678e906c008015e32fe4712b1fd2b26473bdd73ac635193685aa268");
|
||||
|
||||
BOOST_CHECK(addr.get_address() == "bcrt1qawr44trakzl4qev8ed88samt3g3g5mgcjppc6ffs5h4wyakqzavq6fkcc6");
|
||||
BOOST_CHECK(addr.get_redeem_script() == redeem_script);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
387
tests/peerplays_sidechain/bitcoin_sign_tests.cpp
Normal file
387
tests/peerplays_sidechain/bitcoin_sign_tests.cpp
Normal file
File diff suppressed because one or more lines are too long
166
tests/peerplays_sidechain/bitcoin_transaction_tests.cpp
Normal file
166
tests/peerplays_sidechain/bitcoin_transaction_tests.cpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/utils.hpp>
|
||||
|
||||
using namespace graphene::peerplays_sidechain::bitcoin;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(bitcoin_transaction_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(serialize_bitcoin_transaction_test) {
|
||||
out_point prevout;
|
||||
prevout.hash = fc::sha256("89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a");
|
||||
prevout.n = 0;
|
||||
|
||||
tx_in in;
|
||||
in.prevout = prevout;
|
||||
in.scriptSig = parse_hex("473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101");
|
||||
in.nSequence = 4294967294;
|
||||
|
||||
tx_out out1;
|
||||
out1.value = 3500000000;
|
||||
out1.scriptPubKey = parse_hex("76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac");
|
||||
|
||||
tx_out out2;
|
||||
out2.value = 1499996160;
|
||||
out2.scriptPubKey = parse_hex("76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac");
|
||||
|
||||
bitcoin_transaction tx;
|
||||
tx.nVersion = 2;
|
||||
tx.vin = {in};
|
||||
tx.vout = {out1, out2};
|
||||
tx.nLockTime = 101;
|
||||
|
||||
const auto serialized = pack(tx);
|
||||
|
||||
const auto expected = parse_hex("02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000");
|
||||
|
||||
BOOST_CHECK_EQUAL_COLLECTIONS(serialized.cbegin(), serialized.cend(), expected.cbegin(), expected.cend());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(deserialize_bitcoin_transaction_test) {
|
||||
bitcoin_transaction tx = unpack(parse_hex("02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000"));
|
||||
|
||||
BOOST_CHECK_EQUAL(tx.nVersion, 2);
|
||||
BOOST_CHECK_EQUAL(tx.nLockTime, 101);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(tx.vin.size(), 1);
|
||||
BOOST_CHECK_EQUAL(tx.vin[0].prevout.hash.str(), "89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a");
|
||||
BOOST_CHECK_EQUAL(tx.vin[0].prevout.n, 0);
|
||||
|
||||
BOOST_CHECK(fc::to_hex(tx.vin[0].scriptSig) == "473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101");
|
||||
BOOST_CHECK_EQUAL(tx.vin[0].nSequence, 4294967294);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(tx.vout.size(), 2);
|
||||
BOOST_CHECK_EQUAL(tx.vout[0].value, 3500000000);
|
||||
BOOST_CHECK(fc::to_hex(tx.vout[0].scriptPubKey) == "76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac");
|
||||
BOOST_CHECK_EQUAL(tx.vout[1].value, 1499996160);
|
||||
BOOST_CHECK(fc::to_hex(tx.vout[1].scriptPubKey) == "76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(btc_tx_methods_test) {
|
||||
const auto tx = unpack(parse_hex("0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f856040000002322002001d5d92effa6ffba3efa379f9830d0f75618b13393827152d26e4309000e88b1ffffffff0188b3f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02473044022038421164c6468c63dc7bf724aa9d48d8e5abe3935564d38182addf733ad4cd81022076362326b22dd7bfaf211d5b17220723659e4fe3359740ced5762d0e497b7dcc012321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac00000000"));
|
||||
|
||||
BOOST_CHECK(tx.get_hash().str() == "a5947589e2762107ff650958ba0e3a3cf341f53281d15593530bf9762c4edab1");
|
||||
BOOST_CHECK(tx.get_txid().str() == "954f43dbb30ad8024981c07d1f5eb6c9fd461e2cf1760dd1283f052af746fc88");
|
||||
BOOST_CHECK(tx.get_vsize() == 148);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bitcoin_transaction_builder_test) {
|
||||
// All tests are only to verefy the compilation of transactions, the transactions are not real(not valid)
|
||||
{ // P2PKH to P2PKH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2PKH, fc::sha256("5d42b45d5a3ddcf2421b208885871121551acf6ea5cc1c1b4e666537ab6fcbef"), 0, bytes());
|
||||
tb.add_out(payment_type::P2PKH, 4999990000, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "0200000001efcb6fab3765664e1b1ccca56ecf1a552111878588201b42f2dc3d5a5db4425d0000000000ffffffff01f0ca052a010000001976a9143307bf6f98832e53a48b144d65c6a95700a93ffb88ac00000000");
|
||||
} /*
|
||||
{ // coinbase to P2PK
|
||||
const auto pubkey = fc::raw::unpack<fc::ecc::public_key_data>( parse_hex( "02028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00" ) );
|
||||
out_point prevout;
|
||||
prevout.hash = fc::sha256( "0000000000000000000000000000000000000000000000000000000000000000" );
|
||||
prevout.n = 0xffffffff;
|
||||
|
||||
tx_in txin;
|
||||
txin.prevout = prevout;
|
||||
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version( 2 );
|
||||
tb.add_in( payment_type::P2SH, txin, parse_hex( "022a020101" ) );
|
||||
tb.add_out( payment_type::P2PK, 625000000, pubkey);
|
||||
tb.add_out( payment_type::NULLDATA, 0, parse_hex( "21030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac" ) );
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff05022a020101ffffffff0240be402500000000232102028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00ac0000000000000000256a2321030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac00000000" );
|
||||
}*/
|
||||
{ // P2SH to P2SH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2SH, fc::sha256("40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8"), 0, bytes());
|
||||
tb.add_out(payment_type::P2SH, 0xffffffff, "3P14159f73E4gFr7JterCCQh9QjiTjiZrG");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "0200000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee400000000000ffffffff01ffffffff0000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a8700000000");
|
||||
}
|
||||
{ // P2PK to NULLDATA
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2PK, fc::sha256("fa897a4a2b8bc507db6cf4425e81ca7ebde89a369e07d608ac7f7c311cb13b4f"), 0, bytes());
|
||||
tb.add_out(payment_type::NULLDATA, 0, parse_hex("ffffffff"));
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000014f3bb11c317c7fac08d6079e369ae8bd7eca815e42f46cdb07c58b2b4a7a89fa0000000000ffffffff010000000000000000066a04ffffffff00000000");
|
||||
}
|
||||
{ // P2PK+P2PKH to P2PKH,P2PKH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2PK, fc::sha256("13d29149e08b6ca63263f3dddd303b32f5aab646ebc6b7db84756d80a227f6d9"), 0, bytes());
|
||||
tb.add_in(payment_type::P2PKH, fc::sha256("a8e7f661925cdd2c0e37fc93c03540c113aa6bcea02b35de09377127f76d0da3"), 0, bytes());
|
||||
tb.add_out(payment_type::P2PKH, 4999990000, "mzg9RZ1p29uNXu4uTWoMdMERdVXZpunJhW");
|
||||
tb.add_out(payment_type::P2PKH, 4999990000, "n2SPW6abRxUnnTSSHp73VGahbPW4WT9GaK");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "0200000002d9f627a2806d7584dbb7c6eb46b6aaf5323b30ddddf36332a66c8be04991d2130000000000ffffffffa30d6df727713709de352ba0ce6baa13c14035c093fc370e2cdd5c9261f6e7a80000000000ffffffff02f0ca052a010000001976a914d2276c7ed7af07f697175cc2cbcbbf32da81caba88acf0ca052a010000001976a914e57d9a9af070998bedce991c4d8e39f9c51eb93a88ac00000000");
|
||||
}
|
||||
{ // P2WPKH to P2WPKH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2WPKH, fc::sha256("56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 1, bytes());
|
||||
tb.add_out(payment_type::P2WPKH, 99988480, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001600143307bf6f98832e53a48b144d65c6a95700a93ffb00000000");
|
||||
}
|
||||
{ // P2WSH to P2WSH
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2WSH, fc::sha256("56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 2, bytes());
|
||||
tb.add_out(payment_type::P2WSH, 99988360, "p2xtZoXeX5X8BP8JfFhQK2nD3emtjch7UeFm");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560200000000ffffffff0188b3f505000000001600140000010966776006953d5567439e5e39f86a0d2700000000");
|
||||
}
|
||||
{ // P2SH(WPKH) to P2SH(WPKH)
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
tb.add_in(payment_type::P2SH_WPKH, fc::sha256("56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 3, parse_hex("ab68025513c3dbd2f7b92a94e0581f5d50f654e7"));
|
||||
tb.add_out(payment_type::P2SH_WPKH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560300000014ab68025513c3dbd2f7b92a94e0581f5d50f654e7ffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000");
|
||||
}
|
||||
{ // P2SH(WSH) to P2SH(WSH)
|
||||
bitcoin_transaction_builder tb;
|
||||
tb.set_version(2);
|
||||
const auto redeem_script = parse_hex("21038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac");
|
||||
tb.add_in(payment_type::P2SH_WSH, fc::sha256("fca01bd539623013f6f945dc6173c395394621ffaa53a9eb6da6e9a2e7c9400e"), 0, redeem_script);
|
||||
tb.add_out(payment_type::P2SH_WSH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd");
|
||||
|
||||
const auto tx = tb.get_transaction();
|
||||
BOOST_CHECK(fc::to_hex(pack(tx)) == "02000000010e40c9e7a2e9a66deba953aaff21463995c37361dc45f9f613306239d51ba0fc000000002321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acacffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
@ -1,418 +0,0 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin_utils.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
using namespace graphene::peerplays_sidechain;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tx_serialization)
|
||||
{
|
||||
// use real mainnet transaction
|
||||
// txid: 6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315
|
||||
// json: {"txid":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","hash":"6189e3febb5a21cee8b725aa1ef04ffce7e609448446d3a8d6f483c634ef5315","version":1,"size":224,"vsize":224,"weight":896,"locktime":0,"vin":[{"txid":"55d079ca797fee81416b71b373abedd8722e33c9f73177be0166b5d5fdac478b","vout":0,"scriptSig":{"asm":"3045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253[ALL] 02be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4","hex":"483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4"},"sequence":4294967295}],"vout":[{"value":1.26491535,"n":0,"scriptPubKey":{"asm":"OP_DUP OP_HASH160 95783804d28e528fbc4b48c7700471e6845804eb OP_EQUALVERIFY OP_CHECKSIG","hex":"76a91495783804d28e528fbc4b48c7700471e6845804eb88ac","reqSigs":1,"type":"pubkeyhash","addresses":["1EdKhXv7zjGowPzgDQ4z1wa2ukVrXRXXkP"]}},{"value":0.0002,"n":1,"scriptPubKey":{"asm":"OP_HASH160 fb0670971091da8248b5c900c6515727a20e8662 OP_EQUAL","hex":"a914fb0670971091da8248b5c900c6515727a20e866287","reqSigs":1,"type":"scripthash","addresses":["3QaKF8zobqcqY8aS6nxCD5ZYdiRfL3RCmU"]}}]}
|
||||
// hex: "01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000"
|
||||
fc::string strtx("01000000018b47acfdd5b56601be7731f7c9332e72d8edab73b3716b4181ee7f79ca79d055000000006b483045022100d82e57d4d11d3b811d07f2fa4ded2fb8a3b7bb1d3e9f293433de5c0d1093c3bd02206704ccd2ff437e2f7716b5e9f2502a9cbb41f1245a18b2b10296980f1ae38253012102be9919a5ba373b1af58ad757db19e7c836116bb8138e0c6d99599e4db96568f4ffffffff028f1b8a07000000001976a91495783804d28e528fbc4b48c7700471e6845804eb88ac204e00000000000017a914fb0670971091da8248b5c900c6515727a20e86628700000000");
|
||||
bytes bintx;
|
||||
bintx.resize(strtx.length() / 2);
|
||||
fc::from_hex(strtx, reinterpret_cast<char*>(&bintx[0]), bintx.size());
|
||||
btc_tx tx;
|
||||
BOOST_CHECK_NO_THROW(tx.fill_from_bytes(bintx));
|
||||
BOOST_CHECK(tx.nVersion == 1);
|
||||
BOOST_CHECK(tx.nLockTime == 0);
|
||||
BOOST_CHECK(tx.vin.size() == 1);
|
||||
BOOST_CHECK(tx.vout.size() == 2);
|
||||
bytes buff;
|
||||
tx.to_bytes(buff);
|
||||
BOOST_CHECK(bintx == buff);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_transfer)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// get unspent outputs for old wallet with list_uspent (address should be
|
||||
// added to wallet with import_address before). It should return
|
||||
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
|
||||
// with 20000 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
// Here we just serialize the transaction without scriptSig in inputs then sign it.
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 19000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({20000});
|
||||
std::vector<fc::optional<fc::ecc::private_key>> keys_to_sign;
|
||||
for(auto key: priv_old)
|
||||
keys_to_sign.push_back(fc::optional<fc::ecc::private_key>(key));
|
||||
bytes signed_tx =sign_pw_transfer_transaction(unsigned_tx, in_amounts, redeem_old, keys_to_sign);
|
||||
// this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
|
||||
BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_separate_sign)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// get unspent outputs for old wallet with list_uspent (address should be
|
||||
// added to wallet with import_address before). It should return
|
||||
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
|
||||
// with 20000 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
// Here we just serialize the transaction without scriptSig in inputs then sign it.
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 19000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({20000});
|
||||
|
||||
// prepare tx with dummy signs
|
||||
bytes partially_signed_tx = add_dummy_signatures_for_pw_transfer(unsigned_tx, redeem_old, 15);
|
||||
|
||||
// sign with every old key one by one
|
||||
for(unsigned idx = 0; idx < 15; idx++)
|
||||
partially_signed_tx = partially_sign_pw_transfer_transaction(partially_signed_tx, in_amounts, priv_old[idx], idx);
|
||||
|
||||
// now this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
|
||||
BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_separate_sign2)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// get unspent outputs for old wallet with list_uspent (address should be
|
||||
// added to wallet with import_address before). It should return
|
||||
// 1 UTXO: [508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766:0]
|
||||
// with 20000 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 20000 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
// Here we just serialize the transaction without scriptSig in inputs then sign it.
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 19000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({20000});
|
||||
|
||||
// gather all signatures from all SONs separatelly
|
||||
std::vector<std::vector<bytes> > signature_set;
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
std::vector<bytes> signatures = signatures_for_raw_transaction(unsigned_tx, in_amounts, redeem_old, key);
|
||||
signature_set.push_back(signatures);
|
||||
}
|
||||
|
||||
// create signed tx with all signatures
|
||||
bytes signed_tx = add_signatures_to_unsigned_tx(unsigned_tx, signature_set, redeem_old);
|
||||
|
||||
// now this is real testnet tx with id 1734a2f6192c3953c90f9fd7f69eba16eeb0922207f81f3af32d6534a6f8e850
|
||||
BOOST_CHECK(fc::to_hex((char*)&signed_tx[0], signed_tx.size()) == "020000000001016617ba8fec01d942ef23dfa26c99badceb682050c5e67ec5b76de65dd6368a500000000000ffffffff01384a0000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10473044022028cf6df7ed5c2761d7aa2af20717c8b5ace168a7800d6a566f2c1ae28160cae502205e01a3d91f5b9870577e36fbc26ce0cecc3e628cc376c7016364ec3f370703140147304402205c9a88cbe41eb9c6a16ba1d747456222cbe951d04739d21309ef0c0cf00727f202202d06db830ee5823882c7b6f82b708111a8f37741878896cd3558fb91efe8076401473044022009c3184fc0385eb7ed8dc0374791cbdace0eff0dc27dd80ac68f8cb81110f700022042267e8a8788c314347234ea10db6c1ec21a2d423b784cbfbaadf3b2393c44630147304402202363ce306570dc0bbf6d18d41b67c6488a014a91d8e24c03670b4f65523aca12022029d04c114b8e93d982cadee89d80bb25c5c8bc437d6cd2bfce8e0d83a08d14410148304502210087b4742e5cf9c77ca9f99928e7c7087e7d786e09216485628509e4e0b2f29d7e02207daf2eaee9fe8bf117074be137b7ae4b8503a4f6d263424e8e6a16405d5b723c0147304402204f1c3ed8cf595bfaf79d90f4c55c04c17bb6d446e3b9beca7ee6ee7895c6b752022022ac032f219a81b2845d0a1abfb904e40036a3ad332e7dfada6fda21ef7080b501483045022100d020eca4ba1aa77de9caf98f3a29f74f55268276860b9fa35fa16cfc00219dd8022028237de6ad063116cf8182d2dd45a09cb90c2ec8104d793eb3635a1290027cd6014730440220322193b0feba7356651465b86463c7619cd3d96729df6242e9571c74ff1c3c2902206e1de8e77b71c7b6031a934b52321134b6a8d138e2124e90f6345decbd543efb01483045022100d70ade49b3f17812785a41711e107b27c3d4981f8e12253629c07ec46ee511af02203e1ea9059ed9165eeff827002c7399a30c478a9b6f2b958621bfbc6713ab4dd30147304402206f7f10d9993c7019360276bbe790ab587adadeab08088593a9a0c56524aca4df02207c147fe2e51484801a4e059e611e7514729d685a5df892dcf02ba59d455e678101483045022100d5071b8039364bfaa53ef5e22206f773539b082f28bd1fbaaea995fa28aae0f5022056edf7a7bdd8a9a54273a667be5bcd11191fc871798fb44f6e1e35c95d86a81201483045022100a39f8ffbcd9c3f0591fc731a9856c8e024041017cba20c9935f13e4abcf9e9dc0220786823b8cd55664ff9ad6277899aacfd56fa8e48c38881482418b7d50ca27211014730440220361d3b87fcc2b1c12a9e7c684c78192ccb7fe51b90c281b7058384b0b036927a0220434c9b403ee3802b4e5b53feb9bb37d2a9d8746c3688da993549dd9d9954c6800147304402206dc4c3a4407fe9cbffb724928aa0597148c14a20d0d7fbb36ad5d3e2a3abf85e022039ef7baebbf08494495a038b009c6d4ff4b91c38db840673b87f6c27c3b53e7e01483045022100cadac495ea78d0ce9678a4334b8c43f7fafeea5a59413cc2a0144addb63485f9022078ca133e020e3afd0e79936337afefc21d84d3839f5a225a0f3d3eebc15f959901fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pw_partially_sign)
|
||||
{
|
||||
// key set for the old Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_old.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
// print old keys
|
||||
for(auto key: priv_old)
|
||||
{
|
||||
fc::sha256 secret = key.get_secret();
|
||||
bytes data({239});
|
||||
data.insert(data.end(), secret.data(), secret.data() + secret.data_size());
|
||||
fc::sha256 cs = fc::sha256::hash(fc::sha256::hash((char*)&data[0], data.size()));
|
||||
data.insert(data.end(), cs.data(), cs.data() + 4);
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_old;
|
||||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
bytes redeem_old =generate_redeem_script(weights_old);
|
||||
|
||||
// Old PW address
|
||||
std::string old_pw = p2wsh_address_from_redeem_script(redeem_old, bitcoin_network::testnet);
|
||||
// This address was filled with testnet transaction 508a36d65de66db7c57ee6c5502068ebdcba996ca2df23ef42d901ec8fba1766
|
||||
BOOST_REQUIRE(old_pw == "tb1qfhstznulf5cmjzahlkmnuuvs0tkjtwjlme3ugz8jzfjanf8h5rwsp45t7e");
|
||||
|
||||
bytes scriptPubKey = lock_script_for_redeem_script(redeem_old);
|
||||
|
||||
// key set for the new Primary Wallet
|
||||
std::vector<fc::ecc::private_key> priv_new;
|
||||
for(unsigned i = 16; i < 31; ++i)
|
||||
{
|
||||
const char* seed = reinterpret_cast<const char*>(&i);
|
||||
fc::sha256 h = fc::sha256::hash(seed, sizeof(i));
|
||||
priv_new.push_back(fc::ecc::private_key::generate_from_seed(h));
|
||||
}
|
||||
std::vector<fc::ecc::public_key> pub_new;
|
||||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
bytes redeem_new =generate_redeem_script(weights_new);
|
||||
// New PW address
|
||||
std::string new_pw = p2wsh_address_from_redeem_script(redeem_new, bitcoin_network::testnet);
|
||||
BOOST_REQUIRE(new_pw == "tb1qzegrz8r8z8ddfkql8595d90czng6eyjmx4ur73ls4pq57jg99qhsh9fd2y");
|
||||
|
||||
// try to move funds from old wallet to new one
|
||||
|
||||
// Spent 1 UTXO: [7007b77fcd5fe097d02679252aa112900d08ab20c06052f4148265b21b1f9fbf:0]
|
||||
// with 29999 satoshis
|
||||
// So, we creating a raw transaction with 1 input and one output that gets
|
||||
// 29999 - fee satoshis with createrawtransaction call (bitcoin_rpc_client::prepare_tx)
|
||||
btc_outpoint outpoint;
|
||||
outpoint.hash = fc::uint256("7007b77fcd5fe097d02679252aa112900d08ab20c06052f4148265b21b1f9fbf");
|
||||
// reverse hash due to the different from_hex algo
|
||||
std::reverse(outpoint.hash.data(), outpoint.hash.data() + outpoint.hash.data_size());
|
||||
outpoint.n = 0;
|
||||
btc_in input;
|
||||
input.prevout = outpoint;
|
||||
input.nSequence = 0xffffffff;
|
||||
btc_out output;
|
||||
output.nValue = 29000;
|
||||
output.scriptPubKey = lock_script_for_redeem_script(redeem_new);
|
||||
btc_tx tx;
|
||||
tx.nVersion = 2;
|
||||
tx.nLockTime = 0;
|
||||
tx.hasWitness = false;
|
||||
tx.vin.push_back(input);
|
||||
tx.vout.push_back(output);
|
||||
bytes unsigned_tx;
|
||||
tx.to_bytes(unsigned_tx);
|
||||
std::vector<uint64_t> in_amounts({29999});
|
||||
|
||||
// prepare tx with dummy signs
|
||||
bytes partially_signed_tx = add_dummy_signatures_for_pw_transfer(unsigned_tx, redeem_old, 15);
|
||||
|
||||
// sign with every old key one by one except the first one
|
||||
for(unsigned idx = 1; idx < 15; idx++)
|
||||
partially_signed_tx = partially_sign_pw_transfer_transaction(partially_signed_tx, in_amounts, priv_old[idx], idx);
|
||||
|
||||
// now this is real testnet tx with id e86455c40da6993b6fed70daea2046287b206ab5c16e1ab58c4dfb4a7d6efb84
|
||||
BOOST_CHECK(fc::to_hex((char*)&partially_signed_tx[0], partially_signed_tx.size()) == "02000000000101bf9f1f1bb2658214f45260c020ab080d9012a12a257926d097e05fcd7fb707700000000000ffffffff0148710000000000002200201650311c6711dad4d81f3d0b4695f814d1ac925b35783f47f0a8414f4905282f10483045022100c4c567419754c5c1768e959a35633012e8d22ccc90d7cd1b88d6d430a513fbbd0220729c2a3520d0cae7dd6dcd928624ffa3e0b6ce0c4f5c340653a6c18549182588014830450221008c868ea2cdf5b23bdf9e6c7d7c283b8424aeb4aec43621424baef1ee77dd399a02205431f608006f0f0dcd392fab4f25328808b45d4a73852a197e947b289faefece01483045022100aecac85bbb81bc0a4e127c15090c5ab82a62b9e27a9a6eb8eddf8de294aa9d920220482f2ba8d7b62e9f3f7a68b0ef3236bc56e44481d3eb59f62d1daf4b191dc86001483045022100eb27943f8b511a36b1a843f9b3ddf6930aece5a3c0be697dbafc921924fc049c022065ba3e1e4ad57f56337143136c5d3ee3f56dd60f36e798f07b5646e29343d7320147304402206e24158484ebb2cd14b9c410ecd04841d806d8464ce9a827533484c8ad8d921b022021baec9cd0ad46e7b19c8de7df286093b835df5c6243e90b14f5748dc1b7c13901473044022067bfaf0e39d72e49a081d4e43828746ab7524c4764e445173dd96cc7e6187d46022063ef107375cc45d1c26b1e1c87b97694f71645187ad871db9c05b8e981a0da8601483045022100da0162de3e4a5268b616b9d01a1a4f64b0c371c6b44fb1f740a264455f2bc20d02203a0b45a98a341722ad65ae4ad68538d617b1cfbb229751f875615317eaf15dd4014830450221008220c4f97585e67966d4435ad8497eb89945f13dd8ff24048b830582349041a002204cb03f7271895637a31ce6479d15672c2d70528148e3cd6196e6f722117745c50147304402203e83ab4b15bb0680f82779335acf9a3ce45316150a4538d5e3d25cb863fcec5702204b3913874077ed2cae4e10f8786053b6f157973a54d156d5863f13accca595f50147304402201420d2a2830278ffff5842ecb7173a23642f179435443e780b3d1fe04be5c32e02203818202390e0e63b4309b89f9cce08c0f4dfa539c2ed59b05e24325671e2747c0147304402205624ca9d47ae04afd8fff705706d6853f8c679abb385f19e01c36f9380a0bad602203dc817fc55497e4c1759a3dbfff1662faca593a9f10d3a9b3e24d5ee3165d4400147304402203a959f9a34587c56b86826e6ab65644ab19cbd09ca078459eb59956b02bc753002206df5ded568d0e3e3645f8cb8ca02874dd1bfa82933eb5e01ff2e5a773633e51601483045022100a84ed5be60b9e095d40f3f6bd698425cb9c4d8f95e8b43ca6c5120a6c599e9eb022064c703952d18d753f9198d78188a26888e6b06c832d93f8075311d57a13240160147304402202e71d3af33a18397b90072098881fdbdb8d6e4ffa34d21141212dd815c97d00f02207195f1c06a8f44ca72af15fdaba89b07cf6daef9be981c432b9f5c10f1e374200100fd5c02007c21030e88484f2bb5dcfc0b326e9eb565c27c8291efb064d060d226916857a2676e62ac635193687c2102151ad794a3aeb3cf9c190120da3d13d36cd8bdf21ca1ccb15debd61c601314b0ac635293687c2103b45a5955ea7847d121225c752edaeb4a5d731a056a951a876caaf6d1f69adb7dac635393687c2102def03a6ffade4ffb0017c8d93859a247badd60e2d76d00e2a3713f6621932ec1ac635493687c21035f17aa7d58b8c3ee0d87240fded52b27f3f12768a0a54ba2595e0a929dd87155ac635593687c2103c8582ac6b0bd20cc1b02c6a86bad2ea10cadb758fedd754ba0d97be85b63b5a7ac635693687c21028148a1f9669fc4471e76f7a371d7cc0563b26e0821d9633fd37649744ff54edaac635793687c2102f0313701b0035f0365a59ce1a3d7ae7045e1f2fb25c4656c08071e5baf51483dac635893687c21024c4c25d08173b3c4d4e1375f8107fd7040c2dc0691ae1bf6fe82b8c88a85185fac635993687c210360fe2daa8661a3d25d0df79875d70b1c3d443ade731caafda7488cb68b4071b0ac635a93687c210250e41a6a4abd7b0b3a49eaec24a6fafa99e5aa7b1e3a5aabe60664276df3d937ac635b93687c2103045a32125930ca103c7d7c79b6f379754796cd4ea7fb0059da926e415e3877d3ac635c93687c210344943249d7ca9b47316fef0c2a413dda3a75416a449a29f310ab7fc9d052ed70ac635d93687c2103c62967320b63df5136ff1ef4c7959ef5917ee5a44f75c83e870bc488143d4d69ac635e93687c21020429f776e15770e4dc52bd6f72e6ed6908d51de1c4a64878433c4e3860a48dc4ac635f93680150a000000000");
|
||||
}
|
||||
|
|
@ -2,19 +2,16 @@
|
|||
|
||||
#define BOOST_TEST_MODULE Peerplays SON Tests
|
||||
|
||||
BOOST_AUTO_TEST_CASE(peerplays_sidechain)
|
||||
{
|
||||
|
||||
BOOST_AUTO_TEST_CASE(peerplays_sidechain) {
|
||||
}
|
||||
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
|
||||
boost::unit_test::test_suite *init_unit_test_suite(int argc, char *argv[]) {
|
||||
std::srand(time(NULL));
|
||||
std::cout << "Random number generator seeded to " << time(NULL) << std::endl;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) {
|
|||
BOOST_TEST_MESSAGE("Send sidechain_address_add_operation");
|
||||
|
||||
sidechain_address_add_operation op;
|
||||
|
||||
op.payer = alice_id;
|
||||
op.sidechain_address_account = alice_id;
|
||||
op.sidechain = sidechain_type::bitcoin;
|
||||
op.deposit_public_key = "deposit_public_key";
|
||||
op.deposit_address = "deposit_address";
|
||||
op.withdraw_public_key = "withdraw_public_key";
|
||||
op.withdraw_address = "withdraw_address";
|
||||
|
||||
trx.operations.push_back(op);
|
||||
|
|
@ -47,7 +49,9 @@ BOOST_AUTO_TEST_CASE( sidechain_address_add_test ) {
|
|||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->sidechain_address_account == alice_id );
|
||||
BOOST_CHECK( obj->sidechain == sidechain_type::bitcoin );
|
||||
BOOST_CHECK( obj->deposit_public_key == "deposit_public_key" );
|
||||
BOOST_CHECK( obj->deposit_address == "deposit_address" );
|
||||
BOOST_CHECK( obj->withdraw_public_key == "withdraw_public_key" );
|
||||
BOOST_CHECK( obj->withdraw_address == "withdraw_address" );
|
||||
}
|
||||
|
||||
|
|
@ -64,17 +68,22 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) {
|
|||
auto obj = idx.find( boost::make_tuple( alice_id, sidechain_type::bitcoin ) );
|
||||
BOOST_REQUIRE( obj != idx.end() );
|
||||
|
||||
std::string new_deposit_public_key = "new_deposit_public_key";
|
||||
std::string new_deposit_address = "new_deposit_address";
|
||||
std::string new_withdraw_public_key = "new_withdraw_public_key";
|
||||
std::string new_withdraw_address = "new_withdraw_address";
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE("Send sidechain_address_update_operation");
|
||||
|
||||
sidechain_address_update_operation op;
|
||||
op.payer = alice_id;
|
||||
op.sidechain_address_id = sidechain_address_id_type(0);
|
||||
op.sidechain_address_account = obj->sidechain_address_account;
|
||||
op.sidechain = obj->sidechain;
|
||||
op.deposit_public_key = new_deposit_public_key;
|
||||
op.deposit_address = new_deposit_address;
|
||||
op.withdraw_public_key = new_withdraw_public_key;
|
||||
op.withdraw_address = new_withdraw_address;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
|
|
@ -92,7 +101,9 @@ BOOST_AUTO_TEST_CASE( sidechain_address_update_test ) {
|
|||
BOOST_REQUIRE( obj != idx.end() );
|
||||
BOOST_CHECK( obj->sidechain_address_account == obj->sidechain_address_account );
|
||||
BOOST_CHECK( obj->sidechain == obj->sidechain );
|
||||
BOOST_CHECK( obj->deposit_public_key == new_deposit_public_key );
|
||||
BOOST_CHECK( obj->deposit_address == new_deposit_address );
|
||||
BOOST_CHECK( obj->withdraw_public_key == new_withdraw_public_key );
|
||||
BOOST_CHECK( obj->withdraw_address == new_withdraw_address );
|
||||
}
|
||||
}
|
||||
|
|
@ -114,8 +125,9 @@ BOOST_AUTO_TEST_CASE( sidechain_address_delete_test ) {
|
|||
BOOST_TEST_MESSAGE("Send sidechain_address_delete_operation");
|
||||
|
||||
sidechain_address_delete_operation op;
|
||||
op.payer = alice_id;
|
||||
op.sidechain_address_id = sidechain_address_id_type(0);
|
||||
op.sidechain_address_account = obj->sidechain_address_account;
|
||||
op.sidechain_address_account = alice_id;
|
||||
op.sidechain = obj->sidechain;
|
||||
|
||||
trx.operations.push_back(op);
|
||||
|
|
|
|||
Loading…
Reference in a new issue