The mechanism for creating a thickening transaction and calculating the commission.

This commit is contained in:
Anzhy Cherrnyavski 2019-01-18 15:37:22 +03:00
parent 67486275c1
commit fd5a650eeb
18 changed files with 1345 additions and 32 deletions

View file

@ -3,6 +3,7 @@
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/generic_index.hpp>
#include <sidechain/types.hpp>
#include <sidechain/bitcoin_address.hpp>
namespace graphene { namespace chain {
@ -22,10 +23,9 @@ 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;
sidechain::payment_type addr_type;
std::string data;
uint64_t amount;
account_id_type payer;
sidechain::bitcoin_address address;
uint64_t amount;
bool created = false;
};
@ -45,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)(addr_type)(data)(amount)(created) )
FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(address)(amount)(created) )

View file

@ -15,7 +15,7 @@ public:
void_result do_evaluate(const withdraw_pbtc_operation& op);
object_id_type do_apply(const withdraw_pbtc_operation& op);
void_result do_apply(const withdraw_pbtc_operation& op);
void reserve_issue( const withdraw_pbtc_operation& op );

View file

@ -21,20 +21,11 @@ void_result withdraw_pbtc_evaluator::do_evaluate(const withdraw_pbtc_operation&
return void_result();
}
object_id_type withdraw_pbtc_evaluator::do_apply(const withdraw_pbtc_operation& op)
void_result 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();
db().i_w_info.insert_info_for_vout( op.payer, op.data, op.amount );
reserve_issue( op );
return id;
return void_result();
}
void withdraw_pbtc_evaluator::reserve_issue( const withdraw_pbtc_operation& op )

View file

@ -146,7 +146,7 @@ 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] );
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();
@ -154,7 +154,7 @@ void btc_multisig_address::create_redeem_script()
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_num[witnesses_keys.size() - 1] );
redeem_script.push_back( OP_CHECKMULTISIG );
}

View file

@ -0,0 +1,60 @@
#include <sidechain/bitcoin_script.hpp>
#include <sidechain/serialize.hpp>
namespace sidechain {
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 )
{
sidechain::write_compact_size( script, size );
return *this;
}
script_builder& script_builder::operator<<( const bytes& sc ) {
sidechain::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 )
{
sidechain::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 )
{
sidechain::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 )
{
sidechain::write_compact_size( script, pubkey_data.size() );
script.insert( script.end(), pubkey_data.begin(), pubkey_data.begin() + pubkey_data.size() );
return *this;
}
}

View file

@ -0,0 +1,235 @@
#include <sidechain/bitcoin_transaction.hpp>
#include <fc/crypto/base58.hpp>
#include <sidechain/bitcoin_script.hpp>
namespace sidechain {
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;
}
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;
}
}
}
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;
}
}
}

View file

@ -8,7 +8,7 @@ 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
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
{

View file

@ -0,0 +1,69 @@
#pragma once
#include <sidechain/types.hpp>
namespace sidechain {
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;
};
}

View file

@ -0,0 +1,247 @@
#pragma once
#include <sidechain/bitcoin_address.hpp>
#include <sidechain/serialize.hpp>
namespace sidechain {
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 ) {}
};
struct tx_in {
out_point prevout;
bytes scriptSig;
uint32_t nSequence = 0xffffffff;
std::vector<bytes> scriptWitness;
};
struct tx_out {
int64_t value = 0;
bytes scriptPubKey;
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 {
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;
};
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" ) ) }
}
FC_REFLECT( sidechain::out_point, (hash)(n) )
FC_REFLECT( sidechain::tx_in, (prevout)(scriptSig)(nSequence)(scriptWitness) )
FC_REFLECT( sidechain::tx_out, (value)(scriptPubKey) )
FC_REFLECT( sidechain::bitcoin_transaction, (nVersion)(vin)(vout)(nLockTime) )

View file

@ -13,7 +13,6 @@
using boost::multi_index_container;
using namespace boost::multi_index;
using info_for_vout = graphene::chain::info_for_vout_object;
namespace graphene { namespace chain { class database; } }
@ -83,7 +82,7 @@ public:
std::vector<info_for_vin> get_info_for_vins();
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 insert_info_for_vout( const graphene::chain::account_id_type& payer, 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,104 @@
#pragma once
#include <sidechain/types.hpp>
namespace sidechain {
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);
}
}

