WIP: Sidechain objects

This commit is contained in:
Srdjan Obucina 2019-12-06 00:25:44 +01:00
parent 749fc2f330
commit 8676940a28
23 changed files with 1803 additions and 1 deletions

View file

@ -10,3 +10,4 @@ add_subdirectory( utilities )
add_subdirectory( app )
add_subdirectory( plugins )
add_subdirectory( wallet )
add_subdirectory( sidechain )

View file

@ -177,6 +177,8 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
vector<tournament_object> get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state);
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
//Sidechain
vector<bitcoin_address_object> get_bitcoin_addresses(const account_id_type& acc_id) const;
//private:
template<typename T>
@ -2222,6 +2224,32 @@ vector<tournament_id_type> database_api_impl::get_registered_tournaments(account
return tournament_ids;
}
//////////////////////////////////////////////////////////////////////
// //
// Sidechain //
// //
//////////////////////////////////////////////////////////////////////
vector<bitcoin_address_object> database_api::get_bitcoin_addresses(const account_id_type& acc) const
{
return my->get_bitcoin_addresses(acc);
}
vector<bitcoin_address_object> database_api_impl::get_bitcoin_addresses(const account_id_type& acc) const
{
vector<bitcoin_address_object> result;
const auto& btc_addr_idx = _db.get_index_type<bitcoin_address_index>().indices().get<by_owner>();
auto itr = btc_addr_idx.lower_bound( acc );
while( itr != btc_addr_idx.end() && itr->owner == acc )
{
result.push_back( *itr );
++itr;
}
return result;
}
//////////////////////////////////////////////////////////////////////
// //
// Private methods //

View file

@ -48,6 +48,7 @@
#include <graphene/chain/worker_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
@ -706,6 +707,8 @@ class database_api
*/
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
vector<bitcoin_address_object> get_bitcoin_addresses(const account_id_type& acc_id) const;
private:
std::shared_ptr< database_api_impl > my;
};
@ -840,4 +843,6 @@ FC_API(graphene::app::database_api,
(get_tournaments_by_state)
(get_tournaments )
(get_registered_tournaments)
(get_bitcoin_addresses)
)

View file

@ -122,7 +122,7 @@ add_library( graphene_chain
)
add_dependencies( graphene_chain build_hardfork_hpp )
target_link_libraries( graphene_chain fc graphene_db )
target_link_libraries( graphene_chain fc graphene_db sidechain )
target_include_directories( graphene_chain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" )

View file

@ -0,0 +1,46 @@
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/witness_object.hpp>
#include <sidechain/bitcoin/bitcoin_address.hpp>
namespace graphene { namespace chain {
class bitcoin_address_object : public abstract_object<bitcoin_address_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = bitcoin_address_object_type;
bitcoin_address_id_type get_id()const { return id; }
// multisig m-of-n (m = 5). Address is valid before count of changed witnesses < 5
bool valid() { return count_invalid_pub_key < SIDECHAIN_NUMBER_INVALID_KEYS; }
std::string get_address() const { return address.get_address(); }
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;
sidechain::btc_multisig_segwit_address address;
uint8_t count_invalid_pub_key;
};
struct by_address;
struct by_owner;
typedef boost::multi_index_container<
bitcoin_address_object,
indexed_by<
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_address>, const_mem_fun< bitcoin_address_object, std::string, &bitcoin_address_object::get_address > >,
ordered_non_unique< tag<by_owner>, member< bitcoin_address_object, account_id_type, &bitcoin_address_object::owner > >
>
> bitcoin_address_multi_index_container;
typedef generic_index<bitcoin_address_object, bitcoin_address_multi_index_container> bitcoin_address_index;
} }
FC_REFLECT_DERIVED( graphene::chain::bitcoin_address_object, (graphene::chain::object), (owner)(address)(count_invalid_pub_key) )

View file

@ -0,0 +1,96 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/bitcoin_transaction.hpp>
namespace graphene { namespace chain {
struct bitcoin_transaction_send_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
uint32_t price_per_kbyte = 0;
};
asset fee;
account_id_type payer;
sidechain::info_for_vin pw_vin;
std::vector< sidechain::info_for_vin > vins;
std::vector< info_for_vout_id_type > vouts;
sidechain::bitcoin_transaction transaction;
uint64_t fee_for_size;
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;
}
};
struct bitcoin_transaction_sign_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
uint32_t price_per_kbyte = 0;
};
asset fee;
account_id_type payer;
proposal_id_type proposal_id;
std::vector<sidechain::bytes> signatures;
account_id_type fee_payer()const { return payer; }
void validate()const {}
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
};
struct revert_trx_info
{
revert_trx_info() = default;
revert_trx_info( fc::sha256 trx_id, std::set< fc::sha256 > vins ): transaction_id( trx_id ), valid_vins( vins ) {}
fc::sha256 transaction_id;
std::set< fc::sha256 > valid_vins;
bool operator ==( const revert_trx_info& trx_info ){
return this->transaction_id == trx_info.transaction_id && this->valid_vins == trx_info.valid_vins;
}
bool operator !=( const revert_trx_info& trx_info ){
return this->transaction_id != trx_info.transaction_id || this->valid_vins != trx_info.valid_vins;
}
};
struct bitcoin_transaction_revert_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
uint32_t price_per_kbyte = 0;
};
asset fee;
account_id_type payer;
std::vector< revert_trx_info > transactions_info;
account_id_type fee_payer()const { return payer; }
void validate()const {}
share_type calculate_fee( const fee_parameters_type& k )const { return k.fee; }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(pw_vin)(vins)(vouts)(transaction)(fee_for_size) )
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) )
FC_REFLECT( graphene::chain::revert_trx_info, (transaction_id)(valid_vins) )
FC_REFLECT( graphene::chain::bitcoin_transaction_revert_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_transaction_revert_operation, (fee)(payer)(transactions_info) )

