Bring in the bitcoin utils code into plugin
This commit is contained in:
parent
6a8cd25878
commit
c4a48f1317
15 changed files with 1707 additions and 1 deletions
|
|
@ -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
|
||||
|
|
@ -7,6 +7,12 @@ add_library( peerplays_sidechain
|
|||
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
|
||||
)
|
||||
|
||||
if (SUPPORT_MULTIPLE_SONS)
|
||||
|
|
|
|||
192
libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp
Executable file
192
libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp
Executable file
|
|
@ -0,0 +1,192 @@
|
|||
// 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 sidechain::bech32
|
||||
258
libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp
Executable file
258
libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp
Executable file
|
|
@ -0,0 +1,258 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
#include <sstream>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} } }
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#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<<( uint8_t number )
|
||||
{
|
||||
FC_ASSERT( 0 <= number && number <= 16 );
|
||||
|
||||
if ( number == 0 )
|
||||
script.push_back( static_cast<uint8_t>( op::_0 ) );
|
||||
else
|
||||
script.push_back( static_cast<uint8_t>( op::_1 ) + number - 1 );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} } }
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.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( 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;
|
||||
}
|
||||
}
|
||||
|
||||
} } }
|
||||
81
libraries/plugins/peerplays_sidechain/bitcoin/segwit_addr.cpp
Executable file
81
libraries/plugins/peerplays_sidechain/bitcoin/segwit_addr.cpp
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
/* 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/segwit_addr.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bech32.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 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;
|
||||
}
|
||||
|
||||
} } } }
|
||||
35
libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp
Executable file
35
libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/utils.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} } }
|
||||
|
|
@ -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 sidechain::bech32
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
#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:
|
||||
|
||||
bitcoin_address() = default;
|
||||
|
||||
bitcoin_address( const std::string& addr ) : address( addr ), type( determine_type() ),
|
||||
raw_address( determine_raw_address() ) {}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
FC_REFLECT( graphene::peerplays_sidechain::bitcoin::bitcoin_address, (address)(type)(raw_address) );
|
||||
|
||||
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) );
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#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
|
||||
RETURN = 0x6a,
|
||||
|
||||
// stack ops
|
||||
DUP = 0x76,
|
||||
|
||||
// bit logic
|
||||
EQUAL = 0x87,
|
||||
EQUALVERIFY = 0x88,
|
||||
|
||||
// crypto
|
||||
HASH160 = 0xa9,
|
||||
CHECKSIG = 0xac,
|
||||
CHECKMULTISIG = 0xae,
|
||||
};
|
||||
|
||||
class script_builder {
|
||||
|
||||
public:
|
||||
|
||||
script_builder& operator<<( op opcode );
|
||||
|
||||
script_builder& operator<<( uint8_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;
|
||||
|
||||
};
|
||||
|
||||
} } }
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#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( 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;
|
||||
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
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 <vector>
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
|
||||
} } } }
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.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 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 );
|
||||
}
|
||||
|
||||
} } }
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
#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::account_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;
|
||||
};
|
||||
|
||||
} } }
|
||||
|
||||
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,14 @@
|
|||
#pragma once
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
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 );
|
||||
|
||||
} } }
|
||||
Loading…
Reference in a new issue