Filling I/O queues. Create class bitcoin_address.

This commit is contained in:
Anzhy Cherrnyavski 2019-01-12 17:51:26 +03:00
parent 73106a3b7e
commit 9137f31960
33 changed files with 1141 additions and 315 deletions

View file

@ -282,6 +282,10 @@ struct get_impacted_account_visitor
_impacted.insert( op.affiliate );
}
void operator()( const affiliate_referral_payout_operation& op ) { }
void operator()( const withdraw_pbtc_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_address_create_operation& op )
{
_impacted.insert( op.payer );

View file

@ -111,6 +111,7 @@ add_library( graphene_chain
betting_market_group_object.cpp
affiliate_payout.cpp
withdraw_pbtc_evaluator.cpp
${HEADERS}
${PROTOCOL_HEADERS}

View file

@ -78,6 +78,7 @@
#include <graphene/chain/event_evaluator.hpp>
#include <graphene/chain/betting_market_evaluator.hpp>
#include <graphene/chain/tournament_evaluator.hpp>
#include <graphene/chain/withdraw_pbtc_evaluator.hpp>
#include <graphene/chain/bitcoin_address_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -241,6 +242,7 @@ void database::initialize_evaluators()
register_evaluator<tournament_join_evaluator>();
register_evaluator<game_move_evaluator>();
register_evaluator<tournament_leave_evaluator>();
register_evaluator<withdraw_pbtc_evaluator>();
register_evaluator<bitcoin_address_create_evaluator>();
}

View file

@ -36,7 +36,7 @@
namespace graphene { namespace chain {
database::database() :
_random_number_generator(fc::ripemd160().data())
i_w_info(*this), _random_number_generator(fc::ripemd160().data())
{
initialize_indexes();
initialize_evaluators();

View file

@ -269,6 +269,10 @@ struct get_impacted_account_visitor
_impacted.insert( op.affiliate );
}
void operator()( const affiliate_referral_payout_operation& op ) { }
void operator()( const withdraw_pbtc_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_address_create_operation& op )
{
_impacted.insert( op.payer );

View file

@ -3,12 +3,10 @@
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/witness_object.hpp>
#include <sidechain/btc_multisig_address.hpp>
#include <sidechain/bitcoin_address.hpp>
namespace graphene { namespace chain {
using namespace sidechain;
class bitcoin_address_object : public abstract_object<bitcoin_address_object>
{
public:
@ -19,14 +17,14 @@ class bitcoin_address_object : public abstract_object<bitcoin_address_object>
// multisig m-of-n (m = 5). Address is valid before count of changed witnesses < 5
bool valid() { return count_invalid_pub_key < 5; } // TODO: move to global_properties
std::string get_address() const { return address.base58_address; }
std::string get_address() const { return address.get_address(); }
void update_count_invalid_pub_key(const accounts_keys& incoming_wit_keys) {
count_invalid_pub_key = incoming_wit_keys.size() - address.count_intersection(incoming_wit_keys);
void update_count_invalid_pub_key( const sidechain::accounts_keys& incoming_wit_keys ) {
count_invalid_pub_key = incoming_wit_keys.size() - address.count_intersection( incoming_wit_keys );
}
account_id_type owner;
btc_multisig_segwit_address address;
sidechain::btc_multisig_segwit_address address;
uint8_t count_invalid_pub_key;
};

View file

@ -39,6 +39,7 @@
#include <fc/crypto/hash_ctr_rng.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <fc/log/logger.hpp>
@ -504,6 +505,13 @@ namespace graphene { namespace chain {
///@}
///@}
//////////////////// sidechain ////////////////////
public:
sidechain::input_withdrawal_info i_w_info;
private:
vector< processed_transaction > _pending_tx;
fork_database _fork_db;

View file

@ -2,6 +2,7 @@
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/generic_index.hpp>
#include <sidechain/types.hpp>
namespace graphene { namespace chain {
@ -21,10 +22,10 @@ class info_for_vout_object : public abstract_object<info_for_vout_object>
info_for_vout_id_type get_id()const { return id; }
account_id_type payer;
// btc::payment_type addr_type;
std::string data;
uint64_t amount;
account_id_type payer;
sidechain::payment_type addr_type;
std::string data;
uint64_t amount;
bool created = false;
};
@ -44,5 +45,5 @@ typedef generic_index<info_for_vout_object, info_for_vout_multi_index_container>
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(data)(amount)(created) )
FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(addr_type)(data)(amount)(created) )

View file

@ -44,6 +44,7 @@
#include <graphene/chain/protocol/event.hpp>
#include <graphene/chain/protocol/betting_market.hpp>
#include <graphene/chain/protocol/tournament.hpp>
#include <graphene/chain/protocol/withdraw_pbtc.hpp>
#include <graphene/chain/protocol/bitcoin_address.hpp>
namespace graphene { namespace chain {
@ -131,6 +132,7 @@ namespace graphene { namespace chain {
event_group_delete_operation,
affiliate_payout_operation, // VIRTUAL
affiliate_referral_payout_operation, // VIRTUAL
withdraw_pbtc_operation,
bitcoin_address_create_operation
> operation;

View file

@ -0,0 +1,33 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct withdraw_pbtc_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
uint32_t price_per_kbyte = 10;
};
asset fee;
account_id_type payer;
std::string data; // address or script
uint64_t amount;
// object_id_type tx_obj_id;
account_id_type fee_payer() const { return payer; }
void validate() const {}
share_type calculate_fee( const fee_parameters_type& k )const {
share_type fee_required = k.fee;
fee_required += calculate_data_fee( fc::raw::pack_size(*this), k.price_per_kbyte );
return fee_required;
}
};
} } // graphene::chain
FC_REFLECT( graphene::chain::withdraw_pbtc_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::withdraw_pbtc_operation, (fee)(payer)(data)(amount) )

View file

@ -0,0 +1,27 @@
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <sidechain/types.hpp>
using namespace sidechain;
namespace graphene { namespace chain {
class withdraw_pbtc_evaluator : public evaluator<withdraw_pbtc_evaluator>
{
public:
typedef withdraw_pbtc_operation operation_type;
void_result do_evaluate(const withdraw_pbtc_operation& op);
object_id_type do_apply(const withdraw_pbtc_operation& op);
void reserve_issue( const withdraw_pbtc_operation& op );
bool check_amount( const withdraw_pbtc_operation& op );
payment_type type;
};
} } // graphene::chain

View file

@ -0,0 +1,49 @@
#include <graphene/chain/withdraw_pbtc_evaluator.hpp>
#include <graphene/chain/info_for_vout_object.hpp>
#include <sidechain/bitcoin_address.hpp>
#include <sidechain/utils.hpp>
namespace graphene { namespace chain {
void_result withdraw_pbtc_evaluator::do_evaluate(const withdraw_pbtc_operation& op)
{
database& d = db();
// FC_ASSERT( !d.is_sidechain_fork_needed() );
FC_ASSERT( op.data.size() > 0 );
type = bitcoin_address( op.data ).get_type();
FC_ASSERT( type != payment_type::NULLDATA , "Invalid address type." );
FC_ASSERT( check_amount( op ) );
// asset acc_balance = db().get_balance( op.payer, d.get_sidechain_asset_id() );
// FC_ASSERT( acc_balance.amount.value >= op.amount );
return void_result();
}
object_id_type withdraw_pbtc_evaluator::do_apply(const withdraw_pbtc_operation& op)
{
database& d = db();
auto id = d.create<info_for_vout_object>( [&]( info_for_vout_object& obj ) {
obj.payer = op.payer;
obj.addr_type = type;
obj.data = op.data;
obj.amount = op.amount;
} ).get_id();
reserve_issue( op );
return id;
}
void withdraw_pbtc_evaluator::reserve_issue( const withdraw_pbtc_operation& op )
{
}
bool withdraw_pbtc_evaluator::check_amount( const withdraw_pbtc_operation& op ) {
return true;
}
} } // namespace graphene::chain

View 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 <sidechain/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 sidechain { 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

View file

@ -0,0 +1,222 @@
#include <sidechain/bitcoin_address.hpp>
#include <sstream>
#include <fc/crypto/base58.hpp>
#include <sidechain/segwit_addr.hpp>
namespace sidechain {
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( const payment_type& type )
{
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[keys_required - 1] );
for( const auto& key : witnesses_keys ) {
std::stringstream ss;
ss << std::hex << key.second.key_data.size();
auto key_size_hex = sidechain::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[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( sidechain::parse_hex( hash160.str() ) );
raw_address.push_back( OP_HASH160 );
std::stringstream ss;
ss << std::hex << temp_addr_hash.size();
auto address_size_hex = sidechain::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;
}
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;
}
}

View file

@ -1,105 +0,0 @@
#include <sidechain/btc_multisig_address.hpp>
#include <sstream>
#include <fc/crypto/base58.hpp>
namespace sidechain {
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();
}
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[keys_required - 1] );
for( const auto& key : witnesses_keys ) {
std::stringstream ss;
ss << std::hex << key.second.key_data.size();
auto key_size_hex = sidechain::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[witnesses_keys.size() - 1] );
redeem_script.push_back( OP_CHECKMULTISIG );
}
void btc_multisig_address::create_address()
{
FC_ASSERT( redeem_script.size() > 0 );
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() );
std::vector<char> temp_addr_hash( sidechain::parse_hex( hash160.str() ) );
address.push_back( OP_HASH160 );
std::stringstream ss;
ss << std::hex << temp_addr_hash.size();
auto address_size_hex = sidechain::parse_hex( ss.str() );
address.insert( address.end(), address_size_hex.begin(), address_size_hex.end() );
address.insert( address.end(), temp_addr_hash.begin(), temp_addr_hash.end() );
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();
}
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 ||
segwit_address != addr.segwit_address || base58_address != addr.base58_address )
return false;
return true;
}
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() );
segwit_address = std::vector<char>(hash160.data(), hash160.data() + hash160.data_size() );
base58_address = fc::to_base58( get_address_bytes( segwit_address ) );
}
std::vector<char> btc_multisig_segwit_address::get_address_bytes( const std::vector<char>& script_hash )
{
std::vector<char> 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;
}
}

View file

@ -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 sidechain { 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

View file

@ -0,0 +1,128 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/types.hpp>
#include <sidechain/utils.hpp>
using namespace graphene::chain;
namespace sidechain {
const bytes op = {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( type ) ) {}
payment_type get_type() const { return type; }
std::string get_address() const { return address; }
bytes get_raw_address() const { return raw_address; }
private:
enum size_segwit_address { P2WSH = 32, P2WPKH = 20 };
payment_type determine_type();
bytes determine_raw_address( const payment_type& type );
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; }
private:
void create_witness_script();
void create_segwit_address();
bytes get_address_bytes( const bytes& script_hash );
public:
bytes witness_script;
};
}
FC_REFLECT( sidechain::bitcoin_address, (address)(type)(raw_address) );
FC_REFLECT_DERIVED( sidechain::btc_multisig_address, (sidechain::bitcoin_address),
(redeem_script)(keys_required)(witnesses_keys) );
FC_REFLECT_DERIVED( sidechain::btc_multisig_segwit_address, (sidechain::btc_multisig_address), (witness_script) );

