WIP, Moving Bitcoin listener to plugin

- Fix include paths
- Add missing files
This commit is contained in:
S 2019-09-16 23:39:25 +02:00
parent 6f34dabeab
commit 6bc81fdb35
9 changed files with 714 additions and 0 deletions

View file

@ -0,0 +1,279 @@
#include <sidechain/bitcoin_transaction.hpp>
#include <fc/crypto/base58.hpp>
#include <sidechain/bitcoin_script.hpp>
#include <sidechain/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,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,97 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
#include <graphene/peerplays_sidechain/input_withdrawal_info.hpp>
#include <graphene/peerplays_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

@ -0,0 +1,54 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <graphene/db/generic_index.hpp>
#include <graphene/peerplays_sidechain/types.hpp>
#include <graphene/peerplays_sidechain/bitcoin_address.hpp>
namespace graphene { namespace chain {
class info_for_vout_object : public abstract_object<info_for_vout_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = info_for_vout_object_type;
struct comparer {
bool operator()( const info_for_vout_object& lhs, const info_for_vout_object& rhs ) const {
if( lhs.used != rhs.used )
return lhs.used < rhs.used;
return lhs.id < rhs.id;
}
};
bool operator==( const info_for_vout_object& ifv ) const {
return ( this->payer == ifv.payer ) &&
( this->address == ifv.address ) &&
( this->amount == ifv.amount );
}
info_for_vout_id_type get_id()const { return id; }
account_id_type payer;
sidechain::bitcoin_address address;
uint64_t amount;
bool used = false;
};
struct by_created;
struct by_id_and_not_used;
typedef boost::multi_index_container<
info_for_vout_object,
indexed_by<
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
ordered_non_unique< tag< by_created >, member< info_for_vout_object, bool, &info_for_vout_object::used > >,
ordered_non_unique<tag<by_id_and_not_used>, identity< info_for_vout_object >, info_for_vout_object::comparer >
>
> info_for_vout_multi_index_container;
typedef generic_index<info_for_vout_object, info_for_vout_multi_index_container> info_for_vout_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(address)(amount)(used) )

View file

@ -0,0 +1,113 @@
#pragma once
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <fc/crypto/sha256.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/peerplays_sidechain/thread_safe_index.hpp>
#include <graphene/peerplays_sidechain/info_for_vout_object.hpp>
#include <graphene/peerplays_sidechain/types.hpp>
using boost::multi_index_container;
using namespace boost::multi_index;
namespace graphene { namespace chain { class database; } }
namespace sidechain {
struct info_for_vin
{
info_for_vin() = default;
info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes(), bool _resend = false );
bool operator!=( const info_for_vin& obj ) const;
struct comparer {
bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const;
};
static uint64_t count_id_info_for_vin;
uint64_t id;
fc::sha256 identifier;
prev_out out;
std::string address;
bytes script;
bool used = false;
bool resend = false;
};
struct by_id;
struct by_identifier;
struct by_id_resend_not_used;
using info_for_vin_index = boost::multi_index_container<info_for_vin,
indexed_by<
ordered_unique<tag<by_id>, member<info_for_vin, uint64_t, &info_for_vin::id>>,
ordered_unique<tag<by_identifier>, member<info_for_vin, fc::sha256, &info_for_vin::identifier>>,
ordered_non_unique<tag<by_id_resend_not_used>, identity< info_for_vin >, info_for_vin::comparer >
>
>;
class input_withdrawal_info
{
public:
input_withdrawal_info( graphene::chain::database& _db ) : db( _db ) {}
std::vector<sidechain::bytes> get_redeem_scripts( const std::vector<info_for_vin>& info_vins );
std::vector<uint64_t> get_amounts( const std::vector<info_for_vin>& info_vins );
fc::optional<info_for_vin> get_info_for_pw_vin();
void insert_info_for_vin( const prev_out& out, const std::string& address, bytes script = bytes(), bool resend = false );
void modify_info_for_vin( const info_for_vin& obj, const std::function<void( info_for_vin& e )>& func );
void mark_as_used_vin( const info_for_vin& obj );
void mark_as_unused_vin( const info_for_vin& obj );
void remove_info_for_vin( const info_for_vin& obj );
fc::optional<info_for_vin> find_info_for_vin( fc::sha256 identifier );
size_t size_info_for_vins() { return info_for_vins.size(); }
std::vector<info_for_vin> get_info_for_vins();
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 );
void mark_as_unused_vout( const graphene::chain::info_for_vout_object& obj );
void remove_info_for_vout( const info_for_vout& obj );
fc::optional<graphene::chain::info_for_vout_object> find_info_for_vout( graphene::chain::info_for_vout_id_type id );
size_t size_info_for_vouts();
std::vector<info_for_vout> get_info_for_vouts();
private:
graphene::chain::database& db;
thread_safe_index<info_for_vin_index> info_for_vins;
};
}
FC_REFLECT( sidechain::info_for_vin, (identifier)(out)(address)(script)(used)(resend) )