View file

@ -0,0 +1,74 @@
#pragma once
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/bitcoin_transaction.hpp>
using namespace sidechain;
namespace sidechain {
// void example()
// {
// std::vector<info_for_vin> vin_infos { ... };
// std::vector<info_for_vout> vout_infos { ... };
// sidechain_condensing_tx sct( vin_infos, vout_infos );
// if( pw_vin_info ) {
// sct.create_pw_vin( pw_vin_info );
// }
// if( vout_infos.size() ) {
// sct.create_vouts_for_witness_fee( keys );
// }
// if( ( vin_infos.sum() - vout_infos.sum() ) > 0 ) {
// sct.create_pw_vout( vin_infos.sum() - vout_infos.sum(), bytes{ 0x0d, 0x0d, 0x0d } );
// }
// }
class sidechain_condensing_tx
{
public:
sidechain_condensing_tx( const std::vector<info_for_vin>& vin_info, const std::vector<info_for_vout>& vout_info );
void create_pw_vin( const info_for_vin& vin_info );
void create_pw_vout( const uint64_t amount, const bytes& wit_script_out );
void create_vouts_for_witness_fee( const accounts_keys& witness_active_keys );
uint64_t get_estimate_tx_size( size_t number_witness ) const;
void subtract_fee( const uint64_t& fee, const double& witness_percentage );
bitcoin_transaction get_transaction() const { return tb.get_transaction(); }
uint64_t get_amount_vins() { return amount_vins; }
uint64_t get_amount_transfer_to_bitcoin() { return amount_transfer_to_bitcoin; }
private:
void create_vins_for_condensing_tx( const std::vector<info_for_vin>& vin_info );
void create_vouts_for_condensing_tx( const std::vector<info_for_vout>& vout_info );
uint32_t count_transfer_vin = 0;
uint32_t count_transfer_vout = 0;
uint32_t count_witness_vout = 0;
uint64_t amount_vins = 0;
uint64_t amount_transfer_to_bitcoin = 0;
bitcoin_transaction_builder tb;
};
int64_t get_estimated_fee( size_t tx_vsize, uint64_t estimated_feerate ); // move db_sidechain
}

View file

@ -11,6 +11,7 @@ namespace sidechain {
using bytes = std::vector<char>;
using accounts_keys = std::map< graphene::chain::account_id_type, graphene::chain::public_key_type >;
using info_for_vout = graphene::chain::info_for_vout_object;
enum class payment_type
{

View file

@ -65,12 +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, const payment_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 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.data = data;
obj.address = bitcoin_address( data );
obj.amount = amount;
});
}
@ -109,8 +108,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.data = itr->data;
vout.address = itr->address;
vout.amount = itr->amount;
result.push_back( vout );

View file