View file

@ -1,95 +0,0 @@
#pragma once
#include <vector>
#include <map>
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/utils.hpp>
using namespace graphene::chain;
namespace sidechain {
const std::vector<char> op = {0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f}; // OP_1 - OP_15
typedef std::map< account_id_type, public_key_type > accounts_keys;
class btc_multisig_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;
virtual std::vector< char > get_hex_address() { return address; }
std::vector< char > get_redeem_script() { 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 };
std::vector< char > address;
std::vector< char > 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;
std::vector< char > get_hex_address() override { return segwit_address; }
std::string get_base58_address() { return base58_address; }
std::vector< char > get_witness_script() { return witness_script; }
private:
void create_witness_script();
void create_segwit_address();
std::vector<char> get_address_bytes( const std::vector<char>& script_hash );
public:
std::vector< char > segwit_address;
std::vector< char > witness_script;
std::string base58_address;
};
}
FC_REFLECT( sidechain::btc_multisig_address, (address)(redeem_script)(keys_required)(witnesses_keys) );
FC_REFLECT_DERIVED( sidechain::btc_multisig_segwit_address, (sidechain::btc_multisig_address),
(segwit_address)(witness_script)(base58_address) );

View file

@ -1,12 +1,10 @@
#pragma once
#include <string>
#include <vector>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <sidechain/types.hpp>
#include <sidechain/thread_safe_index.hpp>
#include <fc/crypto/sha256.hpp>
@ -32,7 +30,7 @@ struct info_for_vin
{
info_for_vin() = default;
info_for_vin( const prev_out& _out, const std::string& _address, std::vector<char> _script = std::vector<char>() ) :
info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes() ) :
id( count_id_info_for_vin++ ), out( _out ), address( _address ), script( _script ) {
identifier = fc::sha256::hash( out.hash_tx + std::to_string( out.n_vout ) );
}
@ -48,7 +46,7 @@ struct info_for_vin
prev_out out;
std::string address;
std::vector<char> script;
bytes script;
bool created = false;
};
@ -77,7 +75,7 @@ public:
input_withdrawal_info( graphene::chain::database& _db ) : db( _db ) {}
void insert_info_for_vin( const prev_out& out, const std::string& address, std::vector<char> script = std::vector<char>() );
void insert_info_for_vin( const prev_out& out, const std::string& address, bytes script = bytes() );
void modify_info_for_vin( const info_for_vin& obj, const std::function<void( info_for_vin& e )>& func );
@ -92,7 +90,7 @@ public:
std::vector<info_for_vin> get_info_for_vins();
void insert_info_for_vout( const graphene::chain::account_id_type& payer, /*ayment_type addr_type,*/ const std::string& data, const uint64_t& amount );
void insert_info_for_vout( const graphene::chain::account_id_type& payer, const payment_type addr_type, const std::string& data, const uint64_t& amount );
void mark_as_used_vout( const graphene::chain::info_for_vout_object& obj );