View file

@ -240,3 +240,17 @@
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
#define SWEEPS_VESTING_BALANCE_MULTIPLIER 100000000
#define SWEEPS_ACCUMULATOR_ACCOUNT (graphene::chain::account_id_type(0))
////////////////////////////////////////////////////////////////////// SideChain
#define SIDECHAIN_SYMBOL "pBTC"
#define SIDECHAIN_PRECISION_DIGITS 8
#define SIDECHAIN_MAX_SHARE_SUPPLY int64_t(21000000ll * 100000000ll)
#define SIDECHAIN_NUMBER_INVALID_KEYS 5
#define SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG 5
#define SIDECHAIN_DEFAULT_NUMBER_OF_CONFIRMATIONS 6
#define SIDECHAIN_DEFAULT_MAX_UNCONFIRMED_VOUTS 25
#define SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VINS 5
#define SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VOUTS 5
#define SIDECHAIN_DEFAULT_PERCENT_PAYMENT_TO_WITNESSES (GRAPHENE_1_PERCENT/10)
#define SIDECHAIN_NULL_VIN_IDENTIFIER "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b4" // fc::sha256::hash( "" + std::to_string( 0 ) ) - ( 8 bytes )
//////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,26 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct bitcoin_address_create_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
};
asset fee;
account_id_type payer;
account_id_type owner;
account_id_type fee_payer()const { return payer; }
void validate()const {}
share_type calculate_fee( const fee_parameters_type& k )const {
return k.fee;
}
};
} } // graphene::chain
FC_REFLECT( graphene::chain::bitcoin_address_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::bitcoin_address_create_operation, (fee)(payer)(owner) )

View file

@ -0,0 +1,96 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/bitcoin_transaction.hpp>
namespace graphene { namespace chain {
struct bitcoin_transaction_send_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
uint32_t price_per_kbyte = 0;
};
asset fee;
account_id_type payer;
sidechain::info_for_vin pw_vin;
std::vector< sidechain::info_for_vin > vins;
std::vector< info_for_vout_id_type > vouts;
sidechain::bitcoin_transaction transaction;
uint64_t fee_for_size;
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;
}
};
struct bitcoin_transaction_sign_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
uint32_t price_per_kbyte = 0;
};
asset fee;
account_id_type payer;
proposal_id_type proposal_id;
std::vector<sidechain::bytes> signatures;
account_id_type fee_payer()const { return payer; }
void validate()const {}
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
};
struct revert_trx_info
{
revert_trx_info() = default;
revert_trx_info( fc::sha256 trx_id, std::set< fc::sha256 > vins ): transaction_id( trx_id ), valid_vins( vins ) {}
fc::sha256 transaction_id;
std::set< fc::sha256 > valid_vins;
bool operator ==( const revert_trx_info& trx_info ){
return this->transaction_id == trx_info.transaction_id && this->valid_vins == trx_info.valid_vins;
}
bool operator !=( const revert_trx_info& trx_info ){
return this->transaction_id != trx_info.transaction_id || this->valid_vins != trx_info.valid_vins;
}
};
struct bitcoin_transaction_revert_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
uint32_t price_per_kbyte = 0;
};
asset fee;
account_id_type payer;
std::vector< revert_trx_info > transactions_info;
account_id_type fee_payer()const { return payer; }
void validate()const {}
share_type calculate_fee( const fee_parameters_type& k )const { return k.fee; }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_transaction_send_operation, (fee)(payer)(pw_vin)(vins)(vouts)(transaction)(fee_for_size) )
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(proposal_id)(signatures) )
FC_REFLECT( graphene::chain::revert_trx_info, (transaction_id)(valid_vins) )
FC_REFLECT( graphene::chain::bitcoin_transaction_revert_operation::fee_parameters_type, (fee)(price_per_kbyte) )
FC_REFLECT( graphene::chain::bitcoin_transaction_revert_operation, (fee)(payer)(transactions_info) )

View file