@ -0,0 +1,113 @@
#include <sidechain/sidechain_condensing_tx.hpp>
namespace sidechain {
sidechain_condensing_tx::sidechain_condensing_tx( const std::vector<info_for_vin>& vin_info, const std::vector<info_for_vout>& vout_info )
{
create_vins_for_condensing_tx( vin_info );
create_vouts_for_condensing_tx( vout_info );
}
void sidechain_condensing_tx::create_pw_vin( const info_for_vin& vin_info )
{
bytes witness_script_temp = {0x22};
witness_script_temp.insert( witness_script_temp.end(), vin_info.script.begin(), vin_info.script.end() );
tb.add_in( payment_type::P2WSH, fc::sha256( vin_info.out.hash_tx ), vin_info.out.n_vout, witness_script_temp, true );
amount_vins += vin_info.out.amount;
}
void sidechain_condensing_tx::create_pw_vout( const uint64_t amount, const bytes& wit_script_out )
{
tb.add_out( payment_type::P2WSH, amount, bytes(wit_script_out.begin() + 2, wit_script_out.end()), true );
}
void sidechain_condensing_tx::create_vins_for_condensing_tx( const std::vector<info_for_vin>& vin_info )
{
for( const auto& info : vin_info ) {
bytes witness_script_temp = {0x22};
witness_script_temp.insert( witness_script_temp.end(), info.script.begin(), info.script.end() );
tb.add_in( payment_type::P2SH_WSH, fc::sha256( info.out.hash_tx ), info.out.n_vout, witness_script_temp );
amount_vins += info.out.amount;
count_transfer_vin++;
}
}
void sidechain_condensing_tx::create_vouts_for_condensing_tx( const std::vector<info_for_vout>& vout_info )
{
for( const auto& info : vout_info ) {
tb.add_out_all_type( info.amount, info.address );
amount_transfer_to_bitcoin += info.amount;
count_transfer_vout++;
}
}
void sidechain_condensing_tx::create_vouts_for_witness_fee( const accounts_keys& witness_active_keys )
{
for( auto& key : witness_active_keys ) {
tb.add_out( payment_type::P2PK, 0, key.second, true );
count_witness_vout++;
}
}
uint64_t sidechain_condensing_tx::get_estimate_tx_size( size_t number_witness ) const
{
bytes temp_sig(72, 0x00);
bytes temp_key(34, 0x00);
bytes temp_script(3, 0x00);
for(size_t i = 0; i < number_witness; i++) {
temp_script.insert(temp_script.begin() + 1, temp_key.begin(), temp_key.end());
}
std::vector<bytes> temp_scriptWitness = { {},{temp_sig},{temp_sig},{temp_sig},{temp_sig},{temp_sig},{temp_script} };
bitcoin_transaction temp_tx = get_transaction();
for( auto& vin : temp_tx.vin ) {
vin.scriptWitness = temp_scriptWitness;
}
return temp_tx.get_vsize();
}
void sidechain_condensing_tx::subtract_fee( const uint64_t& fee, const double& witness_percentage )
{
bitcoin_transaction tx = get_transaction();
uint64_t fee_size = fee / ( count_transfer_vin + count_transfer_vout );
if( fee % ( count_transfer_vin + count_transfer_vout ) != 0 ) {
fee_size += 1;
}
uint64_t fee_witnesses = 0;
size_t offset = tx.vout.size() > ( count_witness_vout + count_transfer_vout ) ? 1 + count_witness_vout : count_witness_vout;
for( ; offset < tx.vout.size(); offset++ ) {
uint64_t amount_without_fee_size = tx.vout[offset].value - fee_size;
uint64_t amount_fee_witness = amount_without_fee_size * witness_percentage;
tx.vout[offset].value = amount_without_fee_size;
tx.vout[offset].value -= amount_fee_witness;
fee_witnesses += amount_fee_witness;
}
if( count_witness_vout > 0 ) {
uint64_t fee_witness = fee_witnesses / count_witness_vout;
size_t limit = tx.vout.size() > ( count_witness_vout + count_transfer_vout ) ? 1 + count_witness_vout : count_witness_vout;
offset = tx.vout.size() > ( count_witness_vout + count_transfer_vout ) ? 1 : 0;
FC_ASSERT( fee_witness > 0 );
for( ; offset < limit; offset++ ) {
tx.vout[offset].value += fee_witness;
}
}
tb = std::move( bitcoin_transaction_builder( tx ) );
}
int64_t get_estimated_fee( size_t tx_vsize, uint64_t estimated_feerate ) {
static const uint64_t default_feerate = 1000;
static const uint64_t min_relay_fee = 1000;
const auto feerate = std::max( default_feerate, estimated_feerate );
const auto fee = feerate * int64_t( tx_vsize ) / 1000;
return std::max( min_relay_fee, fee );
}
}

View file