View file

@ -1,6 +1,7 @@
#pragma once
#include <graphene/peerplays_sidechain/zmq_listener.hpp>
#include <graphene/peerplays_sidechain/bitcoin_transaction.hpp>
#include <graphene/peerplays_sidechain/bitcoin_rpc_client.hpp>
#include <graphene/chain/database.hpp>

View file

@ -0,0 +1,82 @@
#pragma once
#include <mutex>
#include <fc/optional.hpp>
namespace sidechain {
template<class T1>
class thread_safe_index
{
public:
using iterator = typename T1::iterator;
std::pair<iterator,bool> insert( const typename T1::value_type& value ) {
std::lock_guard<std::recursive_mutex> locker( lock );
return data.insert( value );
}
template<class T2, typename Key>
bool modify( const Key _key, const std::function<void( typename T1::value_type& e)>& func ) {
std::lock_guard<std::recursive_mutex> locker( lock );
const auto option = find_iterator<T2>( _key );
if( !option )
return false;
data.modify( data.iterator_to( **option ), [&func]( typename T1::value_type& obj ) { func( obj ); } );
return true;
}
template<class T2, typename Key>
bool remove( const Key _key ) {
std::lock_guard<std::recursive_mutex> locker( lock );
const auto option = find_iterator<T2>( _key );
if( !option )
return false;
data.erase( data.iterator_to( **option ) );
return true;
}
size_t size() {
std::lock_guard<std::recursive_mutex> locker( lock );
return data.size();
}
template<class T2, typename Key>
fc::optional<typename T1::value_type> find( const Key _key ) {
std::lock_guard<std::recursive_mutex> locker( lock );
auto& index = data.template get<T2>();
auto it = index.find( _key );
if( it != index.end() ) {
return fc::optional<typename T1::value_type>(*it);
}
return fc::optional<typename T1::value_type>();
}
template<class T2>
void safe_for(std::function<void(typename T1::template index<T2>::type::iterator itr1,
typename T1::template index<T2>::type::iterator itr2)> func) {
std::lock_guard<std::recursive_mutex> locker( lock );
auto& index = data.template get<T2>();
func(index.begin(), index.end());
}
private:
template<class T2, typename Key>
fc::optional<typename T1::template index<T2>::type::iterator> find_iterator( const Key _key ) {
auto& index = data.template get<T2>();
auto it = index.find( _key );
if( it == index.end() )
return fc::optional<typename T1::template index<T2>::type::iterator>();
return it;
}
std::recursive_mutex lock;
T1 data;
};
}

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

@ -2,6 +2,8 @@
#include <graphene/peerplays_sidechain/sidechain_network_manager.hpp>
namespace bpo = boost::program_options;
namespace graphene { namespace peerplays_sidechain {
namespace detail