View file

@ -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 sidechain { 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);
} }

View file

@ -0,0 +1,28 @@
#pragma once
#include <map>
#include <vector>
#include <string>
#include <fc/reflect/reflect.hpp>
#include <graphene/chain/account_object.hpp>
namespace sidechain {
using bytes = std::vector<char>;
using accounts_keys = std::map< graphene::chain::account_id_type, graphene::chain::public_key_type >;
enum class payment_type {
NULLDATA,
P2PK,
P2PKH,
P2SH,
P2WPKH,
P2WSH,
P2SH_WPKH,
P2SH_WSH
};
}
FC_REFLECT_ENUM( sidechain::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH) );

View file

@ -1,11 +1,10 @@
#pragma once
#include <string>
#include <vector>
#include <sidechain/types.hpp>
#include <fc/crypto/hex.hpp>
namespace sidechain {
std::vector<char> parse_hex( const std::string& str );
bytes parse_hex( const std::string& str );
}

View file

@ -13,7 +13,7 @@ bool info_for_vin::comparer::operator() ( const info_for_vin& lhs, const info_fo
return lhs.id < rhs.id;
}
void input_withdrawal_info::insert_info_for_vin( const prev_out& out, const std::string& address, std::vector<char> script )
void input_withdrawal_info::insert_info_for_vin( const prev_out& out, const std::string& address, bytes script )
{
info_for_vins.insert( info_for_vin( out, address, script ) );
}
@ -49,6 +49,7 @@ std::vector<info_for_vin> input_withdrawal_info::get_info_for_vins()
{
for( size_t i = 0; itr_b != itr_e && i < 5 && !itr_b->created; i++ ) { // 5 amount vins to bitcoin transaction
info_for_vin vin;
vin.id = itr_b->id;
vin.identifier = itr_b->identifier;
vin.out.hash_tx = itr_b->out.hash_tx;
vin.out.n_vout = itr_b->out.n_vout;
@ -64,11 +65,11 @@ std::vector<info_for_vin> input_withdrawal_info::get_info_for_vins()
return result;
}
void input_withdrawal_info::insert_info_for_vout( const graphene::chain::account_id_type& payer, /*ayment_type addr_type,*/ const std::string& data, const uint64_t& amount )
void input_withdrawal_info::insert_info_for_vout( const graphene::chain::account_id_type& payer, const payment_type addr_type, const std::string& data, const uint64_t& amount )
{
db.create<graphene::chain::info_for_vout_object>([&](graphene::chain::info_for_vout_object& obj) {
obj.payer = payer;
// obj.addr_type = addr_type;
obj.addr_type = addr_type;
obj.data = data;
obj.amount = amount;
});
@ -108,7 +109,7 @@ std::vector<info_for_vout> input_withdrawal_info::get_info_for_vouts()
for(size_t i = 0; i < 5 && itr != info_for_vout_idx.end() && !itr->created; i++) {
info_for_vout vout;
vout.payer = itr->payer;
// vout.addr_type = itr->addr_type;
vout.addr_type = itr->addr_type;
vout.data = itr->data;
vout.amount = itr->amount;

View file

@ -33,6 +33,10 @@ private:
void handle_block( const std::string& block_hash );
std::vector<info_for_vin> extract_info_from_block( const std::string& _block );
inline uint64_t parse_amount(std::string raw);
std::unique_ptr<zmq_listener> listener;
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
std::unique_ptr<graphene::chain::database> db;

View file

@ -1,6 +1,9 @@
#include <sidechain/network/sidechain_net_manager.hpp>
#include <thread>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
namespace sidechain {
sidechain_net_manager::sidechain_net_manager( graphene::chain::database* _db, std::string _ip,
@ -24,12 +27,55 @@ void sidechain_net_manager::initialize_manager( graphene::chain::database* _db,
} );
}
std::vector<info_for_vin> sidechain_net_manager::extract_info_from_block( const std::string& _block )
{
// std::map<std::string, bool> test = { {"2MsmG481n4W4AC1QcPgBUJQrVRTrLNM2GpB", false},
// {"2N2LkZG2Zp9eXGjSJzP9taR1gYdZBPzH7S7", false},
// {"2N4MCW3XggAxs9C8Dh2vNcx7uH8zUqoLMjA", false},
// {"2NEkNoDyQ9tyREFDAPehi9Jr2m6EFiTDxAS", false},
// {"2N1rQcKr4F14L8dfnNiUVpc4LzcZTWN9Kpd", false} };
std::stringstream ss( _block );
boost::property_tree::ptree block;
boost::property_tree::read_json( ss, block );
std::vector<info_for_vin> result;
for (const auto& tx_child : block.get_child("tx")) {
const auto& tx = tx_child.second;
for ( const auto& o : tx.get_child("vout") ) {
const auto script = o.second.get_child("scriptPubKey");
if( !script.count("addresses") ) continue;
for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses?
const auto address_base58 = addr.second.get_value<std::string>();
// if( !test.count( address_base58 ) ) continue; // there is such an address in graphene
info_for_vin vin;
vin.out.hash_tx = tx.get_child("txid").get_value<std::string>();
vin.out.amount = parse_amount( o.second.get_child( "value" ).get_value<std::string>() );
vin.out.n_vout = o.second.get_child( "n" ).get_value<uint32_t>();
vin.address = address_base58;
result.push_back( vin );
}
}
}
return result;
}
void sidechain_net_manager::update_tx_infos( const std::string& block_hash )
{
std::string block = bitcoin_client->receive_full_block( block_hash );
if( block != "" ) {
const auto& vins = extract_info_from_block( block );
for( const auto& v : vins ) {
db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address );
}
}
}
@ -60,4 +106,10 @@ void sidechain_net_manager::handle_block( const std::string& block_hash )
update_tx_infos( block_hash );
}
// Removes dot from amount output: "50.00000000"
inline uint64_t sidechain_net_manager::parse_amount(std::string raw) {
raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end());
return std::stoll(raw);
}
}