@ -0,0 +1,168 @@
#include <boost/test/unit_test.hpp>
#include <sidechain/utils.hpp>
#include <sidechain/bitcoin_transaction.hpp>
using namespace sidechain;
BOOST_AUTO_TEST_SUITE( bitcoin_transaction_tests )
BOOST_AUTO_TEST_CASE( serialize_bitcoin_transaction_test )
{
out_point prevout;
prevout.hash = fc::sha256( "89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a" );
prevout.n = 0;
tx_in in;
in.prevout = prevout;
in.scriptSig = parse_hex( "473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101" );
in.nSequence = 4294967294;
tx_out out1;
out1.value = 3500000000;
out1.scriptPubKey = parse_hex( "76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac" );
tx_out out2;
out2.value = 1499996160;
out2.scriptPubKey = parse_hex( "76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac" );
bitcoin_transaction tx;
tx.nVersion = 2;
tx.vin = { in };
tx.vout = { out1, out2 };
tx.nLockTime = 101;
const auto serialized = pack( tx );
const auto expected = parse_hex( "02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000" );
BOOST_CHECK_EQUAL_COLLECTIONS( serialized.cbegin(), serialized.cend(), expected.cbegin(), expected.cend() );
}
BOOST_AUTO_TEST_CASE( deserialize_bitcoin_transaction_test )
{
bitcoin_transaction tx = unpack(parse_hex( "02000000019a4b71b82745879f7a5ef5081bceb3eb53dac44ef272fcdf00fdc1bd162edf890000000048473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101feffffff0200c39dd0000000001976a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac00206859000000001976a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac65000000" ) );
BOOST_CHECK_EQUAL( tx.nVersion, 2 );
BOOST_CHECK_EQUAL( tx.nLockTime, 101 );
BOOST_REQUIRE_EQUAL( tx.vin.size(), 1 );
BOOST_CHECK_EQUAL( tx.vin[0].prevout.hash.str(), "89df2e16bdc1fd00dffc72f24ec4da53ebb3ce1b08f55e7a9f874527b8714b9a" );
BOOST_CHECK_EQUAL( tx.vin[0].prevout.n, 0 );
BOOST_CHECK( fc::to_hex( tx.vin[0].scriptSig) == "473044022025f722981037f23949df7ac020e9895f6b4d35f98d04dadc43a8897d756b02f202203d4df0a81dac49be676657357f083d06f57b2d6b1199511ebdbd3bf82feff24101" );
BOOST_CHECK_EQUAL( tx.vin[0].nSequence, 4294967294 );
BOOST_REQUIRE_EQUAL( tx.vout.size(), 2 );
BOOST_CHECK_EQUAL( tx.vout[0].value, 3500000000 );
BOOST_CHECK( fc::to_hex( tx.vout[0].scriptPubKey ) == "76a914d377a5fd22419f8180f6d0d12215daffdd15b80088ac" );
BOOST_CHECK_EQUAL(tx.vout[1].value, 1499996160);
BOOST_CHECK( fc::to_hex( tx.vout[1].scriptPubKey ) == "76a914e71562730a2d7b2c2c7f2f137d6ddf80e8ee024288ac" );
}
BOOST_AUTO_TEST_CASE( btc_tx_methods_test ) {
const auto tx = unpack( parse_hex( "0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f856040000002322002001d5d92effa6ffba3efa379f9830d0f75618b13393827152d26e4309000e88b1ffffffff0188b3f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02473044022038421164c6468c63dc7bf724aa9d48d8e5abe3935564d38182addf733ad4cd81022076362326b22dd7bfaf211d5b17220723659e4fe3359740ced5762d0e497b7dcc012321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac00000000" ) );
BOOST_CHECK( tx.get_hash().str() == "a5947589e2762107ff650958ba0e3a3cf341f53281d15593530bf9762c4edab1" );
BOOST_CHECK( tx.get_txid().str() == "954f43dbb30ad8024981c07d1f5eb6c9fd461e2cf1760dd1283f052af746fc88" );
BOOST_CHECK( tx.get_vsize() == 148 );
}
BOOST_AUTO_TEST_CASE( bitcoin_transaction_builder_test )
{
// All tests are only to verefy the compilation of transactions, the transactions are not real(not valid)
{ // P2PKH to P2PKH
bitcoin_transaction_builder tb;
tb.set_version( 2 );
tb.add_in( payment_type::P2PKH, fc::sha256( "5d42b45d5a3ddcf2421b208885871121551acf6ea5cc1c1b4e666537ab6fcbef" ), 0, bytes() );
tb.add_out( payment_type::P2PKH, 4999990000, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex(pack( tx ) ) == "0200000001efcb6fab3765664e1b1ccca56ecf1a552111878588201b42f2dc3d5a5db4425d0000000000ffffffff01f0ca052a010000001976a9143307bf6f98832e53a48b144d65c6a95700a93ffb88ac00000000");
}
{ // coinbase to P2PK
const auto pubkey = fc::raw::unpack<fc::ecc::public_key_data>( parse_hex( "02028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00" ) );
out_point prevout;
prevout.hash = fc::sha256( "0000000000000000000000000000000000000000000000000000000000000000" );
prevout.n = 0xffffffff;
tx_in txin;
txin.prevout = prevout;
bitcoin_transaction_builder tb;
tb.set_version( 2 );
tb.add_in( payment_type::P2SH, txin, parse_hex( "022a020101" ) );
tb.add_out( payment_type::P2PK, 625000000, pubkey);
tb.add_out( payment_type::NULLDATA, 0, parse_hex( "21030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac" ) );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff05022a020101ffffffff0240be402500000000232102028322f70f9bf4a014fb6422f555b05d605229460259c157b3fe34b7695f2d00ac0000000000000000256a2321030e7061b9fb18571cf2441b2a7ee2419933ddaa423bc178672cd11e87911616d1ac00000000" );
}
{ // P2SH to P2SH
bitcoin_transaction_builder tb;
tb.set_version( 2 );
tb.add_in( payment_type::P2SH, fc::sha256( "40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8" ), 0, bytes() );
tb.add_out( payment_type::P2SH, 0xffffffff, "3P14159f73E4gFr7JterCCQh9QjiTjiZrG" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "0200000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee400000000000ffffffff01ffffffff0000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a8700000000" );
}
{ // P2PK to NULLDATA
bitcoin_transaction_builder tb;
tb.set_version( 2 );
tb.add_in( payment_type::P2PK, fc::sha256( "fa897a4a2b8bc507db6cf4425e81ca7ebde89a369e07d608ac7f7c311cb13b4f" ), 0, bytes() );
tb.add_out( payment_type::NULLDATA, 0, parse_hex( "ffffffff" ) );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000014f3bb11c317c7fac08d6079e369ae8bd7eca815e42f46cdb07c58b2b4a7a89fa0000000000ffffffff010000000000000000066a04ffffffff00000000" );
}
{ // P2PK+P2PKH to P2PKH,P2PKH
bitcoin_transaction_builder tb;
tb.set_version(2);
tb.add_in( payment_type::P2PK, fc::sha256( "13d29149e08b6ca63263f3dddd303b32f5aab646ebc6b7db84756d80a227f6d9" ), 0, bytes() );
tb.add_in( payment_type::P2PKH, fc::sha256( "a8e7f661925cdd2c0e37fc93c03540c113aa6bcea02b35de09377127f76d0da3" ), 0, bytes() );
tb.add_out( payment_type::P2PKH, 4999990000, "mzg9RZ1p29uNXu4uTWoMdMERdVXZpunJhW" );
tb.add_out( payment_type::P2PKH, 4999990000, "n2SPW6abRxUnnTSSHp73VGahbPW4WT9GaK" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "0200000002d9f627a2806d7584dbb7c6eb46b6aaf5323b30ddddf36332a66c8be04991d2130000000000ffffffffa30d6df727713709de352ba0ce6baa13c14035c093fc370e2cdd5c9261f6e7a80000000000ffffffff02f0ca052a010000001976a914d2276c7ed7af07f697175cc2cbcbbf32da81caba88acf0ca052a010000001976a914e57d9a9af070998bedce991c4d8e39f9c51eb93a88ac00000000" );
}
{ // P2WPKH to P2WPKH
bitcoin_transaction_builder tb;
tb.set_version(2);
tb.add_in( payment_type::P2WPKH, fc::sha256( "56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115" ), 1, bytes() );
tb.add_out( payment_type::P2WPKH, 99988480, "mkAn3ASzVBTLMbaLf2YTfcotdQ8hSbKD14" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001600143307bf6f98832e53a48b144d65c6a95700a93ffb00000000" );
}
{ // P2WSH to P2WSH
bitcoin_transaction_builder tb;
tb.set_version(2);
tb.add_in( payment_type::P2WSH, fc::sha256( "56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115"), 2, bytes() );
tb.add_out( payment_type::P2WSH, 99988360, "p2xtZoXeX5X8BP8JfFhQK2nD3emtjch7UeFm" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560200000000ffffffff0188b3f505000000001600140000010966776006953d5567439e5e39f86a0d2700000000" );
}
{ // P2SH(WPKH) to P2SH(WPKH)
bitcoin_transaction_builder tb;
tb.set_version( 2 );
tb.add_in( payment_type::P2SH_WPKH, fc::sha256( "56f87210814c8baef7068454e517a70da2f2103fc3ac7f687e32a228dc80e115" ), 3, parse_hex( "ab68025513c3dbd2f7b92a94e0581f5d50f654e7" ) );
tb.add_out( payment_type::P2SH_WPKH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "020000000115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560300000014ab68025513c3dbd2f7b92a94e0581f5d50f654e7ffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000" );
}
{ // P2SH(WSH) to P2SH(WSH)
bitcoin_transaction_builder tb;
tb.set_version(2);
const auto redeem_script = parse_hex( "21038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acac");
tb.add_in( payment_type::P2SH_WSH, fc::sha256( "fca01bd539623013f6f945dc6173c395394621ffaa53a9eb6da6e9a2e7c9400e" ), 0, redeem_script );
tb.add_out( payment_type::P2SH_WSH, 99987100, "3Mwz6cg8Fz81B7ukexK8u8EVAW2yymgWNd" );
const auto tx = tb.get_transaction();
BOOST_CHECK( fc::to_hex( pack( tx ) ) == "02000000010e40c9e7a2e9a66deba953aaff21463995c37361dc45f9f613306239d51ba0fc000000002321038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990acacffffffff019caef5050000000017a914de373b053abb48ec078cf5f41b42aedac0103e278700000000" );
}
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -154,7 +154,7 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test )
BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vout_test )
{
input_withdrawal_info infos( db );
infos.insert_info_for_vout( account_id_type(), payment_type::NULLDATA, "1", 1 );
infos.insert_info_for_vout( account_id_type(), "1", 1 );
BOOST_CHECK( infos.size_info_for_vouts() == 1 );
}
@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test )
{
input_withdrawal_info infos( db );
for( size_t i = 1; i <= 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
}
BOOST_CHECK( infos.size_info_for_vouts() == 10 );
}
@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
}
for( size_t i = 0; i < 10; i++ ) {
@ -195,7 +195,7 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vouts_test )
{
input_withdrawal_info infos( db );
for( size_t i = 0; i < 10; i++ ) {
infos.insert_info_for_vout( account_id_type(i), payment_type::NULLDATA, std::to_string( i ), static_cast<uint64_t>( i ) );
infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast<uint64_t>( i ) );
}
const auto& vouts = infos.get_info_for_vouts();