@ -147,6 +147,7 @@ namespace graphene { namespace chain {
bet_object_type,
son_object_type,
son_proposal_object_type,
bitcoin_address_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
@ -209,6 +210,7 @@ namespace graphene { namespace chain {
class bet_object;
class son_object;
class son_proposal_object;
class bitcoin_address_object;
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
@ -238,6 +240,8 @@ namespace graphene { namespace chain {
typedef object_id< protocol_ids, son_object_type, son_object> son_id_type;
typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type;
typedef object_id< protocol_ids, bitcoin_address_object_type, bitcoin_address_object> bitcoin_address_id_type;
// implementation types
class global_property_object;
class dynamic_global_property_object;
@ -421,6 +425,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
(bet_object_type)
(son_object_type)
(son_proposal_object_type)
(bitcoin_address_object_type)
(OBJECT_TYPE_COUNT)
)
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
@ -493,6 +498,7 @@ FC_REFLECT_TYPENAME( graphene::chain::global_betting_statistics_id_type )
FC_REFLECT_TYPENAME( graphene::chain::tournament_details_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
FC_REFLECT_TYPENAME( graphene::chain::bitcoin_address_id_type )
FC_REFLECT( graphene::chain::void_t, )

View file

@ -0,0 +1,10 @@
file( GLOB SOURCES "*.cpp" )
file( GLOB HEADERS "include/*.hpp" )
file( GLOB SOURCES_BITCOIN "bitcoin/*.cpp" )
file( GLOB HEADERS_BITCOIN "include/bitcoin/*.hpp" )
add_library( sidechain STATIC ${SOURCES} ${HEADERS} ${SOURCES_BITCOIN} ${HEADERS_BITCOIN} )
target_link_libraries( sidechain fc graphene_chain )
target_include_directories( sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -0,0 +1,258 @@
#include <sidechain/bitcoin/bitcoin_address.hpp>
#include <sstream>
#include <fc/crypto/base58.hpp>
#include <sidechain/bitcoin/segwit_addr.hpp>
#include <sidechain/bitcoin/bitcoin_script.hpp>
namespace sidechain {
bool bitcoin_address::operator==( const bitcoin_address& btc_addr ) const {
return ( this->address == btc_addr.address ) &&
( this->type == btc_addr.type ) &&
( this->raw_address == btc_addr.raw_address );
}
bytes bitcoin_address::get_script() const
{
switch ( type ) {
case payment_type::NULLDATA:
return script_builder() << op::RETURN << raw_address;
case payment_type::P2PK:
return script_builder() << raw_address << op::CHECKSIG;
case payment_type::P2PKH:
return script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG;
case payment_type::P2SH:
case payment_type::P2SH_WPKH:
case payment_type::P2SH_WSH:
return script_builder() << op::HASH160 << raw_address << op::EQUAL;
case payment_type::P2WPKH:
case payment_type::P2WSH:
return script_builder() << op::_0 << raw_address;
default:
return raw_address;
}
}
payment_type bitcoin_address::determine_type()
{
if( is_p2pk() ) {
return payment_type::P2PK;
} else if( is_p2wpkh() ) {
return payment_type::P2WPKH;
} else if( is_p2wsh() ) {
return payment_type::P2WSH;
} else if( is_p2pkh() ) {
return payment_type::P2PKH;
} else if( is_p2sh() ) {
return payment_type::P2SH;
} else {
return payment_type::NULLDATA;
}
}
bytes bitcoin_address::determine_raw_address()
{
bytes result;
switch( type ) {
case payment_type::P2PK : {
result = parse_hex( address );
break;
}
case payment_type::P2WPKH :
case payment_type::P2WSH : {
std::string prefix( address.compare(0,4,"bcrt") == 0 ? std::string( address.begin(), address.begin() + 4 ) :
std::string( address.begin(), address.begin() + 2 ) );
const auto& decode_bech32 = segwit_addr::decode( prefix, address );
result = bytes( decode_bech32.second.begin(), decode_bech32.second.end() );
break;
}
case payment_type::P2SH_WPKH :
case payment_type::P2SH_WSH :
case payment_type::P2PKH :
case payment_type::P2SH : {
bytes hex_addr = fc::from_base58( address );
result = bytes( hex_addr.begin() + 1, hex_addr.begin() + 21 );
break;
}
case payment_type::NULLDATA : return result;
}
return result;
}
bool bitcoin_address::check_segwit_address( const size_segwit_address& size ) const {
if( !address.compare(0,4,"bcrt") || !address.compare(0,2,"bc") || !address.compare(0,2,"tb") ) {
std::string prefix( !address.compare(0,4,"bcrt") ? std::string(address.begin(), address.begin() + 4) :
std::string(address.begin(), address.begin() + 2) );
const auto& decode_bech32 = segwit_addr::decode( prefix, address );
if( decode_bech32.first == -1 || decode_bech32.second.size() != size ) {
return false;
}
return true;
}
return false;
}
bool bitcoin_address::is_p2pk() const
{
try {
bool prefix = !address.compare(0,2,"02") || !address.compare(0,2,"03");
if( address.size() == 66 && prefix ) {
parse_hex( address );
return true;
}
} catch( fc::exception e ) {
return false;
}
return false;
}
bool bitcoin_address::is_p2wpkh() const
{
return check_segwit_address( size_segwit_address::P2WPKH );
}
bool bitcoin_address::is_p2wsh() const
{
return check_segwit_address( size_segwit_address::P2WSH );
}
bool bitcoin_address::is_p2pkh() const
{
try {
bytes hex_addr = fc::from_base58( address );
if( hex_addr.size() == 25 && ( static_cast<unsigned char>( hex_addr[0] ) == 0x00 ||
static_cast<unsigned char>( hex_addr[0] ) == 0x6f ) ) {
return true;
}
return false;
} catch( fc::exception e ) {
return false;
}
}
bool bitcoin_address::is_p2sh() const
{
try {
bytes hex_addr = fc::from_base58( address );
if( hex_addr.size() == 25 && ( static_cast<unsigned char>( hex_addr[0] ) == 0x05 ||
static_cast<unsigned char>( hex_addr[0] ) == 0xc4 ) ) {
return true;
}
return false;
} catch( fc::exception e ) {
return false;
}
}
btc_multisig_address::btc_multisig_address( const size_t n_required, const accounts_keys& keys ) :
keys_required ( n_required ), witnesses_keys( keys )
{
create_redeem_script();
create_address();
type = payment_type::P2SH;
}
size_t btc_multisig_address::count_intersection( const accounts_keys& keys ) const
{
FC_ASSERT( keys.size() > 0 );
int intersections_count = 0;
for( auto& key : keys ) {
auto witness_key = witnesses_keys.find( key.first );
if( witness_key == witnesses_keys.end() ) continue;
if( key.second == witness_key->second )
intersections_count++;
}
return intersections_count;
}
void btc_multisig_address::create_redeem_script()
{
FC_ASSERT( keys_required > 0 );
FC_ASSERT( keys_required < witnesses_keys.size() );
redeem_script.clear();
redeem_script.push_back( op_num[keys_required - 1] );
for( const auto& key : witnesses_keys ) {
std::stringstream ss;
ss << std::hex << key.second.key_data.size();
auto key_size_hex = 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_num[witnesses_keys.size() - 1] );
redeem_script.push_back( OP_CHECKMULTISIG );
}
void btc_multisig_address::create_address()
{
FC_ASSERT( redeem_script.size() > 0 );
raw_address.clear();
fc::sha256 hash256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() );
fc::ripemd160 hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
bytes temp_addr_hash( 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;
}
std::vector<public_key_type> btc_multisig_segwit_address::get_keys() {
std::vector<public_key_type> keys;
for( const auto& k : witnesses_keys ) {
keys.push_back( k.second );
}
return keys;
}
void btc_multisig_segwit_address::create_witness_script()
{
const auto redeem_sha256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() );
witness_script.push_back( OP_0 );
witness_script.push_back( 0x20 ); // PUSH_32
witness_script.insert( witness_script.end(), redeem_sha256.data(), redeem_sha256.data() + redeem_sha256.data_size() );
}
void btc_multisig_segwit_address::create_segwit_address()
{
fc::sha256 hash256 = fc::sha256::hash( witness_script.data(), witness_script.size() );
fc::ripemd160 hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size() );
address = fc::to_base58( get_address_bytes( raw_address ) );
}
bytes btc_multisig_segwit_address::get_address_bytes( const bytes& script_hash )
{
bytes address_bytes( 1, TESTNET_SCRIPT ); // 1 byte version
address_bytes.insert( address_bytes.end(), script_hash.begin(), script_hash.end() );
fc::sha256 hash256 = fc::sha256::hash( fc::sha256::hash( address_bytes.data(), address_bytes.size() ) );
address_bytes.insert( address_bytes.end(), hash256.data(), hash256.data() + 4 ); // 4 byte checksum
return address_bytes;
}
}

View file

@ -0,0 +1,60 @@
#include <sidechain/bitcoin/bitcoin_script.hpp>
#include <sidechain/bitcoin/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,280 @@
#include <sidechain/bitcoin/bitcoin_transaction.hpp>
#include <fc/crypto/base58.hpp>
#include <sidechain/bitcoin/bitcoin_script.hpp>
#include <sidechain/bitcoin/serialize.hpp>
namespace sidechain {
bool out_point::operator==( const out_point& op ) const
{
if( this->hash == op.hash &&
this->n == op.n )
{
return true;
}
return false;
}
bool tx_in::operator==( const tx_in& ti ) const
{
if( this->prevout == ti.prevout &&
this->scriptSig == ti.scriptSig &&
this->nSequence == ti.nSequence )
{
return true;
}
return false;
}
bool tx_out::operator==( const tx_out& to ) const
{
if( this->value == to.value &&
this->scriptPubKey == to.scriptPubKey )
{
return true;
}
return false;
}
bool tx_out::is_p2wsh() const
{
if( scriptPubKey.size() == 34 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x20) ) {
return true;
}
return false;
}
bool tx_out::is_p2wpkh() const
{
if( scriptPubKey.size() == 22 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x14) ) {
return true;
}
return false;
}
bool tx_out::is_p2pkh() const
{
if( scriptPubKey.size() == 25 && scriptPubKey[0] == static_cast<char>(0x76) && scriptPubKey[1] == static_cast<char>(0xa9) &&
scriptPubKey[2] == static_cast<char>(0x14) && scriptPubKey[23] == static_cast<char>(0x88) && scriptPubKey[24] == static_cast<char>(0xac) ) {
return true;
}
return false;
}
bool tx_out::is_p2sh() const
{
if( scriptPubKey.size() == 23 && scriptPubKey[0] == static_cast<char>(0xa9) &&
scriptPubKey[1] == static_cast<char>(0x14) && scriptPubKey[22] == static_cast<char>(0x87) ) {
return true;
}
return false;
}
bool tx_out::is_p2pk() const
{
if( scriptPubKey.size() == 35 && scriptPubKey[0] == static_cast<char>(0x21) && scriptPubKey[34] == static_cast<char>(0xac) ) {
return true;
}
return false;
}
bytes tx_out::get_data_or_script() const
{
if( is_p2pkh() ) {
return bytes( scriptPubKey.begin() + 3, scriptPubKey.begin() + 23 );
} else if( is_p2sh() || is_p2wpkh() ) {
return bytes( scriptPubKey.begin() + 2, scriptPubKey.begin() + 22 );
} else if( is_p2wsh() ) {
return bytes( scriptPubKey.begin() + 2, scriptPubKey.begin() + 34 );
} else if( is_p2pk() ) {
return bytes( scriptPubKey.begin() + 1, scriptPubKey.begin() + 34 );
}
return scriptPubKey;
}
bool bitcoin_transaction::operator!=( const bitcoin_transaction& bt ) const
{
if( this->nVersion != bt.nVersion ||
this->vin != bt.vin ||
this->vout != bt.vout ||
this->nLockTime != bt.nLockTime )
{
return true;
}
return false;
}
fc::sha256 bitcoin_transaction::get_hash() const
{
const auto bytes = pack( *this, true) ;
const auto hash = fc::sha256::hash( fc::sha256::hash(bytes.data(), bytes.size()) );
std::reverse( hash.data(), hash.data() + hash.data_size() );
return hash;
}
fc::sha256 bitcoin_transaction::get_txid() const
{
const auto bytes = pack( *this, false );
const auto hash = fc::sha256::hash( fc::sha256::hash(bytes.data(), bytes.size()) );
std::reverse( hash.data(), hash.data() + hash.data_size() );
return hash;
}
size_t bitcoin_transaction::get_vsize() const
{
static const auto witness_scale_factor = 4;
fc::datastream<size_t> no_wit_ds;
pack( no_wit_ds, *this, false );
fc::datastream<size_t> wit_ds;
pack( wit_ds, *this, true );
const size_t weight = no_wit_ds.tellp() * ( witness_scale_factor - 1 ) + wit_ds.tellp();
const size_t vsize = ( weight + witness_scale_factor - 1 ) / witness_scale_factor;
return vsize;
}
void bitcoin_transaction_builder::set_version( int32_t version )
{
tx.nVersion = version;
}
void bitcoin_transaction_builder::set_locktime(uint32_t lock_time)
{
tx.nLockTime = lock_time;
}
void bitcoin_transaction_builder::add_in( payment_type type, const fc::sha256& txid, uint32_t n_out, const bytes& script_code, bool front, uint32_t sequence )
{
out_point prevout;
prevout.hash = txid;
prevout.n = n_out;
tx_in txin;
txin.prevout = prevout;
txin.nSequence = sequence;
add_in( type, txin, script_code, front );
}
void bitcoin_transaction_builder::add_in( payment_type type, tx_in txin, const bytes& script_code, bool front )
{
switch ( type ) {
case payment_type::P2SH_WPKH:
case payment_type::P2SH_WSH:
FC_ASSERT( script_code != bytes() );
txin.scriptSig = script_code;
break;
default:{
if( txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000") ) { //coinbase
FC_ASSERT( script_code != bytes() );
txin.scriptSig = script_code;
}
break;
}
}
if( front ) {
tx.vin.insert( tx.vin.begin(), txin );
} else {
tx.vin.push_back( txin );
}
}
void bitcoin_transaction_builder::add_out( payment_type type, int64_t amount, const std::string& base58_address, bool front )
{
// TODO: add checks
const auto address_bytes = fc::from_base58(base58_address);
add_out( type, amount, bytes( address_bytes.begin() + 1, address_bytes.begin() + 1 + 20 ), front );
}
void bitcoin_transaction_builder::add_out( payment_type type, int64_t amount, const fc::ecc::public_key_data& pubkey, bool front )
{
FC_ASSERT( is_payment_to_pubkey( type ) );
if ( type == payment_type::P2PK ) {
const auto pubkey_bytes = bytes( pubkey.begin(), pubkey.begin() + pubkey.size() );
add_out( type, amount, pubkey_bytes, front );
} else {
const auto hash256 = fc::sha256::hash( pubkey.begin(), pubkey.size() );
const auto hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
add_out( type, amount, bytes( hash160.data(), hash160.data() + hash160.data_size() ), front );
}
}
void bitcoin_transaction_builder::add_out( payment_type type, int64_t amount, const bytes& script_code, bool front )
{
tx_out out;
out.value = amount;
out.scriptPubKey = get_script_pubkey( type, script_code );
if( front ) {
tx.vout.insert( tx.vout.begin(), out );
} else {
tx.vout.push_back( out );
}
}
void bitcoin_transaction_builder::add_out_all_type( const uint64_t& amount, const bitcoin_address& address, bool front )
{
switch( address.get_type() ) {
case payment_type::P2PK: {
bytes raw_address( address.get_raw_address() );
fc::ecc::public_key_data public_key;
std::copy( raw_address.begin(), raw_address.end(), public_key.begin() );
add_out( address.get_type(), amount, public_key, front );
break;
}
case payment_type::P2PKH:
case payment_type::P2SH:
case payment_type::P2SH_WPKH:
case payment_type::P2SH_WSH: {
add_out( address.get_type(), amount, address.get_address(), front );
break;
}
case payment_type::P2WPKH:
case payment_type::P2WSH: {
add_out( address.get_type(), amount, address.get_raw_address(), front );
break;
}
}
}
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

@ -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/bitcoin/segwit_addr.hpp>
#include <sidechain/bitcoin/bech32.hpp>
namespace
{
typedef std::vector<uint8_t> data;
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad>
bool convertbits(data& out, const data& in) {
int acc = 0;
int bits = 0;
const int maxv = (1 << tobits) - 1;
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
for (size_t i = 0; i < in.size(); ++i) {
int value = in[i];
acc = ((acc << frombits) | value) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
out.push_back((acc >> bits) & maxv);
}
}
if (pad) {
if (bits) out.push_back((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
return true;
}
}
namespace 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

@ -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,133 @@
#pragma once
#include <sidechain/bitcoin/types.hpp>
#include <sidechain/bitcoin/utils.hpp>
using namespace graphene::chain;
namespace sidechain {
const bytes op_num = {0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f}; // OP_1 - OP_15
class bitcoin_address
{
public:
bitcoin_address() = default;
bitcoin_address( const std::string& addr ) : address( addr ), type( determine_type() ),
raw_address( determine_raw_address() ) {}
bool operator==( const bitcoin_address& btc_addr ) const;
payment_type get_type() const { return type; }
std::string get_address() const { return address; }
bytes get_raw_address() const { return raw_address; }
bytes get_script() const;
private:
enum size_segwit_address { P2WSH = 32, P2WPKH = 20 };
payment_type determine_type();
bytes determine_raw_address();
bool check_segwit_address( const size_segwit_address& size ) const;
bool is_p2pk() const;
bool is_p2wpkh() const;
bool is_p2wsh() const;
bool is_p2pkh() const;
bool is_p2sh() const;
public:
std::string address;
payment_type type;
bytes raw_address;
};
class btc_multisig_address : public bitcoin_address
{
public:
btc_multisig_address() = default;
btc_multisig_address( const size_t n_required, const accounts_keys& keys );
size_t count_intersection( const accounts_keys& keys ) const;
bytes get_redeem_script() const { return redeem_script; }
private:
void create_redeem_script();
void create_address();
public:
enum address_types { MAINNET_SCRIPT = 5, TESTNET_SCRIPT = 196 };
enum { OP_0 = 0x00, OP_EQUAL = 0x87, OP_HASH160 = 0xa9, OP_CHECKMULTISIG = 0xae };
bytes redeem_script;
size_t keys_required = 0;
accounts_keys witnesses_keys;
};
// multisig segwit address (P2WSH)
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
class btc_multisig_segwit_address : public btc_multisig_address
{
public:
btc_multisig_segwit_address() = default;
btc_multisig_segwit_address( const size_t n_required, const accounts_keys& keys );
bool operator==( const btc_multisig_segwit_address& addr ) const;
bytes get_witness_script() const { return witness_script; }
std::vector<public_key_type> get_keys();
private:
void create_witness_script();
void create_segwit_address();
bytes get_address_bytes( const bytes& script_hash );
public:
bytes witness_script;
};
}
FC_REFLECT( 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

@ -0,0 +1,69 @@
#pragma once
#include <sidechain/bitcoin/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,100 @@
#pragma once
#include <sidechain/bitcoin/bitcoin_address.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 ) {}
bool operator==( const out_point& op ) const;
};
struct tx_in {
bool operator==( const tx_in& ti ) const;
out_point prevout;
bytes scriptSig;
uint32_t nSequence = 0xffffffff;
std::vector<bytes> scriptWitness;
};
struct tx_out {
int64_t value = 0;
bytes scriptPubKey;
bool operator==( const tx_out& to ) const;
bool is_p2wsh() const;
bool is_p2wpkh() const;
bool is_p2pkh() const;
bool is_p2sh() const;
bool is_p2pk() const;
bytes get_data_or_script() const;
};
struct bitcoin_transaction {
bool operator!=( const bitcoin_transaction& bt ) const;
int32_t nVersion = 1;
std::vector<tx_in> vin;
std::vector<tx_out> vout;
uint32_t nLockTime = 0;
fc::sha256 get_hash() const;
fc::sha256 get_txid() const;
size_t get_vsize() const;
};
class bitcoin_transaction_builder
{
public:
bitcoin_transaction_builder() = default;
bitcoin_transaction_builder( const bitcoin_transaction _tx ) : tx( _tx ) {}
void set_version( int32_t version );
void set_locktime( uint32_t lock_time );
void add_in( payment_type type, const fc::sha256& txid, uint32_t n_out,
const bytes& script_code, bool front = false, uint32_t sequence = 0xffffffff );
void add_in( payment_type type, tx_in txin, const bytes& script_code, bool front = false );
void add_out( payment_type type, int64_t amount, const std::string& base58_address, bool front = false );
void add_out( payment_type type, int64_t amount, const fc::ecc::public_key_data& pubkey, bool front = false );
void add_out( payment_type type, int64_t amount, const bytes& script_code, bool front = false );
void add_out_all_type( const uint64_t& amount, const bitcoin_address& address, bool front = false );
bitcoin_transaction get_transaction() const { return tx; }
private:
inline bool is_payment_to_pubkey( payment_type type );
bytes get_script_pubkey( payment_type type, const bytes& script_code );
bitcoin_transaction tx;
};
}
FC_REFLECT( 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

@ -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,360 @@
#pragma once
#include <sidechain/bitcoin/types.hpp>
#include <sidechain/bitcoin/bitcoin_transaction.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 );
}
template<typename Stream>
inline void pack( Stream& s, const out_point& op )
{
fc::sha256 reversed( op.hash );
std::reverse( reversed.data(), reversed.data() + reversed.data_size() );
s.write( reversed.data(), reversed.data_size() );
pack( s, op.n );
}
template<typename Stream>
inline void unpack( Stream& s, out_point& op )
{
uint64_t hash_size = op.hash.data_size();
std::unique_ptr<char[]> hash_data( new char[hash_size] );
s.read( hash_data.get(), hash_size );
std::reverse( hash_data.get(), hash_data.get() + hash_size );
op.hash = fc::sha256( hash_data.get(), hash_size );
unpack( s, op.n );
}
template<typename Stream>
inline void pack( Stream& s, const tx_in& in )
{
pack( s, in.prevout );
pack( s, in.scriptSig );
pack( s, in.nSequence );
}
template<typename Stream>
inline void unpack( Stream& s, tx_in& in )
{
unpack( s, in.prevout );
unpack( s, in.scriptSig );
unpack( s, in.nSequence );
}
template<typename Stream>
inline void pack( Stream& s, const tx_out& out )
{
pack( s, out.value );
pack( s, out.scriptPubKey );
}
template<typename Stream>
inline void unpack( Stream& s, tx_out& out )
{
unpack( s, out.value );
unpack( s, out.scriptPubKey );
}
template<typename Stream>
inline void pack( Stream& s, const bitcoin_transaction& tx, bool with_witness = true )
{
uint8_t flags = 0;
if ( with_witness ) {
for ( const auto& in : tx.vin ) {
if ( !in.scriptWitness.empty() ) {
flags |= 1;
break;
}
}
}
pack( s, tx.nVersion );
if ( flags ) {
pack_compact_size( s, 0 );
pack( s, flags );
}
pack_compact_size( s, tx.vin.size() );
for ( const auto& in : tx.vin )
pack( s, in );
pack_compact_size( s, tx.vout.size() );
for ( const auto& out : tx.vout )
pack( s, out );
if ( flags & 1 ) {
for ( const auto in : tx.vin ) {
pack_compact_size( s, in.scriptWitness.size() );
for ( const auto& sc : in.scriptWitness )
pack( s, sc );
}
}
pack( s, tx.nLockTime );
}
template<typename Stream>
inline void unpack( Stream& s, bitcoin_transaction& tx )
{
uint8_t flags = 0;
unpack( s, tx.nVersion );
auto vin_size = unpack_compact_size( s );
if ( vin_size == 0 ) {
unpack( s, flags );
vin_size = unpack_compact_size( s );
}
tx.vin.reserve( vin_size );
for ( size_t i = 0; i < vin_size; i++ ) {
tx_in in;
unpack( s, in );
tx.vin.push_back( in );
}
const auto vout_size = unpack_compact_size( s );
tx.vout.reserve( vout_size );
for ( size_t i = 0; i < vout_size; i++ ) {
tx_out out;
unpack( s, out );
tx.vout.push_back( out );
}
if ( flags & 1 ) {
for ( auto& in : tx.vin ) {
uint64_t stack_size = unpack_compact_size( s );
in.scriptWitness.reserve( stack_size );
for ( uint64_t i = 0; i < stack_size; i++ ) {
std::vector<char> script;
unpack( s, script );
in.scriptWitness.push_back( script );
}
}
}
unpack( s, tx.nLockTime );
}
inline std::vector<char> pack( const bitcoin_transaction& v, bool with_witness = true )
{
fc::datastream<size_t> ps;
pack( ps, v, with_witness );
std::vector<char> vec( ps.tellp() );
if( !vec.empty() ) {
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
pack( ds, v, with_witness );
}
return vec;
}
inline bitcoin_transaction unpack( const std::vector<char>& s )
{ try {
bitcoin_transaction tmp;
if( !s.empty() ) {
fc::datastream<const char*> ds( s.data(), size_t( s.size() ) );
unpack(ds, tmp);
}
return tmp;
} FC_RETHROW_EXCEPTIONS( warn, "error unpacking ${type}", ("type","transaction" ) ) }
template<typename Stream>
inline void pack_tx_signature( Stream& s, const std::vector<char>& scriptPubKey, const bitcoin_transaction& tx, unsigned int in_index, int hash_type )
{
pack( s, tx.nVersion );
pack_compact_size( s, tx.vin.size() );
for ( size_t i = 0; i < tx.vin.size(); i++ ) {
const auto& in = tx.vin[i];
pack( s, in.prevout );
if ( i == in_index )
pack( s, scriptPubKey );
else
pack_compact_size( s, 0 ); // Blank signature
pack( s, in.nSequence );
}
pack_compact_size( s, tx.vout.size() );
for ( const auto& out : tx.vout )
pack( s, out );
pack( s, tx.nLockTime );
pack( s, hash_type );
}
template<typename Stream>
inline void pack_tx_witness_signature( Stream& s, const std::vector<char>& scriptCode, const bitcoin_transaction& tx, unsigned int in_index, int64_t amount, int hash_type )
{
fc::sha256 hash_prevouts;
fc::sha256 hash_sequence;
fc::sha256 hash_output;
{
fc::datastream<size_t> ps;
for ( const auto in : tx.vin )
pack( ps, in.prevout );
std::vector<char> vec( ps.tellp() );
if ( vec.size() ) {
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
for ( const auto in : tx.vin )
pack( ds, in.prevout );
}
hash_prevouts = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
}
{
fc::datastream<size_t> ps;
for ( const auto in : tx.vin )
pack( ps, in.nSequence );
std::vector<char> vec( ps.tellp() );
if ( vec.size() ) {
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
for ( const auto in : tx.vin )
pack( ds, in.nSequence );
}
hash_sequence = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
};
{
fc::datastream<size_t> ps;
for ( const auto out : tx.vout )
pack( ps, out );
std::vector<char> vec( ps.tellp() );
if ( vec.size() ) {
fc::datastream<char*> ds( vec.data(), size_t( vec.size() ) );
for ( const auto out : tx.vout )
pack( ds, out );
}
hash_output = fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
}
pack( s, tx.nVersion );
pack( s, hash_prevouts );
pack( s, hash_sequence );
pack( s, tx.vin[in_index].prevout );
pack( s, scriptCode );
pack( s, amount );
pack( s, tx.vin[in_index].nSequence );
pack( s, hash_output );
pack( s, tx.nLockTime );
pack( s, hash_type );
}
}

View file

@ -0,0 +1,60 @@
#pragma once
#include <map>
#include <vector>
#include <string>
#include <fc/reflect/reflect.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace sidechain {
class bitcoin_transaction;
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;
//using full_btc_transaction = std::pair<bitcoin_transaction, uint64_t>;
enum class payment_type
{
NULLDATA,
P2PK,
P2PKH,
P2SH,
P2WPKH,
P2WSH,
P2SH_WPKH,
P2SH_WSH
};
enum class sidechain_proposal_type
{
ISSUE_BTC,
SEND_BTC_TRANSACTION,
REVERT_BTC_TRANSACTION
};
struct prev_out
{
bool operator!=( const prev_out& obj ) const
{
if( this->hash_tx != obj.hash_tx ||
this->n_vout != obj.n_vout ||
this->amount != obj.amount )
{
return true;
}
return false;
}
std::string hash_tx;
uint32_t n_vout;
uint64_t amount;
};
}
FC_REFLECT_ENUM( sidechain::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH) );
FC_REFLECT_ENUM( sidechain::sidechain_proposal_type, (ISSUE_BTC)(SEND_BTC_TRANSACTION)(REVERT_BTC_TRANSACTION) );
FC_REFLECT( sidechain::prev_out, (hash_tx)(n_vout)(amount) );

View file

@ -0,0 +1,15 @@
#pragma once
#include <sidechain/bitcoin/types.hpp>
#include <fc/crypto/hex.hpp>
#include <fc/crypto/elliptic.hpp>
namespace sidechain {
bytes parse_hex( const std::string& str );
std::vector<bytes> get_pubkey_from_redeemScript( bytes script );
bytes public_key_data_to_bytes( const fc::ecc::public_key_data& key );
}