View 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 <sidechain/segwit_addr.hpp>
#include <sidechain/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 sidechain { 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 = sidechain::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 = sidechain::bech32::Encode(hrp, enc);
if (decode(hrp, ret).first == -1) return "";
return ret;
}
} }

View file

@ -2,9 +2,9 @@
namespace sidechain {
std::vector<char> parse_hex( const std::string& str )
bytes parse_hex( const std::string& str )
{
std::vector<char> vec( str.size() / 2 );
bytes vec( str.size() / 2 );
fc::from_hex( str, vec.data(), vec.size() );
return vec;
}

View file

@ -767,6 +767,8 @@ class wallet_api
string memo,
bool broadcast = false);
signed_transaction withdraw_pBTC(account_id_type payer, string to, uint64_t amount, bool broadcast = false);
/**
* This method works just like transfer, except it always broadcasts and
* returns the transaction ID along with the signed transaction.
@ -2057,4 +2059,5 @@ FC_API( graphene::wallet::wallet_api,
(get_binned_order_book)
(get_matched_bets_for_bettor)
(get_all_matched_bets_for_bettor)
(withdraw_pBTC)
)

View file

@ -2392,6 +2392,23 @@ public:
return sign_transaction(tx, broadcast);
} FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(asset_symbol)(memo)(broadcast) ) }
signed_transaction withdraw_pBTC(account_id_type payer, string to, uint64_t amount, bool broadcast)
{ try {
FC_ASSERT( !is_locked() );
withdraw_pbtc_operation withdraw_pbtc_op;
withdraw_pbtc_op.payer = payer;
withdraw_pbtc_op.data = to;
withdraw_pbtc_op.amount = amount;
signed_transaction tx;
tx.operations.push_back(withdraw_pbtc_op);
set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees);
tx.validate();
return sign_transaction(tx, broadcast);
} FC_CAPTURE_AND_RETHROW( (payer)(to)(amount)(broadcast) ) }
signed_transaction issue_asset(string to_account, string amount, string symbol,
string memo, bool broadcast = false)
{
@ -3870,6 +3887,12 @@ signed_transaction wallet_api::transfer(string from, string to, string amount,
{
return my->transfer(from, to, amount, asset_symbol, memo, broadcast);
}
signed_transaction wallet_api::withdraw_pBTC(account_id_type payer, string to, uint64_t amount, bool broadcast)
{
return my->withdraw_pBTC(payer, to, amount, broadcast);
}
signed_transaction wallet_api::create_asset(string issuer,
string symbol,
uint8_t precision,

View file

@ -0,0 +1,29 @@
#include <boost/test/unit_test.hpp>
#include "../common/database_fixture.hpp"
#include <fc/crypto/digest.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
using namespace graphene::chain;
BOOST_FIXTURE_TEST_SUITE( bitcoin_addresses_obj_tests, database_fixture )
BOOST_AUTO_TEST_CASE( create_bitcoin_address_test ) {
transaction_evaluation_state context(&db);
bitcoin_address_create_operation op;
op.payer = account_id_type();
op.owner = account_id_type();
const auto& idx = db.get_index_type<bitcoin_address_index>().indices().get< by_id >();
BOOST_CHECK( idx.size() == 0 );
db.apply_operation( context, op );
auto btc_address = idx.begin();
BOOST_CHECK(btc_address->count_invalid_pub_key == 1);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,132 @@
#include <boost/test/unit_test.hpp>
#include <sidechain/bitcoin_address.hpp>
using namespace sidechain;
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 );
}
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 );
sidechain::btc_multisig_address cma(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_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 );
sidechain::btc_multisig_segwit_address address(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
BOOST_CHECK( address.get_witness_script() == witness_script );
BOOST_CHECK( address.get_address() == "2NGU4ogScHEHEpReUzi9RB2ha58KAFnkFyk" );
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -1,55 +0,0 @@
#include <boost/test/unit_test.hpp>
#include <fc/crypto/digest.hpp>
#include <sidechain/btc_multisig_address.hpp>
using namespace sidechain;
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_SUITE( btc_multisig_address_tests )
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 );
sidechain::btc_multisig_segwit_address cma(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
BOOST_CHECK( address == cma.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 );
sidechain::btc_multisig_segwit_address address(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
BOOST_CHECK( address.get_witness_script() == witness_script );
BOOST_CHECK( address.get_base58_address() == "2NGU4ogScHEHEpReUzi9RB2ha58KAFnkFyk" );
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -1,47 +1,49 @@
#include <boost/test/unit_test.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include "../common/database_fixture.hpp"
#include <sidechain/types.hpp>
using namespace graphene::chain;
using namespace sidechain;
BOOST_FIXTURE_TEST_SUITE( input_withdrawal_info_tests, database_fixture )
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vin_test ) {
sidechain::input_withdrawal_info infos( db );
sidechain::prev_out out = { "1", 1, 13 };
input_withdrawal_info infos( db );
prev_out out = { "1", 1, 13 };
infos.insert_info_for_vin( out, "addr1", { 0x01, 0x02, 0x03 } );
BOOST_CHECK( infos.size_info_for_vins() == 1 );
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vin_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 1; i <= 10; i++ ) {
sidechain::prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
}
BOOST_CHECK( infos.size_info_for_vins() == 10 );
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_id_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
sidechain::prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).first );
BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->id == i );
}
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
sidechain::prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
}
@ -56,13 +58,13 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test ) {
BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->script == script );
}
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
sidechain::prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
}
@ -70,7 +72,7 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test ) {
if( i % 2 == 0 ) {
auto iter = infos.find_info_for_vin( static_cast< uint64_t >( i ) );
BOOST_CHECK( iter.first );
infos.modify_info_for_vin( *iter.second, [&]( sidechain::info_for_vin& obj ) {
infos.modify_info_for_vin( *iter.second, [&]( info_for_vin& obj ) {
obj.out.hash_tx = std::to_string( i + 1 );
obj.out.n_vout = i + 1;
obj.out.amount = i + 1;
@ -92,13 +94,13 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test ) {
}
}
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
sidechain::prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
}
@ -118,13 +120,13 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test ) {
}
}
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
sidechain::prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
prev_out out = { std::to_string( i ), static_cast<uint32_t>( i ), static_cast< uint64_t >( i ) };
infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } );
}
@ -139,27 +141,27 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test ) {
const auto& vins2 = infos.get_info_for_vins();
BOOST_CHECK( vins2.size() == 3 );
sidechain::info_for_vin::count_id_info_for_vin = 0;
info_for_vin::count_id_info_for_vin = 0;
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vout_test ) {
sidechain::input_withdrawal_info infos( db );
infos.insert_info_for_vout( account_id_type(), "1", 1 );
input_withdrawal_info infos( db );
infos.insert_info_for_vout( account_id_type(), payment_type::NULLDATA, "1", 1 );
BOOST_CHECK( infos.size_info_for_vouts() == 1 );
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 1; i <= 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
}
BOOST_CHECK( infos.size_info_for_vouts() == 10 );
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
}
for( size_t i = 0; i < 10; i++ ) {
@ -180,9 +182,9 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test ) {
}
BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vouts_test ) {
sidechain::input_withdrawal_info infos( db );
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
}
const auto& vouts = infos.get_info_for_vouts();