View file

@ -0,0 +1,254 @@
#include <boost/test/unit_test.hpp>
#include <sidechain/sidechain_condensing_tx.hpp>
#include <sidechain/bitcoin_script.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/digest.hpp>
#include <iostream>
#include <algorithm>
BOOST_AUTO_TEST_SUITE( sidechain_condensing_tx_tests )
uint64_t size_fee = 100;
uint64_t pw_vout_amount = 113;
double witness_percentage = 0.01;
void create_info_vins_and_info_vouts( std::vector<info_for_vin>& info_vins, std::vector<info_for_vout>& info_vouts )
{
for( size_t i = 0; i < 10; i++ ) {
info_for_vin vin;
vin.out.hash_tx = "1111111111111111111111111111111111111111111111111111111111111111";
vin.out.n_vout = static_cast<uint32_t>( i );
vin.out.amount = static_cast<uint64_t>( i + 1000 );
vin.address = std::to_string( i );
vin.script = { 0x0d };
info_vins.push_back( vin );
info_for_vout vout;
vout.payer = account_id_type( i );
vout.address = sidechain::bitcoin_address( "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" );
vout.amount = static_cast<uint64_t>( i + 1000 );
info_vouts.push_back( vout );
}
}
info_for_vin create_pw_vin()
{
info_for_vin vin_info;
vin_info.out.hash_tx = "2222222222222222222222222222222222222222222222222222222222222222";
vin_info.out.n_vout = static_cast<uint32_t>( 13 );
vin_info.out.amount = static_cast<uint64_t>( 1113 );
vin_info.address = std::to_string( 13 );
vin_info.script = { 0x0d };
return vin_info;
}
accounts_keys create_accounts_keys()
{
fc::ecc::private_key priv_key = fc::ecc::private_key::regenerate( fc::digest( "key" ) );
return { { account_id_type( 0 ), public_key_type( priv_key.get_public_key() ) },
{ account_id_type( 1 ), public_key_type( priv_key.get_public_key() ) },
{ account_id_type( 2 ), public_key_type( priv_key.get_public_key() ) } };
}
BOOST_AUTO_TEST_CASE( create_sidechain_condensing_tx_test )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
create_info_vins_and_info_vouts( info_vins, info_vouts );
sidechain_condensing_tx ct( info_vins, info_vouts );
const auto& tx = ct.get_transaction();
BOOST_CHECK( tx.vin.size() == 10 );
BOOST_CHECK( tx.vout.size() == 10 );
for( size_t i = 0; i < 10; i++ ) {
BOOST_CHECK( tx.vin[i].prevout.hash == fc::sha256( "1111111111111111111111111111111111111111111111111111111111111111" ) );
BOOST_CHECK( tx.vin[i].prevout.n == static_cast<uint32_t>( i ) );
bytes scriptSig = { 0x22, 0x0d };
BOOST_CHECK( tx.vin[i].scriptSig == scriptSig );
BOOST_CHECK( tx.vin[i].scriptWitness == std::vector<bytes>() );
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( i + 1000 ) );
const auto address_bytes = fc::from_base58( "mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn" );
bytes raw_address( address_bytes.begin() + 1, address_bytes.begin() + 21 );
bytes scriptPubKey = script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG;
BOOST_CHECK( tx.vout[i].scriptPubKey == scriptPubKey );
}
}
BOOST_AUTO_TEST_CASE( creation_additional_in_out_sidechain_condensing_tx )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
create_info_vins_and_info_vouts( info_vins, info_vouts );
accounts_keys keys = create_accounts_keys();
sidechain_condensing_tx ct( info_vins, info_vouts );
ct.create_pw_vin( create_pw_vin() );
ct.create_vouts_for_witness_fee( keys );
ct.create_pw_vout( 1113, bytes{ 0x0d, 0x0d, 0x0d } );
const auto& tx = ct.get_transaction();
BOOST_CHECK( tx.vin.size() == 11 );
BOOST_CHECK( tx.vout.size() == 14 );
BOOST_CHECK( tx.vin[0].prevout.hash == fc::sha256( "2222222222222222222222222222222222222222222222222222222222222222" ) );
BOOST_CHECK( tx.vin[0].prevout.n == static_cast<uint32_t>( 13 ) );
bytes scriptSig = { 0x22, 0x0d };
BOOST_CHECK( tx.vin[0].scriptSig == bytes() );
BOOST_CHECK( tx.vout[0].value == static_cast<int64_t>( 1113 ) );
bytes scriptPubKey{ 0x00, 0x01, 0x0d };
BOOST_CHECK( tx.vout[0].scriptPubKey == scriptPubKey );
for( size_t i = 1; i <= keys.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( 0 ) );
bytes scriptPubKey{ 0x21 };
const auto key_data = keys[account_id_type( i - 1 )].key_data;
std::copy( key_data.begin(), key_data.end(), std::back_inserter( scriptPubKey ) );
scriptPubKey.push_back( 0xac );
BOOST_CHECK( tx.vout[i].scriptPubKey == scriptPubKey );
}
}
BOOST_AUTO_TEST_CASE( subtract_fee_tests )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
accounts_keys keys = create_accounts_keys();
create_info_vins_and_info_vouts( info_vins, info_vouts );
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
sidechain_condensing_tx ct( info_vins, info_vouts );
ct.create_pw_vin( create_pw_vin() );
ct.create_vouts_for_witness_fee( keys );
ct.create_pw_vout( pw_vout_amount, bytes{ 0x0d, 0x0d, 0x0d } );
ct.subtract_fee( size_fee, witness_percentage );
const auto& tx = ct.get_transaction();
std::vector<uint64_t> witnesses_fee;
for( size_t i = 0; i < info_vouts.size(); i++ ) {
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage );
}
uint64_t witnesses_fee_sum = std::accumulate( witnesses_fee.begin(), witnesses_fee.end(), 0 );
uint64_t witness_fee = witnesses_fee_sum / keys.size();
BOOST_CHECK( tx.vout[0].value == static_cast<int64_t>( pw_vout_amount ) );
for( size_t i = 1; i <= keys.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( witness_fee ) );
}
size_t offset = 1 + keys.size();
for( size_t i = offset; i < tx.vout.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
}
}
BOOST_AUTO_TEST_CASE( subtract_fee_not_pw_vout_and_witness_vouts_tests )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
create_info_vins_and_info_vouts( info_vins, info_vouts );
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
sidechain_condensing_tx ct( info_vins, info_vouts );
ct.create_pw_vin( create_pw_vin() );
ct.subtract_fee( size_fee, witness_percentage );
const auto& tx = ct.get_transaction();
std::vector<uint64_t> witnesses_fee;
for( size_t i = 0; i < info_vouts.size(); i++ ) {
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage );
}
for( size_t i = 0; i < tx.vout.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i].amount - size_fee_user ) - witnesses_fee[i] ) );
}
}
BOOST_AUTO_TEST_CASE( subtract_fee_not_witness_vouts_tests )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
create_info_vins_and_info_vouts( info_vins, info_vouts );
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
sidechain_condensing_tx ct( info_vins, info_vouts );
ct.create_pw_vin( create_pw_vin() );
ct.create_pw_vout( pw_vout_amount, bytes{ 0x0d, 0x0d, 0x0d } );
ct.subtract_fee( size_fee, witness_percentage );
const auto& tx = ct.get_transaction();
std::vector<uint64_t> witnesses_fee;
for( size_t i = 0; i < info_vouts.size(); i++ ) {
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage );
}
size_t offset = 1;
for( size_t i = offset; i < tx.vout.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
}
}
BOOST_AUTO_TEST_CASE( subtract_fee_not_pw_vout_tests )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
create_info_vins_and_info_vouts( info_vins, info_vouts );
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
accounts_keys keys = create_accounts_keys();
sidechain_condensing_tx ct( info_vins, info_vouts );
ct.create_pw_vin( create_pw_vin() );
ct.create_vouts_for_witness_fee( keys );
ct.subtract_fee( size_fee, witness_percentage );
const auto& tx = ct.get_transaction();
std::vector<uint64_t> witnesses_fee;
for( size_t i = 0; i < info_vouts.size(); i++ ) {
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage );
}
size_t offset = keys.size();
for( size_t i = offset; i < tx.vout.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
}
}
BOOST_AUTO_TEST_CASE( subtract_fee_not_vins_vout_tests )
{
std::vector<info_for_vin> info_vins;
std::vector<info_for_vout> info_vouts;
create_info_vins_and_info_vouts( info_vins, info_vouts );
info_vins.clear();
uint64_t size_fee_user = size_fee / ( info_vins.size() + info_vouts.size() );
accounts_keys keys = create_accounts_keys();
sidechain_condensing_tx ct( info_vins, info_vouts );
ct.create_pw_vin( create_pw_vin() );
ct.create_vouts_for_witness_fee( keys );
ct.create_pw_vout( pw_vout_amount, bytes{ 0x0d, 0x0d, 0x0d } );
ct.subtract_fee( size_fee, witness_percentage );
const auto& tx = ct.get_transaction();
std::vector<uint64_t> witnesses_fee;
for( size_t i = 0; i < info_vouts.size(); i++ ) {
witnesses_fee.push_back( ( info_vouts[i].amount - size_fee_user ) * witness_percentage );
}
size_t offset = 1 + keys.size();
for( size_t i = offset; i < tx.vout.size(); i++ ) {
BOOST_CHECK( tx.vout[i].value == static_cast<int64_t>( ( info_vouts[i - offset].amount - size_fee_user ) - witnesses_fee[i - offset] ) );
}
}
BOOST_AUTO_TEST_SUITE_END()