Compare commits

...

10 commits

Author SHA1 Message Date
S
ce87762dee Moving Bitcoin listener to SON plugin
- First buildable version
2019-09-18 21:51:46 +02:00
S
11e5469195 WIP, Moving Bitcoin listener to SON plugin
- Fix CMakeLists.txt
2019-09-18 18:54:52 +02:00
Srdjan Obucina
52ddeccbef WIP, Moving Bitcoin listener to SON plugin
- Fix include path
2019-09-18 17:03:53 +02:00
Srdjan Obucina
22adce712c WIP, Moving Bitcoin listener to SON plugin
- All sidechain code moved to plugin
2019-09-18 16:46:40 +02:00
Srdjan Obucina
942e571949 WIP, Moving Bitcoin listener to plugin
- Fix include paths
- Add missing files
2019-09-17 22:18:12 +02:00
Srdjan Obucina
bfa6babfd2 WIP, Moving Bitcoin listener to plugin
- Add missing files
- Remove few parts
2019-09-17 21:43:44 +02:00
Srdjan Obucina
b7f117f23c WIP, Moving Bitcoin listener to plugin
- Add missing types
2019-09-17 21:03:13 +02:00
S
6bc81fdb35 WIP, Moving Bitcoin listener to plugin
- Fix include paths
- Add missing files
2019-09-16 23:39:25 +02:00
Srdjan Obucina
6f34dabeab WIP, Moving Bitcoin listener to plugin
- Fix include paths
- Add missing files
2019-09-16 21:34:36 +02:00
Srdjan Obucina
fd313adaa8 WIP, Moving Bitcoin listener to plugin
- Original source code taken from https://github.com/peerplays-network/peerplays/pull/11
2019-09-16 21:24:25 +02:00
78 changed files with 5432 additions and 85 deletions

View file

@ -105,7 +105,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
// Peerplays
vector<sport_object> list_sports() const;
vector<sport_object> list_sports() const;
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
@ -161,6 +161,9 @@ 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;
// gpos
gpos_info get_gpos_info(const account_id_type account) const;
@ -733,7 +736,7 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
acnt.withdraws.emplace_back(withdraw);
});
auto pending_payouts_range =
auto pending_payouts_range =
_db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>().indices().get<by_account_dividend_payout>().equal_range(boost::make_tuple(account->id));
std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments));
@ -1965,7 +1968,7 @@ vector<tournament_object> database_api::get_tournaments(tournament_id_type stop,
vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type stop,
unsigned limit,
tournament_id_type start)
tournament_id_type start)
{
vector<tournament_object> result;
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
@ -1992,7 +1995,7 @@ vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament
unsigned limit,
tournament_id_type start,
tournament_state state)
{
{
vector<tournament_object> result;
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
for (auto elem: tournament_idx) {
@ -2023,6 +2026,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;
}
//////////////////////////////////////////////////////////////////////
// //
// GPOS methods //
@ -2170,7 +2199,7 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec
/// if a connection hangs then this could get backed up and result in
/// a failure to exit cleanly.
//fc::async([capture_this,this,updates,market_broadcast_queue](){
//if( _subscribe_callback )
//if( _subscribe_callback )
// _subscribe_callback( updates );
for(auto id : ids)

View file

@ -286,8 +286,8 @@ struct get_impacted_account_visitor
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )
{
get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
op.visit( vtor );
//get_impacted_account_visitor vtor = get_impacted_account_visitor( result );
//op.visit( vtor );
}
void transaction_get_impacted_accounts( const transaction& tx, flat_set<account_id_type>& result )

View file

@ -47,6 +47,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>
@ -651,6 +652,11 @@ class database_api
*/
vector<tournament_id_type> get_registered_tournaments(account_id_type account_filter, uint32_t limit) const;
/**
* @return the list of bitcoin addresses which belong to acc_id
*/
vector<bitcoin_address_object> get_bitcoin_addresses(const account_id_type& acc_id) const;
//////////
// GPOS //
//////////
@ -783,6 +789,7 @@ FC_API(graphene::app::database_api,
(get_tournaments_by_state)
(get_tournaments )
(get_registered_tournaments)
(get_bitcoin_addresses)
// gpos
(get_gpos_info)

View file

@ -21,6 +21,7 @@ if( GRAPHENE_DISABLE_UNITY_BUILD )
db_market.cpp
db_update.cpp
db_witness_schedule.cpp
db_sidechain.cpp
)
message( STATUS "Graphene database unity build disabled" )
else( GRAPHENE_DISABLE_UNITY_BUILD )
@ -85,6 +86,9 @@ add_library( graphene_chain
confidential_evaluator.cpp
special_authority.cpp
buyback.cpp
bitcoin_address_evaluator.cpp
bitcoin_transaction_evaluator.cpp
sidechain_evaluator.cpp
account_object.cpp
asset_object.cpp
@ -110,6 +114,7 @@ add_library( graphene_chain
betting_market_group_object.cpp
affiliate_payout.cpp
withdraw_pbtc_evaluator.cpp
${HEADERS}
${PROTOCOL_HEADERS}
@ -117,9 +122,11 @@ 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" )
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../plugins/peerplays_sidechain/sidechain/include" )
if(MSVC)
set_source_files_properties( db_init.cpp db_block.cpp database.cpp block_database.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
@ -133,4 +140,4 @@ INSTALL( TARGETS
ARCHIVE DESTINATION lib
)
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/chain" )
INSTALL( FILES ${PROTOCOL_HEADERS} DESTINATION "include/graphene/chain/protocol" )
INSTALL( FILES ${PROTOCOL_HEADERS} DESTINATION "include/graphene/chain/protocol" )

View file

@ -0,0 +1,46 @@
#include <graphene/chain/bitcoin_address_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
namespace graphene { namespace chain {
void_result bitcoin_address_create_evaluator::do_evaluate( const bitcoin_address_create_operation& op )
{
database& d = db();
FC_ASSERT( !d.is_sidechain_fork_needed() );
auto& acc_idx = d.get_index_type<account_index>().indices().get<by_id>();
auto acc_itr = acc_idx.find( op.owner );
FC_ASSERT( acc_itr != acc_idx.end() );
return void_result();
}
object_id_type bitcoin_address_create_evaluator::do_apply( const bitcoin_address_create_operation& op )
{
database& d = db();
const auto pw_obj = d.get_latest_PW();
auto witnesses_keys = pw_obj.address.witnesses_keys;
const auto& new_btc_address = d.create<bitcoin_address_object>( [&]( bitcoin_address_object& a ) {
witnesses_keys.erase( --witnesses_keys.end() );
witnesses_keys.emplace( d.get_sidechain_account_id(), pubkey_from_id( a.id ) );
a.owner = op.owner;
a.address = sidechain::btc_multisig_segwit_address( SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG, witnesses_keys );
a.count_invalid_pub_key = 1;
});
return new_btc_address.id;
}
public_key_type bitcoin_address_create_evaluator::pubkey_from_id( object_id_type id )
{
fc::ecc::public_key_data pubkey_data;
pubkey_data.at( 0 ) = 0x02; // version
const auto hash = fc::sha256::hash( std::to_string( id.instance() ) ) ;
std::copy( hash.data(), hash.data() + hash.data_size(), pubkey_data.begin() + 1 );
return public_key_type( pubkey_data );
}
} } // namespace graphene::chain

View file

@ -0,0 +1,274 @@
#include <graphene/chain/bitcoin_transaction_evaluator.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <graphene/chain/info_for_used_vin_object.hpp>
#include <graphene/chain/sidechain_proposal_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <sidechain/sign_bitcoin_transaction.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/sidechain_proposal_checker.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
namespace graphene { namespace chain {
using namespace sidechain;
void_result bitcoin_transaction_send_evaluator::do_evaluate( const bitcoin_transaction_send_operation& op )
{
// FC_ASSERT( db().get_sidechain_account_id() == op.payer );
return void_result();
}
object_id_type bitcoin_transaction_send_evaluator::do_apply( const bitcoin_transaction_send_operation& op )
{
bitcoin_transaction_send_operation& mutable_op = const_cast<bitcoin_transaction_send_operation&>( op );
database& d = db();
finalize_bitcoin_transaction( mutable_op );
auto new_vins = create_transaction_vins( mutable_op );
const bitcoin_transaction_object& btc_tx = d.create< bitcoin_transaction_object >( [&]( bitcoin_transaction_object& obj )
{
obj.pw_vin = mutable_op.pw_vin.identifier;
obj.vins = new_vins;
obj.vouts = mutable_op.vouts;
obj.transaction = mutable_op.transaction;
obj.transaction_id = mutable_op.transaction.get_txid();
obj.fee_for_size = mutable_op.fee_for_size;
});
sidechain::prev_out new_pw_vout = { "", 0, 0 };
sidechain::bytes wit_script = d.get_latest_PW().address.get_witness_script();
if( btc_tx.transaction.vout[0].is_p2wsh() && btc_tx.transaction.vout[0].scriptPubKey == wit_script )
new_pw_vout = { btc_tx.transaction.get_txid(), 0, btc_tx.transaction.vout[0].value };
d.pw_vout_manager.create_new_vout( new_pw_vout );
send_bitcoin_transaction( btc_tx );
return btc_tx.id;
}
std::vector<fc::sha256> bitcoin_transaction_send_evaluator::create_transaction_vins( bitcoin_transaction_send_operation& op )
{
database& d = db();
std::vector<fc::sha256> new_vins;
for( auto itr : op.vins ) {
auto info_for_used_vin_itr = d.create< info_for_used_vin_object >( [&]( info_for_used_vin_object& obj )
{
obj.identifier = itr.identifier;
obj.out = itr.out;
obj.address = itr.address;
obj.script = itr.script;
});
new_vins.push_back( info_for_used_vin_itr.identifier );
auto obj_itr = d.i_w_info.find_info_for_vin( itr.identifier );
if( obj_itr.valid() ) {
d.i_w_info.remove_info_for_vin( *obj_itr );
}
}
return new_vins;
}
void bitcoin_transaction_send_evaluator::finalize_bitcoin_transaction( bitcoin_transaction_send_operation& op )
{
database& d = db();
auto vins = op.vins;
if( op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
vins.insert( vins.begin(), op.pw_vin );
}
std::vector<bytes> redeem_scripts( d.i_w_info.get_redeem_scripts( vins ) );
std::vector<uint64_t> amounts( d.i_w_info.get_amounts( vins ) );
std::vector<std::vector<sidechain::bytes>> new_stacks( sidechain::sort_sigs( op.transaction, redeem_scripts, amounts, d.context_verify ) );
for( size_t i = 0; i < new_stacks.size(); i++ ) {
op.transaction.vin[i].scriptWitness = new_stacks[i];
}
sidechain::sign_witness_transaction_finalize( op.transaction, redeem_scripts );
}
void bitcoin_transaction_send_evaluator::send_bitcoin_transaction( const bitcoin_transaction_object& btc_tx )
{
database& d = db();
uint32_t skip = d.get_node_properties().skip_flags;
if( !(skip & graphene::chain::database::skip_btc_tx_sending) && d.send_btc_tx_flag ){
idump((btc_tx));
d.send_btc_tx( btc_tx.transaction );
}
}
void_result bitcoin_transaction_sign_evaluator::do_evaluate( const bitcoin_transaction_sign_operation& op )
{
database& d = db();
const auto& proposal_idx = d.get_index_type<proposal_index>().indices().get<by_id>();
const auto& proposal_itr = proposal_idx.find( op.proposal_id );
FC_ASSERT( proposal_idx.end() != proposal_itr, "proposal not found");
const auto& witness_obj = d.get< witness_object >( d._current_witness_id );
FC_ASSERT( witness_obj.witness_account == op.payer, "Incorrect witness." );
sidechain::bytes public_key( public_key_data_to_bytes( witness_obj.signing_key.key_data ) );
auto btc_send_op = proposal_itr->proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();
auto vins = btc_send_op.vins;
if( btc_send_op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
vins.insert( vins.begin(), btc_send_op.pw_vin );
}
FC_ASSERT( check_sigs( public_key, op.signatures, vins, btc_send_op.transaction ) );
sidechain::sidechain_proposal_checker checker( d );
FC_ASSERT( checker.check_witness_opportunity_to_approve( witness_obj, *proposal_itr ), "Can't sign this transaction" );
return void_result();
}
void_result bitcoin_transaction_sign_evaluator::do_apply( const bitcoin_transaction_sign_operation& op )
{
database& d = db();
const auto& proposal = op.proposal_id( d );
d.modify( proposal, [&]( proposal_object& po ) {
auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();
for( size_t i = 0; i < op.signatures.size(); i++ ) {
bitcoin_transaction_send_op.transaction.vin[i].scriptWitness.push_back( op.signatures[i] );
}
po.proposed_transaction.operations[0] = bitcoin_transaction_send_op;
});
update_proposal( op );
return void_result();
}
void bitcoin_transaction_sign_evaluator::update_proposal( const bitcoin_transaction_sign_operation& op )
{
database& d = db();
proposal_update_operation update_op;
update_op.fee_paying_account = op.payer;
update_op.proposal = op.proposal_id;
update_op.active_approvals_to_add = { op.payer };
bool skip_fee_old = trx_state->skip_fee;
bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check;
trx_state->skip_fee = true;
trx_state->skip_fee_schedule_check = true;
d.apply_operation( *trx_state, update_op );
trx_state->skip_fee = skip_fee_old;
trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old;
}
bool bitcoin_transaction_sign_evaluator::check_sigs( const bytes& key_data, const std::vector<bytes>& sigs,
const std::vector<sidechain::info_for_vin>& info_for_vins,
const bitcoin_transaction& tx )
{
FC_ASSERT( sigs.size() == info_for_vins.size() && sigs.size() == tx.vin.size() );
const auto& bitcoin_address_idx = db().get_index_type<bitcoin_address_index>().indices().get< by_address >();
for( size_t i = 0; i < tx.vin.size(); i++ ) {
const auto pbtc_address = bitcoin_address_idx.find( info_for_vins[i].address );
const bytes& script = pbtc_address->address.get_redeem_script();
const auto& sighash_str = get_signature_hash( tx, script, static_cast<int64_t>( info_for_vins[i].out.amount ), i, 1, true ).str();
const bytes& sighash_hex = parse_hex( sighash_str );
if( !verify_sig( sigs[i], key_data, sighash_hex, db().context_verify ) ) {
return false;
}
size_t count_sigs = 0;
for( auto& s : tx.vin[i].scriptWitness ) {
if( verify_sig( s, key_data, sighash_hex, db().context_verify ) ) {
count_sigs++;
}
}
std::vector<bytes> pubkeys = get_pubkey_from_redeemScript( script );
size_t count_pubkeys = std::count( pubkeys.begin(), pubkeys.end(), key_data );
if( count_sigs >= count_pubkeys ) {
return false;
}
uint32_t position = std::find( op_num.begin(), op_num.end(), script[0] ) - op_num.begin();
if( !( position >= 0 && position < op_num.size() ) || tx.vin[i].scriptWitness.size() == position + 1 ) {
return false;
}
}
return true;
}
void_result bitcoin_transaction_revert_evaluator::do_evaluate( const bitcoin_transaction_revert_operation& op )
{ try {
database& d = db();
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
for( auto trx_info: op.transactions_info ) {
const auto& btc_itr = btc_trx_idx.find( trx_info.transaction_id );
FC_ASSERT( btc_itr != btc_trx_idx.end() );
for( const auto& vout_id : btc_itr->vouts ) {
FC_ASSERT( vouts_info_idx.find( vout_id ) != vouts_info_idx.end() );
}
for( const auto& vout_id : btc_itr->vins ) {
FC_ASSERT( vins_info_idx.find( vout_id ) != vins_info_idx.end() );
}
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result bitcoin_transaction_revert_evaluator::do_apply( const bitcoin_transaction_revert_operation& op )
{ try {
database& d = db();
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
for( auto trx_info: op.transactions_info ) {
const auto& btc_trx_obj = *btc_trx_idx.find( trx_info.transaction_id );
d.pw_vout_manager.delete_vouts_after( btc_trx_obj.pw_vin );
d.pw_vout_manager.mark_as_unused_vout( btc_trx_obj.pw_vin );
for( const auto& vout_id : btc_trx_obj.vouts ) {
const auto& vout_obj = *vouts_info_idx.find( vout_id );
d.modify( vout_obj, [&]( info_for_vout_object& obj ){
obj.used = false;
});
}
for( const auto& vin_id : btc_trx_obj.vins ) {
const auto& vin_obj = *vins_info_idx.find( vin_id );
if( trx_info.valid_vins.count( fc::sha256 ( vin_obj.out.hash_tx ) ) ) {
d.i_w_info.insert_info_for_vin( vin_obj.out, vin_obj.address, vin_obj.script, true );
}
d.remove( vin_obj );
}
d.bitcoin_confirmations.remove<sidechain::by_hash>( trx_info.transaction_id );
d.remove( btc_trx_obj );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
} }

View file

@ -33,4 +33,5 @@
#include "db_market.cpp"
#include "db_update.cpp"
#include "db_witness_schedule.cpp"
#include "db_notify.cpp"
#include "db_notify.cpp"
#include "db_sidechain.cpp"

View file

@ -71,9 +71,13 @@ void database::adjust_balance(account_id_type account, asset delta )
} else {
if( delta.amount < 0 )
FC_ASSERT( itr->get_balance() >= -delta, "Insufficient Balance: ${a}'s balance of ${b} is less than required ${r}", ("a",account(*this).name)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta)));
modify(*itr, [delta](account_balance_object& b) {
b.adjust_balance(delta);
});
if( delta.amount < 0 && itr->get_balance() == -delta ) {
remove(*itr);
} else {
modify(*itr, [delta](account_balance_object& b) {
b.adjust_balance(delta);
});
}
}
} FC_CAPTURE_AND_RETHROW( (account)(delta) ) }

View file

@ -44,11 +44,11 @@
#include <fc/smart_ref_impl.hpp>
namespace {
struct proposed_operations_digest_accumulator
{
typedef void result_type;
void operator()(const graphene::chain::proposal_create_operation& proposal)
{
for (auto& operation: proposal.proposed_ops)
@ -56,20 +56,20 @@ namespace {
proposed_operations_digests.push_back(fc::digest(operation.op));
}
}
//empty template method is needed for all other operation types
//we can ignore them, we are interested in only proposal_create_operation
template<class T>
void operator()(const T&)
void operator()(const T&)
{}
std::vector<fc::sha256> proposed_operations_digests;
};
std::vector<fc::sha256> gather_proposed_operations_digests(const graphene::chain::transaction& trx)
{
proposed_operations_digest_accumulator digest_accumulator;
for (auto& operation: trx.operations)
{
if( operation.which() != graphene::chain::operation::tag<graphene::chain::betting_market_group_create_operation>::value
@ -78,7 +78,7 @@ namespace {
else
edump( ("Found dup"));
}
return digest_accumulator.proposed_operations_digests;
}
}
@ -148,24 +148,24 @@ std::vector<block_id_type> database::get_block_ids_on_fork(block_id_type head_of
result.emplace_back(branches.first.back()->previous_id());
return result;
}
void database::check_tansaction_for_duplicated_operations(const signed_transaction& trx)
{
const auto& proposal_index = get_index<proposal_object>();
std::set<fc::sha256> existed_operations_digests;
proposal_index.inspect_all_objects( [&](const object& obj){
const proposal_object& proposal = static_cast<const proposal_object&>(obj);
auto proposed_operations_digests = gather_proposed_operations_digests( proposal.proposed_transaction );
existed_operations_digests.insert( proposed_operations_digests.begin(), proposed_operations_digests.end() );
});
for (auto& pending_transaction: _pending_tx)
{
auto proposed_operations_digests = gather_proposed_operations_digests(pending_transaction);
existed_operations_digests.insert(proposed_operations_digests.begin(), proposed_operations_digests.end());
}
auto proposed_operations_digests = gather_proposed_operations_digests(trx);
for (auto& digest: proposed_operations_digests)
{
@ -182,6 +182,9 @@ void database::check_tansaction_for_duplicated_operations(const signed_transacti
bool database::push_block(const signed_block& new_block, uint32_t skip)
{
// idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous));
send_btc_tx_flag = true;
bool result;
detail::with_skip_flags( *this, skip, [&]()
{
@ -339,6 +342,7 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
auto session = _undo_db.start_undo_session(true);
for( auto& op : proposal.proposed_transaction.operations )
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
remove_sidechain_proposal_object(proposal);
remove(proposal);
session.merge();
} catch ( const fc::exception& e ) {
@ -399,6 +403,10 @@ signed_block database::_generate_block(
auto maximum_block_size = get_global_properties().parameters.maximum_block_size;
size_t total_block_size = max_block_header_size;
if( !is_sidechain_fork_needed() ) {
processing_sidechain_proposals( witness_obj, block_signing_private_key );
}
signed_block pending_block;
//
@ -415,6 +423,27 @@ signed_block database::_generate_block(
_pending_tx_session.reset();
_pending_tx_session = _undo_db.start_undo_session();
if( !is_sidechain_fork_needed() ) {
auto op = create_send_btc_tx_proposal( witness_obj );
if( op.valid() ) {
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *op ) );
}
auto iss_op = create_bitcoin_issue_proposals( witness_obj );
if( iss_op.valid() ) {
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *iss_op ) );
}
auto revert_op = create_bitcoin_revert_proposals( witness_obj );
if( revert_op.valid() ) {
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( block_signing_private_key, *revert_op ) );
}
}
send_btc_tx_flag = false;
_current_witness_id = witness_obj.id;
uint64_t postponed_tx_count = 0;
// pop pending state (reset to head block state)
for( const processed_transaction& tx : _pending_tx )
@ -465,21 +494,21 @@ signed_block database::_generate_block(
pending_block.transaction_merkle_root = pending_block.calculate_merkle_root();
pending_block.witness = witness_id;
// Genesis witnesses start with a default initial secret
if( witness_obj.next_secret_hash == secret_hash_type::hash( secret_hash_type() ) )
pending_block.previous_secret = secret_hash_type();
else
{
secret_hash_type::encoder last_enc;
fc::raw::pack( last_enc, block_signing_private_key );
fc::raw::pack( last_enc, witness_obj.previous_secret );
pending_block.previous_secret = last_enc.result();
}
secret_hash_type::encoder next_enc;
fc::raw::pack( next_enc, block_signing_private_key );
fc::raw::pack( next_enc, pending_block.previous_secret );
pending_block.next_secret_hash = secret_hash_type::hash(next_enc.result());
// Genesis witnesses start with a default initial secret
if( witness_obj.next_secret_hash == secret_hash_type::hash( secret_hash_type() ) )
pending_block.previous_secret = secret_hash_type();
else
{
secret_hash_type::encoder last_enc;
fc::raw::pack( last_enc, block_signing_private_key );
fc::raw::pack( last_enc, witness_obj.previous_secret );
pending_block.previous_secret = last_enc.result();
}
secret_hash_type::encoder next_enc;
fc::raw::pack( next_enc, block_signing_private_key );
fc::raw::pack( next_enc, pending_block.previous_secret );
pending_block.next_secret_hash = secret_hash_type::hash(next_enc.result());
if( !(skip & skip_witness_signature) )
pending_block.sign( block_signing_private_key );
@ -566,6 +595,8 @@ void database::apply_block( const signed_block& next_block, uint32_t skip )
skip = ~0;// WE CAN SKIP ALMOST EVERYTHING
}
_current_witness_id = next_block.witness;
detail::with_skip_flags( *this, skip, [&]()
{
_apply_block( next_block );
@ -579,6 +610,11 @@ void database::_apply_block( const signed_block& next_block )
uint32_t skip = get_node_properties().skip_flags;
_applied_ops.clear();
if( head_block_time() > HARDFORK_SIDECHAIN_TIME && is_sidechain_fork_needed() )
{
perform_sidechain_fork();
}
FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) );
const witness_object& signing_witness = validate_block_header(skip, next_block);
@ -742,10 +778,10 @@ const witness_object& database::validate_block_header( uint32_t skip, const sign
FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()) );
const witness_object& witness = next_block.witness(*this);
//DLN: TODO: Temporarily commented out to test shuffle vs RNG scheduling algorithm for witnesses, this was causing shuffle agorithm to fail during create_witness test. This should be re-enabled for RNG, and maybe for shuffle too, don't really know for sure.
// FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "",
// FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "",
// ("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash)("null_secret_hash", secret_hash_type::hash( secret_hash_type())));
if( !(skip&skip_witness_signature) )
if( !(skip&skip_witness_signature) )
FC_ASSERT( next_block.validate_signee( witness.signing_key ) );
if( !(skip&skip_witness_schedule_check) )

View file

@ -94,7 +94,7 @@ node_property_object& database::node_properties()
uint32_t database::last_non_undoable_block_num() const
{
return head_block_num() - _undo_db.size();
return head_block_num() < _undo_db.size() ? 0 : head_block_num() - _undo_db.size();
}

View file

@ -49,6 +49,12 @@
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/match_object.hpp>
#include <graphene/chain/game_object.hpp>
#include <graphene/chain/info_for_vout_object.hpp>
#include <graphene/chain/info_for_used_vin_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <graphene/chain/sidechain_proposal_object.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <graphene/chain/sport_object.hpp>
@ -76,6 +82,10 @@
#include <graphene/chain/event_evaluator.hpp>
#include <graphene/chain/betting_market_evaluator.hpp>
#include <graphene/chain/tournament_evaluator.hpp>
#include <graphene/chain/withdraw_pbtc_evaluator.hpp>
#include <graphene/chain/bitcoin_address_evaluator.hpp>
#include <graphene/chain/bitcoin_transaction_evaluator.hpp>
#include <graphene/chain/sidechain_evaluator.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
@ -237,6 +247,12 @@ void database::initialize_evaluators()
register_evaluator<tournament_join_evaluator>();
register_evaluator<game_move_evaluator>();
register_evaluator<tournament_leave_evaluator>();
register_evaluator<withdraw_pbtc_evaluator>();
register_evaluator<bitcoin_address_create_evaluator>();
register_evaluator<bitcoin_transaction_send_evaluator>();
register_evaluator<bitcoin_transaction_sign_evaluator>();
register_evaluator<bitcoin_issue_evaluator>();
register_evaluator<bitcoin_transaction_revert_evaluator>();
}
void database::initialize_indexes()
@ -301,6 +317,13 @@ void database::initialize_indexes()
//add_index< primary_index<distributed_dividend_balance_object_index > >();
add_index< primary_index<pending_dividend_payout_balance_for_holder_object_index > >();
add_index< primary_index<total_distributed_dividend_balance_object_index > >();
add_index< primary_index<info_for_vout_index > >();
add_index< primary_index<info_for_used_vin_index > >();
add_index< primary_index<bitcoin_address_index > >();
add_index< primary_index<primary_wallet_vout_index > >();
add_index< primary_index<sidechain_proposal_index > >();
add_index< primary_index<bitcoin_transaction_index > >();
}
void database::init_genesis(const genesis_state_type& genesis_state)

View file

@ -36,15 +36,22 @@
namespace graphene { namespace chain {
database::database() :
_random_number_generator(fc::ripemd160().data())
i_w_info(*this), pw_vout_manager(*this), _random_number_generator(fc::ripemd160().data())
{
initialize_indexes();
initialize_evaluators();
context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
estimated_feerate = 0;
}
database::~database()
{
clear_pending();
secp256k1_context_destroy(context_sign);
context_sign = nullptr;
secp256k1_context_destroy(context_verify);
context_verify = nullptr;
}
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
@ -101,18 +108,21 @@ void database::reindex(fc::path data_dir, const genesis_state_type& initial_allo
skip_transaction_dupe_check |
skip_tapos_check |
skip_witness_schedule_check |
skip_authority_check);
skip_authority_check |
skip_btc_tx_sending);
else
apply_block(*block, skip_witness_signature |
skip_transaction_signatures |
skip_transaction_dupe_check |
skip_tapos_check |
skip_witness_schedule_check |
skip_authority_check);
skip_authority_check |
skip_btc_tx_sending);
}
if (!_slow_replays)
_undo_db.enable();
auto end = fc::time_point::now();
restore_bitcoin_transaction_status();
ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }

View file

@ -269,6 +269,30 @@ struct get_impacted_account_visitor
_impacted.insert( op.affiliate );
}
void operator()( const affiliate_referral_payout_operation& op ) { }
void operator()( const withdraw_pbtc_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_address_create_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_transaction_send_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_transaction_sign_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_issue_operation& op )
{
_impacted.insert( op.payer );
}
void operator()( const bitcoin_transaction_revert_operation& op )
{
_impacted.insert( op.payer );
}
};
void operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result )

View file

@ -0,0 +1,432 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <graphene/chain/sidechain_proposal_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/protocol/asset.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <sidechain/sidechain_condensing_tx.hpp>
#include <sidechain/sign_bitcoin_transaction.hpp>
#include <sidechain/sidechain_proposal_checker.hpp>
using namespace sidechain;
namespace graphene { namespace chain {
std::map< account_id_type, public_key_type> database::get_active_witnesses_keys() const
{
const auto& witnesses_by_id = get_index_type<witness_index>().indices().get<by_id>();
std::map< account_id_type, public_key_type > witnesses_keys;
auto& active_witnesses = get_global_properties().active_witnesses;
for( auto witness_id : active_witnesses ) {
const auto& witness_obj = witnesses_by_id.find( witness_id );
if( witness_obj != witnesses_by_id.end() ){
witnesses_keys.emplace( witness_obj->witness_account, witness_obj->signing_key );
}
}
return witnesses_keys;
}
bool database::is_sidechain_fork_needed() const
{
const auto& params = get_global_properties().parameters.extensions.value.sidechain_parameters;
return !params;
}
void database::perform_sidechain_fork()
{
const auto& sidechain_account = create<account_object>( [&]( account_object& obj ) {
obj.name = "sidechain_account";
obj.statistics = create<account_statistics_object>([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id;
obj.owner.weight_threshold = 5;
obj.active.weight_threshold = 5;
obj.membership_expiration_date = time_point_sec::maximum();
obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
});
const asset_object& new_asset = create<asset_object>( [&]( asset_object& obj ) {
obj.symbol = SIDECHAIN_SYMBOL;
obj.precision = SIDECHAIN_PRECISION_DIGITS;
obj.issuer = sidechain_account.get_id();
obj.options.max_supply = SIDECHAIN_MAX_SHARE_SUPPLY;
obj.options.issuer_permissions = 0;
obj.options.flags = 0;
obj.dynamic_asset_data_id = create<asset_dynamic_data_object>([&]( asset_dynamic_data_object& a ) { a.current_supply = 0; }).id;
});
modify( get_global_properties(), [&]( global_property_object& gpo ) {
sidechain_parameters_extension params_ext;
params_ext.managing_account = sidechain_account.get_id();
params_ext.asset_id = new_asset.get_id();
gpo.parameters.extensions.value.sidechain_parameters = params_ext;
if( gpo.pending_parameters )
gpo.pending_parameters->extensions.value.sidechain_parameters = params_ext;
});
auto global_properties = get_global_properties();
const auto& witnesses_idx = get_index_type<witness_index>().indices().get<by_id>();
std::vector<account_id_type> witness_accounts;
for( auto witness_id : global_properties.active_witnesses ) {
const auto& witness_obj = witnesses_idx.find( witness_id );
if( witness_obj != witnesses_idx.end() )
witness_accounts.push_back( witness_obj->witness_account );
}
modify( sidechain_account, [&]( account_object& obj ) {
for( auto& a : witness_accounts ) {
obj.owner.add_authority( a, 1 );
obj.active.add_authority( a, 1 );
}
});
create<bitcoin_address_object>( [&]( bitcoin_address_object& pw ) { // Create PW address
pw.address = btc_multisig_segwit_address( 5, get_active_witnesses_keys() );
pw.owner = sidechain_account.get_id();
pw.count_invalid_pub_key = 1;
});
pw_vout_manager.create_new_vout( {"", 0, 0 } );
}
const sidechain_parameters_extension& database::get_sidechain_params() const
{
const auto& params = get_global_properties().parameters.extensions.value.sidechain_parameters;
FC_ASSERT( params.valid() );
return *params;
}
const account_id_type& database::get_sidechain_account_id() const
{
return get_sidechain_params().managing_account;
}
const asset_id_type& database::get_sidechain_asset_id() const
{
return get_sidechain_params().asset_id;
}
bitcoin_address_object database::get_latest_PW() const
{
const auto& btc_addr_idx = get_index_type<bitcoin_address_index>().indices().get<by_owner>();
auto itr = btc_addr_idx.upper_bound( get_sidechain_account_id() );
return *(--itr);
}
int64_t database::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 );
}
void database::processing_sidechain_proposals( const witness_object& current_witness, const fc::ecc::private_key& private_key )
{
const auto& sidechain_proposal_idx = get_index_type<sidechain_proposal_index>().indices().get< by_id >();
const auto& proposal_idx = get_index_type<proposal_index>().indices().get< by_id >();
sidechain_proposal_checker checker( *this );
auto approve_propose = [ & ]( const proposal_id_type& id )
{
proposal_update_operation puo;
puo.fee_paying_account = current_witness.witness_account;
puo.proposal = id;
puo.active_approvals_to_add = { current_witness.witness_account };
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, puo ) );
};
for( auto& sidechain_proposal : sidechain_proposal_idx ) {
const auto& proposal = proposal_idx.find( sidechain_proposal.proposal_id );
FC_ASSERT( proposal != proposal_idx.end() );
if( !checker.check_reuse( proposal->proposed_transaction.operations.back() ) ) {
continue;
}
switch( sidechain_proposal.proposal_type ) {
case sidechain_proposal_type::ISSUE_BTC :{
approve_propose( proposal->id );
break;
}
case sidechain_proposal_type::SEND_BTC_TRANSACTION :{
bitcoin_transaction_send_operation op = proposal->proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
if( checker.check_bitcoin_transaction_send_operation( op ) &&
checker.check_witnesses_keys( current_witness, *proposal ) &&
checker.check_witness_opportunity_to_approve( current_witness, *proposal ) ) {
const auto& sign_operation = create_sign_btc_tx_operation( current_witness, private_key, proposal->id );
_pending_tx.insert( _pending_tx.begin(), create_signed_transaction( private_key, sign_operation ) );
}
break;
}
case sidechain_proposal_type::REVERT_BTC_TRANSACTION :{
bitcoin_transaction_revert_operation op = proposal->proposed_transaction.operations.back().get<bitcoin_transaction_revert_operation>();
if( checker.check_bitcoin_transaction_revert_operation( op ) )
approve_propose( proposal->id );
break;
}
}
}
}
full_btc_transaction database::create_btc_transaction( const std::vector<info_for_vin>& info_vins,
const std::vector<info_for_vout>& info_vouts,
const info_for_vin& info_pw_vin )
{
sidechain_condensing_tx ctx( info_vins, info_vouts );
if( info_pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
ctx.create_pw_vin( info_pw_vin );
}
const auto& pw_address = get_latest_PW().address;
if( info_vouts.size() > 0 ) {
ctx.create_vouts_for_witness_fee( pw_address.witnesses_keys );
}
const uint64_t& change = ctx.get_amount_vins() - ctx.get_amount_transfer_to_bitcoin();
if( change > 0 ) {
ctx.create_pw_vout( change, pw_address.get_witness_script() );
}
const uint64_t& size_fee = get_estimated_fee( sidechain_condensing_tx::get_estimate_tx_size( ctx.get_transaction(), pw_address.witnesses_keys.size() ), estimated_feerate.load() );
ctx.subtract_fee( size_fee, get_sidechain_params().percent_payment_to_witnesses );
return std::make_pair( ctx.get_transaction(), size_fee );
}
bool database::delete_invalid_amount_with_fee( sidechain::input_withdrawal_info& i_w_info, std::vector<info_for_vin> info_vins, const uint64_t& fee, const uint64_t& count_transfer_vout )
{
uint64_t size_fee = fee / ( info_vins.size() + count_transfer_vout );
bool deleted = false;
for( auto info: info_vins ) {
if( info.out.amount < size_fee ) {
wlog("Amount is too small, vin will be ignored (prevout hash tx = ${hash_tx})",("hash_tx", info.out.hash_tx));
i_w_info.remove_info_for_vin( info );
deleted = true;
}
}
return deleted;
};
database::full_btc_tx_and_new_vins database::create_tx_with_valid_vin( const std::vector<info_for_vout>& info_vouts,
const info_for_vin& info_pw_vin )
{
// we have to be sure, that we have enough BTC on vin to pay size_fee
full_btc_transaction btc_tx_and_fee;
std::vector<info_for_vin> info_vins = i_w_info.get_info_for_vins();
bool deleted = true;
while( info_vins.size() && deleted ) {
btc_tx_and_fee = create_btc_transaction( info_vins, info_vouts, info_pw_vin );
deleted = delete_invalid_amount_with_fee( i_w_info, info_vins, btc_tx_and_fee.second, info_vouts.size() );
info_vins = i_w_info.get_info_for_vins();
}
return std::make_pair( btc_tx_and_fee, info_vins );
}
fc::optional<operation> database::create_send_btc_tx_proposal( const witness_object& current_witness )
{
const auto& info_vins = i_w_info.get_info_for_vins();
const auto& info_vouts = i_w_info.get_info_for_vouts();
const auto& info_pw_vin = i_w_info.get_info_for_pw_vin();
if( info_pw_vin.valid() && ( info_vins.size() || info_vouts.size() ) ) {
// tx_and_new_vins.first = sidechain::full_btc_transaction; tx_and_new_vins.second = std::vector<info_for_vin>
const auto& tx_and_new_vins = create_tx_with_valid_vin( info_vouts, *info_pw_vin );
if( !tx_and_new_vins.second.size() && !info_vouts.size() )
return fc::optional<operation>();
const auto& btc_tx_and_size_fee = tx_and_new_vins.first;
bitcoin_transaction_send_operation btc_send_op;
btc_send_op.payer = get_sidechain_account_id();
btc_send_op.pw_vin = *info_pw_vin;
btc_send_op.vins = tx_and_new_vins.second;
for( auto& out : info_vouts ) {
btc_send_op.vouts.push_back( out.get_id() );
}
btc_send_op.transaction = btc_tx_and_size_fee.first;
btc_send_op.fee_for_size = btc_tx_and_size_fee.second;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = current_witness.witness_account;
proposal_op.proposed_ops.push_back( op_wrapper( btc_send_op ) );
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
return proposal_op;
}
return fc::optional<operation>();
}
signed_transaction database::create_signed_transaction( const private_key& signing_private_key, const operation& op )
{
signed_transaction processed_trx;
auto dyn_props = get_dynamic_global_properties();
processed_trx.set_reference_block( dyn_props.head_block_id );
processed_trx.set_expiration( head_block_time() + get_global_properties().parameters.maximum_time_until_expiration );
processed_trx.operations.push_back( op );
current_fee_schedule().set_fee( processed_trx.operations.back() );
processed_trx.sign( signing_private_key, get_chain_id() );
return processed_trx;
}
operation database::create_sign_btc_tx_operation( const witness_object& current_witness, const private_key_type& privkey,
const proposal_id_type& proposal_id )
{
const auto& proposal_idx = get_index_type<proposal_index>().indices().get<by_id>();
const auto& proposal_itr = proposal_idx.find( proposal_id );
bitcoin_transaction_send_operation op = proposal_itr->proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
bitcoin_transaction_sign_operation sign_operation;
sign_operation.payer = current_witness.witness_account;
sign_operation.proposal_id = proposal_id;
const auto secret = privkey.get_secret();
bytes key(secret.data(), secret.data() + secret.data_size());
auto vins = op.vins;
if( op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
vins.insert( vins.begin(), op.pw_vin );
}
std::vector<bytes> redeem_scripts( i_w_info.get_redeem_scripts( vins ) );
std::vector<uint64_t> amounts( i_w_info.get_amounts( vins ) );
sign_operation.signatures = sign_witness_transaction_part( op.transaction, redeem_scripts, amounts, key, context_sign, 1 );
return sign_operation;
}
void database::remove_sidechain_proposal_object( const proposal_object& proposal )
{ try {
if( proposal.proposed_transaction.operations.size() == 1 &&
( proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_transaction_send_operation>::value ||
proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_issue_operation>::value ||
proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_transaction_revert_operation>::value ) )
{
const auto& sidechain_proposal_idx = get_index_type<sidechain_proposal_index>().indices().get<by_proposal>();
auto sidechain_proposal_itr = sidechain_proposal_idx.find( proposal.id );
if( sidechain_proposal_itr == sidechain_proposal_idx.end() ) {
return;
}
remove( *sidechain_proposal_itr );
}
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
void database::roll_back_vin_and_vout( const proposal_object& proposal )
{
if( proposal.proposed_transaction.operations.size() == 1 &&
proposal.proposed_transaction.operations.back().which() == operation::tag<bitcoin_transaction_send_operation>::value )
{
bitcoin_transaction_send_operation op = proposal.proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
if( pw_vout_manager.get_vout( op.pw_vin.identifier ).valid() ) {
pw_vout_manager.mark_as_unused_vout( op.pw_vin.identifier );
}
for( const auto& vin : op.vins ) {
const auto& v = i_w_info.find_info_for_vin( vin.identifier );
if( v.valid() ) {
i_w_info.mark_as_unused_vin( vin );
}
}
for( const auto& vout : op.vouts ) {
const auto& v = i_w_info.find_info_for_vout( vout );
if( v.valid() ) {
i_w_info.mark_as_unused_vout( *v );
}
}
remove_sidechain_proposal_object( proposal );
}
}
fc::optional<operation> database::create_bitcoin_issue_proposals( const witness_object& current_witness )
{
std::vector<fc::sha256> trx_ids;
bitcoin_confirmations.safe_for<by_confirmed_and_not_used>([&]( btc_tx_confirmations_index::index<by_confirmed_and_not_used>::type::iterator itr_b, btc_tx_confirmations_index::index<by_confirmed_and_not_used>::type::iterator itr_e ){
for(auto iter = itr_b; iter != itr_e; iter++) {
if( !iter->is_confirmed_and_not_used() ) return;
const auto& btc_trx_idx = get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& btc_tx = btc_trx_idx.find( iter->transaction_id );
if( btc_tx == btc_trx_idx.end() ) continue;
trx_ids.push_back( iter->transaction_id );
}
});
if( trx_ids.size() ) {
bitcoin_issue_operation issue_op;
issue_op.payer = get_sidechain_account_id();
issue_op.transaction_ids = trx_ids;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = current_witness.witness_account;
proposal_op.proposed_ops.push_back( op_wrapper( issue_op ) );
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
return fc::optional<operation>( proposal_op );
}
return fc::optional<operation>();
}
fc::optional<operation> database::create_bitcoin_revert_proposals( const witness_object& current_witness )
{
using iter_by_missing = btc_tx_confirmations_index::index<by_missing_and_not_used>::type::iterator;
std::vector<revert_trx_info> trx_info;
bitcoin_confirmations.safe_for<by_missing_and_not_used>([&]( iter_by_missing itr_b, iter_by_missing itr_e ){
for(auto iter = itr_b; iter != itr_e; iter++) {
if( !iter->is_missing_and_not_used() ) return;
const auto& btc_trx_idx = get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& btc_tx = btc_trx_idx.find( iter->transaction_id );
if( btc_tx == btc_trx_idx.end() ) continue;
trx_info.push_back( revert_trx_info( iter->transaction_id, iter->valid_vins ) );
}
});
if( trx_info.size() ) {
bitcoin_transaction_revert_operation revert_op;
revert_op.payer = get_sidechain_account_id();
revert_op.transactions_info = trx_info;
proposal_create_operation proposal_op;
proposal_op.fee_paying_account = current_witness.witness_account;
proposal_op.proposed_ops.push_back( op_wrapper( revert_op ) );
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
return fc::optional<operation>( proposal_op );
}
return fc::optional<operation>();
}
void database::restore_bitcoin_transaction_status()
{
const auto& btc_tx_idx = get_index_type<bitcoin_transaction_index>().indices().get<by_id>();
for( const auto& bto : btc_tx_idx ) {
std::set<fc::sha256> valid_vins;
for( const auto& v : bto.transaction.vin ) {
valid_vins.insert( v.prevout.hash );
}
bitcoin_confirmations.insert( bitcoin_transaction_confirmations( bto.transaction.get_txid(), valid_vins ) );
}
}
} }

View file

@ -253,6 +253,7 @@ void database::clear_expired_proposals()
elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
("proposal", proposal)("error", e.to_detail_string()));
}
roll_back_vin_and_vout(proposal);
remove(proposal);
}
}

View file

@ -0,0 +1,4 @@
// sidechain fardfork
#ifndef HARDFORK_SIDECHAIN_TIME
#define HARDFORK_SIDECHAIN_TIME (fc::time_point_sec( 1550000000 ))
#endif

View file

@ -0,0 +1,20 @@
#pragma once
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
class bitcoin_address_create_evaluator : public evaluator<bitcoin_address_create_evaluator>
{
public:
typedef bitcoin_address_create_operation operation_type;
void_result do_evaluate(const bitcoin_address_create_operation& op);
object_id_type do_apply(const bitcoin_address_create_operation& op);
public_key_type pubkey_from_id( object_id_type id );
};
} } // graphene::chain

View file

@ -0,0 +1,47 @@
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/witness_object.hpp>
#include <sidechain/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,55 @@
#pragma once
#include <graphene/chain/evaluator.hpp>
namespace sidechain { class info_for_vin; }
namespace graphene { namespace chain {
class bitcoin_transaction_send_evaluator : public evaluator<bitcoin_transaction_send_evaluator>
{
public:
typedef bitcoin_transaction_send_operation operation_type;
void_result do_evaluate( const bitcoin_transaction_send_operation& op );
object_id_type do_apply( const bitcoin_transaction_send_operation& op );
std::vector<fc::sha256> create_transaction_vins( bitcoin_transaction_send_operation& op );
void finalize_bitcoin_transaction( bitcoin_transaction_send_operation& op );
void send_bitcoin_transaction( const bitcoin_transaction_object& btc_tx );
};
class bitcoin_transaction_sign_evaluator : public evaluator<bitcoin_transaction_sign_evaluator>
{
public:
typedef bitcoin_transaction_sign_operation operation_type;
void_result do_evaluate( const bitcoin_transaction_sign_operation& op );
void_result do_apply( const bitcoin_transaction_sign_operation& op );
void update_proposal( const bitcoin_transaction_sign_operation& op );
bool check_sigs( const sidechain::bytes& key_data, const std::vector<sidechain::bytes>& sigs,
const std::vector<sidechain::info_for_vin>& info_for_vins, const sidechain::bitcoin_transaction& tx );
};
class bitcoin_transaction_revert_evaluator : public evaluator<bitcoin_transaction_revert_evaluator>
{
public:
typedef bitcoin_transaction_revert_operation operation_type;
void_result do_evaluate( const bitcoin_transaction_revert_operation& op );
void_result do_apply( const bitcoin_transaction_revert_operation& op );
};
} } // graphene::chain

View file

@ -0,0 +1,41 @@
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/bitcoin_transaction.hpp>
namespace graphene { namespace chain {
class bitcoin_transaction_object : public abstract_object<bitcoin_transaction_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = bitcoin_transaction_object_type;
bitcoin_transaction_id_type get_id()const { return id; }
fc::sha256 pw_vin;
std::vector< fc::sha256 > vins;
std::vector< info_for_vout_id_type > vouts;
sidechain::bitcoin_transaction transaction;
fc::sha256 transaction_id;
uint64_t fee_for_size;
};
struct by_transaction_id;
typedef boost::multi_index_container<
bitcoin_transaction_object,
indexed_by<
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
ordered_unique< tag< by_transaction_id >, member< bitcoin_transaction_object, fc::sha256, &bitcoin_transaction_object::transaction_id > >
>
> bitcoin_transaction_multi_index_container;
typedef generic_index<bitcoin_transaction_object, bitcoin_transaction_multi_index_container> bitcoin_transaction_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::chain::object), (pw_vin)(vins)(vouts)(transaction)(transaction_id)(fee_for_size) )

View file

@ -228,3 +228,18 @@
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
#define GPOS_PERIOD (60*60*24*30*6) // 6 months
#define GPOS_SUBPERIOD (60*60*24*30) // 1 month
////////////////////////////////////////////////////////////////////// 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

@ -40,9 +40,20 @@
#include <graphene/chain/protocol/protocol.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/bitcoin_transaction_confirmations.hpp>
#include <sidechain/primary_wallet_vout_manager.hpp>
#include <atomic>
#include <fc/log/logger.hpp>
#include <map>
#include <secp256k1.h>
using namespace fc::ecc;
using sidechain::bitcoin_transaction;
using sidechain::info_for_vin;
using sidechain::info_for_vout;
namespace graphene { namespace chain {
using graphene::db::abstract_object;
@ -78,7 +89,8 @@ namespace graphene { namespace chain {
skip_assert_evaluation = 1 << 8, ///< used while reindexing
skip_undo_history_check = 1 << 9, ///< used while reindexing
skip_witness_schedule_check = 1 << 10, ///< used while reindexing
skip_validate = 1 << 11 ///< used prior to checkpoint, skips validate() call on transaction
skip_validate = 1 << 11, ///< used prior to checkpoint, skips validate() call on transaction
skip_btc_tx_sending = 1 << 12
};
/**
@ -136,7 +148,7 @@ namespace graphene { namespace chain {
void add_checkpoints( const flat_map<uint32_t,block_id_type>& checkpts );
const flat_map<uint32_t,block_id_type> get_checkpoints()const { return _checkpoints; }
bool before_last_checkpoint()const;
void check_tansaction_for_duplicated_operations(const signed_transaction& trx);
bool push_block( const signed_block& b, uint32_t skip = skip_nothing );
@ -177,7 +189,7 @@ namespace graphene { namespace chain {
// most plugins should use the const version of get_applied_operations
const vector<optional< operation_history_object > >& get_applied_operations()const;
// the account_history plugin uses the non-const version. When it decides to track an
// the account_history plugin uses the non-const version. When it decides to track an
// operation and assigns an operation_id to it, it will store that id into the operation
// history object so other plugins that evaluate later can reference it.
vector<optional< operation_history_object > >& get_applied_operations();
@ -285,6 +297,7 @@ namespace graphene { namespace chain {
uint32_t last_non_undoable_block_num() const;
//////////////////// db_init.cpp ////////////////////
void initialize_evaluators();
@ -328,7 +341,7 @@ namespace graphene { namespace chain {
* to newly created VBID and return it.
*
* Otherwise, credit amount to ovbid.
*
*
* @return ID of newly created VBO, but only if VBO was created.
*/
optional< vesting_balance_id_type > deposit_lazy_vesting(
@ -506,6 +519,65 @@ namespace graphene { namespace chain {
void perform_account_maintenance(std::tuple<Types...> helpers);
///@}
///@}
//////////////////// db_sidechain.cpp ////////////////////
public:
using full_btc_tx_and_new_vins = std::pair< sidechain::full_btc_transaction, std::vector<info_for_vin> >;
std::map< account_id_type, public_key_type> get_active_witnesses_keys() const;
bool is_sidechain_fork_needed() const;
void perform_sidechain_fork();
bitcoin_address_object get_latest_PW() const;
const sidechain::sidechain_parameters_extension& get_sidechain_params() const;
const account_id_type& get_sidechain_account_id() const;
const asset_id_type& get_sidechain_asset_id() const;
int64_t get_estimated_fee( size_t tx_vsize, uint64_t estimated_feerate );
void processing_sidechain_proposals( const witness_object& current_witness, const private_key& signing_private_key );
inline bool delete_invalid_amount_with_fee( sidechain::input_withdrawal_info& i_w_info, std::vector<info_for_vin> info_vins, const uint64_t& fee, const uint64_t& count_transfer_vout );
full_btc_tx_and_new_vins create_tx_with_valid_vin( const std::vector<info_for_vout>& info_vouts,
const info_for_vin& info_pw_vin );
sidechain::full_btc_transaction create_btc_transaction( const std::vector<info_for_vin>& info_vins,
const std::vector<info_for_vout>& info_vouts,
const info_for_vin& info_pw_vin );
fc::optional<operation> create_send_btc_tx_proposal( const witness_object& current_witness );
operation create_sign_btc_tx_operation( const witness_object& current_witness, const private_key_type& privkey,
const proposal_id_type& proposal_id );
signed_transaction create_signed_transaction( const private_key& signing_private_key, const operation& op );
void remove_sidechain_proposal_object( const proposal_object& proposal );
void roll_back_vin_and_vout( const proposal_object& proposal );
fc::optional<operation> create_bitcoin_issue_proposals( const witness_object& current_witness );
fc::optional<operation> create_bitcoin_revert_proposals( const witness_object& current_witness );
fc::signal<void( const bitcoin_transaction& )> send_btc_tx;
sidechain::input_withdrawal_info i_w_info;
sidechain::thread_safe_index<sidechain::btc_tx_confirmations_index> bitcoin_confirmations;
sidechain::primary_wallet_vout_manager pw_vout_manager;
std::atomic<uint64_t> estimated_feerate;
secp256k1_context_t* context_sign;
secp256k1_context_t* context_verify;
bool send_btc_tx_flag = true;
witness_id_type _current_witness_id;
private:
void restore_bitcoin_transaction_status(); // db_sidechain
vector< processed_transaction > _pending_tx;
fork_database _fork_db;

View file

@ -0,0 +1,39 @@
#pragma once
#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 {
class info_for_used_vin_object : public abstract_object<info_for_used_vin_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = info_for_used_vin_object_type;
info_for_used_vin_id_type get_id()const { return id; }
fc::sha256 identifier;
sidechain::prev_out out;
std::string address;
sidechain::bytes script;
};
struct by_id;
struct by_identifier;
typedef boost::multi_index_container<
info_for_used_vin_object,
indexed_by<
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
ordered_unique<tag<by_identifier>, member<info_for_used_vin_object, fc::sha256, &info_for_used_vin_object::identifier>>
>
> info_for_used_vin_multi_index_container;
typedef generic_index<info_for_used_vin_object, info_for_used_vin_multi_index_container> info_for_used_vin_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::info_for_used_vin_object, (graphene::chain::object), (identifier)(out)(address)(script) )

View file

@ -0,0 +1,55 @@
#pragma once
#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 {
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,44 @@
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/types.hpp>
namespace graphene { namespace chain {
class primary_wallet_vout_object : public abstract_object<primary_wallet_vout_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = primary_wallet_vout_object_type;
struct comparer {
bool operator()(const primary_wallet_vout_object& lhs, const primary_wallet_vout_object& rhs) const;
};
primary_wallet_vout_id_type get_id() const { return id; }
sidechain::prev_out vout;
fc::sha256 hash_id; // ( sha256(hash + n_vout) - 8 bytes ) + id_obj
bool confirmed;
bool used;
};
struct by_hash_id;
typedef boost::multi_index_container<
primary_wallet_vout_object,
indexed_by<
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
ordered_unique< tag< by_hash_id >, member< primary_wallet_vout_object, fc::uint256, &primary_wallet_vout_object::hash_id > >
>
> primary_wallet_vout_multi_index_container;
typedef generic_index<primary_wallet_vout_object, primary_wallet_vout_multi_index_container> primary_wallet_vout_index;
} }
FC_REFLECT_DERIVED( graphene::chain::primary_wallet_vout_object, (graphene::chain::object), (vout)(hash_id)(confirmed)(used) )

View file

@ -30,6 +30,25 @@
namespace graphene { namespace chain {
class sidechain_hardfork_visitor
{
public:
typedef void result_type;
database& db;
proposal_id_type prop_id;
sidechain_hardfork_visitor( database& _db, const proposal_id_type& _prop_id ) : db( _db ), prop_id( _prop_id ) {}
template<typename T>
void operator()( const T &v ) const {}
void operator()( const bitcoin_transaction_send_operation &v );
void operator()( const bitcoin_issue_operation &v );
void operator()( const bitcoin_transaction_revert_operation &v );
};
class proposal_create_evaluator : public evaluator<proposal_create_evaluator>
{
public:

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

@ -26,6 +26,7 @@
#include <graphene/chain/protocol/ext.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <fc/smart_ref_fwd.hpp>
#include <sidechain/sidechain_parameters.hpp>
#include <graphene/chain/hardfork.hpp>
@ -39,6 +40,7 @@ namespace graphene { namespace chain {
optional< uint16_t > betting_rake_fee_percentage;
optional< flat_map<bet_multiplier_type, bet_multiplier_type> > permitted_betting_odds_increments;
optional< uint16_t > live_betting_delay_time;
optional< sidechain::sidechain_parameters_extension > sidechain_parameters;
/* gpos parameters */
optional < uint32_t > gpos_period;
optional < uint32_t > gpos_subperiod;

View file

@ -44,6 +44,10 @@
#include <graphene/chain/protocol/event.hpp>
#include <graphene/chain/protocol/betting_market.hpp>
#include <graphene/chain/protocol/tournament.hpp>
#include <graphene/chain/protocol/withdraw_pbtc.hpp>
#include <graphene/chain/protocol/bitcoin_address.hpp>
#include <graphene/chain/protocol/bitcoin_transaction.hpp>
#include <graphene/chain/protocol/sidechain.hpp>
namespace graphene { namespace chain {
@ -129,7 +133,13 @@ namespace graphene { namespace chain {
sport_delete_operation,
event_group_delete_operation,
affiliate_payout_operation, // VIRTUAL
affiliate_referral_payout_operation // VIRTUAL
affiliate_referral_payout_operation, // VIRTUAL
withdraw_pbtc_operation,
bitcoin_address_create_operation,
bitcoin_transaction_send_operation,
bitcoin_transaction_sign_operation,
bitcoin_issue_operation,
bitcoin_transaction_revert_operation
> operation;
/// @} // operations group

View file

@ -0,0 +1,28 @@
#pragma once
#include <graphene/chain/protocol/base.hpp>
namespace graphene { namespace chain {
struct bitcoin_issue_operation : public base_operation
{
struct fee_parameters_type {
uint64_t fee = 0;
};
asset fee;
account_id_type payer;
std::vector<fc::sha256> transaction_ids;
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;
return fee_required;
}
};
} } // graphene::chain
FC_REFLECT( graphene::chain::bitcoin_issue_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::bitcoin_issue_operation, (fee)(payer)(transaction_ids) )

View file

@ -145,6 +145,12 @@ namespace graphene { namespace chain {
betting_market_group_object_type,
betting_market_object_type,
bet_object_type,
info_for_vout_object_type,
info_for_used_vin_object_type,
bitcoin_address_object_type,
primary_wallet_vout_object_type,
sidechain_proposal_object_type,
bitcoin_transaction_object_type,
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
@ -202,32 +208,44 @@ namespace graphene { namespace chain {
class betting_market_group_object;
class betting_market_object;
class bet_object;
class info_for_vout_object;
class info_for_used_vin_object;
class bitcoin_address_object;
class primary_wallet_vout_object;
class sidechain_proposal_object;
class bitcoin_transaction_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;
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type;
typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type;
typedef object_id< protocol_ids, operation_history_object_type, operation_history_object> operation_history_id_type;
typedef object_id< protocol_ids, withdraw_permission_object_type,withdraw_permission_object> withdraw_permission_id_type;
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
typedef object_id< protocol_ids, tournament_object_type, tournament_object> tournament_id_type;
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
typedef object_id< protocol_ids, sport_object_type, sport_object> sport_id_type;
typedef object_id< protocol_ids, event_group_object_type, event_group_object> event_group_id_type;
typedef object_id< protocol_ids, event_object_type, event_object> event_id_type;
typedef object_id< protocol_ids, betting_market_rules_object_type, betting_market_rules_object> betting_market_rules_id_type;
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
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;
typedef object_id< protocol_ids, force_settlement_object_type, force_settlement_object> force_settlement_id_type;
typedef object_id< protocol_ids, committee_member_object_type, committee_member_object> committee_member_id_type;
typedef object_id< protocol_ids, witness_object_type, witness_object> witness_id_type;
typedef object_id< protocol_ids, limit_order_object_type, limit_order_object> limit_order_id_type;
typedef object_id< protocol_ids, call_order_object_type, call_order_object> call_order_id_type;
typedef object_id< protocol_ids, custom_object_type, custom_object> custom_id_type;
typedef object_id< protocol_ids, proposal_object_type, proposal_object> proposal_id_type;
typedef object_id< protocol_ids, operation_history_object_type, operation_history_object> operation_history_id_type;
typedef object_id< protocol_ids, withdraw_permission_object_type, withdraw_permission_object> withdraw_permission_id_type;
typedef object_id< protocol_ids, vesting_balance_object_type, vesting_balance_object> vesting_balance_id_type;
typedef object_id< protocol_ids, worker_object_type, worker_object> worker_id_type;
typedef object_id< protocol_ids, balance_object_type, balance_object> balance_id_type;
typedef object_id< protocol_ids, tournament_object_type, tournament_object> tournament_id_type;
typedef object_id< protocol_ids, tournament_details_object_type, tournament_details_object> tournament_details_id_type;
typedef object_id< protocol_ids, match_object_type, match_object> match_id_type;
typedef object_id< protocol_ids, game_object_type, game_object> game_id_type;
typedef object_id< protocol_ids, sport_object_type, sport_object> sport_id_type;
typedef object_id< protocol_ids, event_group_object_type, event_group_object> event_group_id_type;
typedef object_id< protocol_ids, event_object_type, event_object> event_id_type;
typedef object_id< protocol_ids, betting_market_rules_object_type, betting_market_rules_object> betting_market_rules_id_type;
typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type;
typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type;
typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type;
typedef object_id< protocol_ids, info_for_vout_object_type, info_for_vout_object> info_for_vout_id_type;
typedef object_id< protocol_ids, info_for_used_vin_object_type, info_for_used_vin_object> info_for_used_vin_id_type;
typedef object_id< protocol_ids, bitcoin_address_object_type, bitcoin_address_object> bitcoin_address_id_type;
typedef object_id< protocol_ids, primary_wallet_vout_object_type, primary_wallet_vout_object> primary_wallet_vout_id_type;
typedef object_id< protocol_ids, sidechain_proposal_object_type, sidechain_proposal_object> sidechain_proposal_id_type;
typedef object_id< protocol_ids, bitcoin_transaction_object_type, bitcoin_transaction_object> bitcoin_transaction_id_type;
// implementation types
class global_property_object;

View file

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

View file

@ -0,0 +1,26 @@
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/evaluator.hpp>
#include <graphene/chain/exceptions.hpp>
namespace graphene { namespace chain {
struct bitcoin_issue_evaluator : public evaluator< bitcoin_issue_evaluator >
{
typedef bitcoin_issue_operation operation_type;
void_result do_evaluate( const bitcoin_issue_operation& op );
void_result do_apply( const bitcoin_issue_operation& op );
void add_issue( const bitcoin_transaction_object& btc_obj );
void clear_btc_transaction_information( const bitcoin_transaction_object& btc_obj );
std::vector<uint64_t> get_amounts_to_issue( std::vector<fc::sha256> vins_identifier );
std::vector<account_id_type> get_accounts_to_issue( std::vector<fc::sha256> vins_identifier );
};
} } // graphene::chain

View file

@ -0,0 +1,35 @@
#pragma once
#include <graphene/db/generic_index.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/types.hpp>
namespace graphene { namespace chain {
class sidechain_proposal_object : public abstract_object<sidechain_proposal_object>
{
public:
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = sidechain_proposal_object_type;
sidechain_proposal_id_type get_id()const { return id; }
proposal_id_type proposal_id;
sidechain::sidechain_proposal_type proposal_type;
};
struct by_proposal;
struct by_type;
typedef boost::multi_index_container<
sidechain_proposal_object,
indexed_by<
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
ordered_unique< tag< by_proposal >, member< sidechain_proposal_object, proposal_id_type, &sidechain_proposal_object::proposal_id > >,
ordered_non_unique< tag< by_type >, member< sidechain_proposal_object, sidechain::sidechain_proposal_type, &sidechain_proposal_object::proposal_type > >
>
> sidechain_multi_index_container;
typedef generic_index<sidechain_proposal_object, sidechain_multi_index_container> sidechain_proposal_index;
} } // graphene::chain
FC_REFLECT_DERIVED( graphene::chain::sidechain_proposal_object, (graphene::chain::object), (proposal_id)(proposal_type) )

View file

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

View file

@ -0,0 +1,141 @@
#include <graphene/chain/sidechain_evaluator.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <graphene/chain/info_for_used_vin_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
namespace graphene { namespace chain {
void_result bitcoin_issue_evaluator::do_evaluate( const bitcoin_issue_operation& op )
{ try {
database& d = db();
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
const auto& btc_addr_idx = d.get_index_type<bitcoin_address_index>().indices().get<by_address>();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
FC_ASSERT( op.payer == db().get_sidechain_account_id() );
for( const auto& id: op.transaction_ids ) {
const auto& btc_itr = btc_trx_idx.find( id );
FC_ASSERT( btc_itr != btc_trx_idx.end() );
for( auto& vin_id : btc_itr->vins ) {
const auto& itr = vins_info_idx.find( vin_id );
FC_ASSERT( itr != vins_info_idx.end() );
auto addr_itr = btc_addr_idx.find( itr->address );
FC_ASSERT( addr_itr != btc_addr_idx.end() );
}
for( auto& vout_id : btc_itr->vouts )
FC_ASSERT( vouts_info_idx.find( vout_id ) != vouts_info_idx.end() );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result bitcoin_issue_evaluator::do_apply( const bitcoin_issue_operation& op )
{ try {
database& d = db();
const auto& btc_trx_idx = d.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
for( const auto& id: op.transaction_ids ) {
const auto& btc_obj = *btc_trx_idx.find( id );
add_issue( btc_obj );
d.pw_vout_manager.confirm_vout( btc_obj.pw_vin );
clear_btc_transaction_information( btc_obj );
}
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
void bitcoin_issue_evaluator::add_issue( const bitcoin_transaction_object& btc_obj )
{
database& d = db();
const auto& accounts_to_issue = get_accounts_to_issue( btc_obj.vins );
const auto& amounts_to_issue = get_amounts_to_issue( btc_obj.vins );
uint64_t fee_deduction = btc_obj.fee_for_size / ( btc_obj.vins.size() + btc_obj.vouts.size() );
if( btc_obj.fee_for_size % ( btc_obj.vins.size() + btc_obj.vouts.size() ) != 0 ) {
fee_deduction += 1;
}
bool skip_fee_old = trx_state->skip_fee;
bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check;
trx_state->skip_fee = true;
trx_state->skip_fee_schedule_check = true;
for( size_t i = 0; i < accounts_to_issue.size(); i++ ){
asset_issue_operation issue_op;
issue_op.issuer = d.get_sidechain_account_id();
issue_op.asset_to_issue = asset( amounts_to_issue[i] - fee_deduction, d.get_sidechain_asset_id() );
issue_op.issue_to_account = accounts_to_issue[i];
d.apply_operation( *trx_state, issue_op );
}
trx_state->skip_fee = skip_fee_old;
trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old;
}
void bitcoin_issue_evaluator::clear_btc_transaction_information( const bitcoin_transaction_object& btc_obj )
{
database& d = db();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
const auto& vouts_info_idx = d.get_index_type<info_for_vout_index>().indices().get<by_id>();
for( auto& vin_id : btc_obj.vins ) {
auto vin_itr = vins_info_idx.find( vin_id );
d.remove( *vin_itr );
}
for( auto& vout_id : btc_obj.vouts ) {
auto vout_itr = vouts_info_idx.find( vout_id );
d.remove( *vout_itr );
}
auto trx_approvals = d.bitcoin_confirmations.find<sidechain::by_hash>( btc_obj.transaction_id );
if( trx_approvals.valid() ) {
d.bitcoin_confirmations.remove<sidechain::by_hash>( btc_obj.transaction_id );
}
d.remove( btc_obj );
}
std::vector<uint64_t> bitcoin_issue_evaluator::get_amounts_to_issue( std::vector<fc::sha256> vins_identifier )
{
database& d = db();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
std::vector<uint64_t> result;
for( auto& identifier : vins_identifier ) {
auto vin_itr = vins_info_idx.find( identifier );
result.push_back( vin_itr->out.amount );
}
return result;
}
std::vector<account_id_type> bitcoin_issue_evaluator::get_accounts_to_issue( std::vector<fc::sha256> vins_identifier )
{
database& d = db();
const auto& btc_addr_idx = d.get_index_type<bitcoin_address_index>().indices().get<by_address>();
const auto& vins_info_idx = d.get_index_type<info_for_used_vin_index>().indices().get<by_identifier>();
std::vector<account_id_type> result;
for( auto& identifier : vins_identifier ) {
auto vin_itr = vins_info_idx.find( identifier );
auto addr_itr = btc_addr_idx.find( vin_itr->address );
result.push_back( addr_itr->owner );
}
return result;
}
} } // graphene::chain

View file

@ -0,0 +1,85 @@
#include <graphene/chain/withdraw_pbtc_evaluator.hpp>
#include <graphene/chain/info_for_vout_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <sidechain/sidechain_condensing_tx.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/bitcoin_address.hpp>
#include <sidechain/utils.hpp>
namespace graphene { namespace chain {
void_result withdraw_pbtc_evaluator::do_evaluate(const withdraw_pbtc_operation& op)
{
database& d = db();
FC_ASSERT( !d.is_sidechain_fork_needed() );
FC_ASSERT( op.data.size() > 0 );
type = bitcoin_address( op.data ).get_type();
FC_ASSERT( type != payment_type::NULLDATA , "Invalid address type." );
FC_ASSERT( check_amount_higher_than_fee( op ) );
asset acc_balance = db().get_balance( op.payer, d.get_sidechain_asset_id() );
FC_ASSERT( acc_balance.amount.value >= op.amount );
return void_result();
}
void_result withdraw_pbtc_evaluator::do_apply(const withdraw_pbtc_operation& op)
{
db().i_w_info.insert_info_for_vout( op.payer, op.data, op.amount );
reserve_issue( op );
return void_result();
}
void withdraw_pbtc_evaluator::reserve_issue( const withdraw_pbtc_operation& op )
{
database& d = db();
bool skip_fee_old = trx_state->skip_fee;
bool skip_fee_schedule_check_old = trx_state->skip_fee_schedule_check;
trx_state->skip_fee = true;
trx_state->skip_fee_schedule_check = true;
asset_reserve_operation reserve_op;
reserve_op.amount_to_reserve = asset( op.amount, d.get_sidechain_asset_id() );
reserve_op.payer = op.payer;
d.apply_operation( *trx_state, reserve_op );
trx_state->skip_fee = skip_fee_old;
trx_state->skip_fee_schedule_check = skip_fee_schedule_check_old;
}
bool withdraw_pbtc_evaluator::check_amount_higher_than_fee( const withdraw_pbtc_operation& op ) {
database& d = db();
info_for_vout_object obj;
obj.payer = op.payer;
obj.address = op.data;
obj.amount = op.amount;
obj.used = false;
const auto& pw_address = d.get_latest_PW().address;
sidechain::info_for_vin pw_vin;
pw_vin.identifier = fc::sha256( std::string(64, '1') );
pw_vin.out.hash_tx = std::string(64, '1');
pw_vin.out.n_vout = 0;
pw_vin.out.amount = 2*op.amount;
pw_vin.address = pw_address.get_address();
pw_vin.script = pw_address.get_witness_script();
const auto& mock_trx = d.create_btc_transaction( {}, { obj }, pw_vin );
if( op.amount < mock_trx.second )
return false;
uint64_t fee_for_witnesses = ( (op.amount - mock_trx.second) * d.get_sidechain_params().percent_payment_to_witnesses ) / GRAPHENE_100_PERCENT;
if( op.amount < mock_trx.second + fee_for_witnesses )
return false;
return true;
}
} } // namespace graphene::chain

View file

@ -14,6 +14,7 @@ target_link_libraries( graphene_net
target_include_directories( graphene_net
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../chain/include" "${CMAKE_CURRENT_BINARY_DIR}/../chain/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../plugins/peerplays_sidechain/sidechain/include"
)
if(MSVC)

View file

@ -1,12 +1,16 @@
add_subdirectory( sidechain )
file(GLOB HEADERS "include/graphene/peerplays_sidechain_history/*.hpp")
add_library( peerplays_sidechain
peerplays_sidechain_plugin.cpp
)
target_link_libraries( peerplays_sidechain graphene_chain graphene_app )
target_link_libraries( peerplays_sidechain sidechain graphene_chain graphene_app )
target_include_directories( peerplays_sidechain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/sidechain/include"
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/sidechain/network/include" )
install( TARGETS
peerplays_sidechain
@ -16,4 +20,3 @@ install( TARGETS
ARCHIVE DESTINATION lib
)
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/peerplays_sidechain" )

View file

@ -31,4 +31,3 @@ class peerplays_sidechain_plugin : public graphene::app::plugin
};
} } //graphene::peerplays_sidechain_plugin

View file

@ -1,5 +1,9 @@
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
#include <sidechain/network/sidechain_net_manager.hpp>
namespace bpo = boost::program_options;
namespace graphene { namespace peerplays_sidechain {
namespace detail
@ -15,6 +19,10 @@ class peerplays_sidechain_plugin_impl
virtual ~peerplays_sidechain_plugin_impl();
peerplays_sidechain_plugin& _self;
sidechain::sidechain_net_manager bitcoin_manager;
private:
};
peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl()
@ -40,15 +48,36 @@ std::string peerplays_sidechain_plugin::plugin_name()const
}
void peerplays_sidechain_plugin::plugin_set_program_options(
boost::program_options::options_description& /*cli*/,
boost::program_options::options_description& /*cfg*/
boost::program_options::options_description& cli,
boost::program_options::options_description& cfg
)
{
cli.add_options()
("bitcoin-node-ip", bpo::value<string>()->implicit_value("127.0.0.1"), "IP address of Bitcoin node")
("bitcoin-node-zmq-port", bpo::value<uint32_t>()->implicit_value(28332), "ZMQ port of Bitcoin node")
("bitcoin-node-rpc-port", bpo::value<uint32_t>()->implicit_value(18332), "RPC port of Bitcoin node")
("bitcoin-node-rpc-user", bpo::value<string>(), "Bitcoin RPC user")
("bitcoin-node-rpc-password", bpo::value<string>(), "Bitcoin RPC password")
;
cfg.add(cli);
}
void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map& /*options*/)
void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{
ilog("peerplays sidechain plugin: plugin_initialize()");
if( options.count( "bitcoin-node-ip" ) && options.count( "bitcoin-node-zmq-port" ) && options.count( "bitcoin-node-rpc-port" )
&& options.count( "bitcoin-node-rpc-user" ) && options.count( "bitcoin-node-rpc-password" ) )
{
const auto ip = options.at("bitcoin-node-ip").as<std::string>();
const auto zmq_port = options.at("bitcoin-node-zmq-port").as<uint32_t>();
const auto rpc_port = options.at("bitcoin-node-rpc-port").as<uint32_t>();
const auto rpc_user = options.at("bitcoin-node-rpc-user").as<std::string>();
const auto rpc_password = options.at("bitcoin-node-rpc-password").as<std::string>();
my->bitcoin_manager.initialize_manager(&database(), ip, zmq_port, rpc_port, rpc_user, rpc_password);
} else {
wlog("Haven't set up sidechain parameters");
}
}
void peerplays_sidechain_plugin::plugin_startup()

View file

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

View file

@ -0,0 +1,192 @@
// Copyright (c) 2017 Pieter Wuille
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <sidechain/bech32.hpp>
// #include <bech32.h>
namespace
{
typedef std::vector<uint8_t> data;
/** The Bech32 character set for encoding. */
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
/** The Bech32 character set for decoding. */
const int8_t CHARSET_REV[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
};
/** Concatenate two byte arrays. */
data Cat(data x, const data& y)
{
x.insert(x.end(), y.begin(), y.end());
return x;
}
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
* bits correspond to earlier values. */
uint32_t PolyMod(const data& v)
{
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
// v(x) mod g(x), where g(x) is the Bech32 generator,
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
// fact guarantee detection of up to 4 errors within a window of 89 characters.
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
// During the course of the loop below, `c` contains the bitpacked coefficients of the
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
// for `c`.
uint32_t c = 1;
for (auto v_i : v) {
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
// process. Simplifying:
// c'(x) = (f(x) * x + v_i) mod g(x)
// ((f(x) mod g(x)) * x + v_i) mod g(x)
// (c(x) * x + v_i) mod g(x)
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
// If we call (x^6 mod g(x)) = k(x), this can be written as
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
// First, determine the value of c0:
uint8_t c0 = c >> 25;
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
c = ((c & 0x1ffffff) << 5) ^ v_i;
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
}
return c;
}
/** Convert to lower case. */
inline unsigned char LowerCase(unsigned char c)
{
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
}
/** Expand a HRP for use in checksum computation. */
data ExpandHRP(const std::string& hrp)
{
data ret;
ret.reserve(hrp.size() + 90);
ret.resize(hrp.size() * 2 + 1);
for (size_t i = 0; i < hrp.size(); ++i) {
unsigned char c = hrp[i];
ret[i] = c >> 5;
ret[i + hrp.size() + 1] = c & 0x1f;
}
ret[hrp.size()] = 0;
return ret;
}
/** Verify a checksum. */
bool VerifyChecksum(const std::string& hrp, const data& values)
{
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
// list of values would result in a new valid list. For that reason, Bech32 requires the
// resulting checksum to be 1 instead.
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
}
/** Create a checksum. */
data CreateChecksum(const std::string& hrp, const data& values)
{
data enc = Cat(ExpandHRP(hrp), values);
enc.resize(enc.size() + 6); // Append 6 zeroes
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
data ret(6);
for (size_t i = 0; i < 6; ++i) {
// Convert the 5-bit groups in mod to checksum values.
ret[i] = (mod >> (5 * (5 - i))) & 31;
}
return ret;
}
} // namespace
namespace sidechain { namespace bech32 {
/** Encode a Bech32 string. */
std::string Encode(const std::string& hrp, const data& values) {
data checksum = CreateChecksum(hrp, values);
data combined = Cat(values, checksum);
std::string ret = hrp + '1';
ret.reserve(ret.size() + combined.size());
for (auto c : combined) {
ret += CHARSET[c];
}
return ret;
}
/** Decode a Bech32 string. */
std::pair<std::string, data> Decode(const std::string& str) {
bool lower = false, upper = false;
for (size_t i = 0; i < str.size(); ++i) {
unsigned char c = str[i];
if (c < 33 || c > 126) return {};
if (c >= 'a' && c <= 'z') lower = true;
if (c >= 'A' && c <= 'Z') upper = true;
}
if (lower && upper) return {};
size_t pos = str.rfind('1');
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
return {};
}
data values(str.size() - 1 - pos);
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
unsigned char c = str[i + pos + 1];
int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c];
if (rev == -1) {
return {};
}
values[i] = rev;
}
std::string hrp;
for (size_t i = 0; i < pos; ++i) {
hrp += LowerCase(str[i]);
}
if (!VerifyChecksum(hrp, values)) {
return {};
}
return {hrp, data(values.begin(), values.end() - 6)};
}
} } // namespace sidechain::bech32

View file

@ -0,0 +1,258 @@
#include <sidechain/bitcoin_address.hpp>
#include <sstream>
#include <fc/crypto/base58.hpp>
#include <sidechain/segwit_addr.hpp>
#include <sidechain/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_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,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,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/types.hpp>
#include <sidechain/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/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_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,61 @@
#pragma once
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <sidechain/thread_safe_index.hpp>
#include <fc/crypto/sha256.hpp>
#include <graphene/chain/protocol/types.hpp>
using boost::multi_index_container;
using namespace boost::multi_index;
namespace graphene { namespace chain { class database; } }
namespace sidechain {
struct bitcoin_transaction_confirmations
{
bitcoin_transaction_confirmations() = default;
bitcoin_transaction_confirmations( const fc::sha256& trx_id, const std::set<fc::sha256>& vins ) :
id( count_id_tx_conf++ ), transaction_id( trx_id ), valid_vins( vins ) {}
struct comparer {
bool operator()( const bitcoin_transaction_confirmations& lhs, const bitcoin_transaction_confirmations& rhs ) const {
if( lhs.is_confirmed_and_not_used() != rhs.is_confirmed_and_not_used() )
return lhs.is_confirmed_and_not_used() < rhs.is_confirmed_and_not_used();
return lhs.id < rhs.id;
}
};
static uint64_t count_id_tx_conf;
uint64_t id;
bool is_confirmed_and_not_used() const { return !used && confirmed; }
bool is_missing_and_not_used() const { return !used && missing; }
fc::sha256 transaction_id;
std::set<fc::sha256> valid_vins;
uint64_t count_block = 0;
bool confirmed = false;
bool missing = false;
bool used = false;
};
struct by_hash;
struct by_confirmed_and_not_used;
struct by_missing_and_not_used;
using btc_tx_confirmations_index = boost::multi_index_container<bitcoin_transaction_confirmations,
indexed_by<
ordered_unique<tag<by_hash>, member<bitcoin_transaction_confirmations, fc::sha256, &bitcoin_transaction_confirmations::transaction_id>>,
ordered_non_unique<tag<by_confirmed_and_not_used>, identity< bitcoin_transaction_confirmations >, bitcoin_transaction_confirmations::comparer >,
ordered_non_unique<tag<by_missing_and_not_used>, identity< bitcoin_transaction_confirmations >, bitcoin_transaction_confirmations::comparer >
>
>;
}

View file

@ -0,0 +1,114 @@
#pragma once
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <sidechain/types.hpp>
#include <sidechain/thread_safe_index.hpp>
#include <fc/crypto/sha256.hpp>
#include <graphene/chain/info_for_vout_object.hpp>
#include <graphene/chain/protocol/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

@ -0,0 +1,46 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
#include <sidechain/types.hpp>
#include <fc/crypto/sha256.hpp>
namespace graphene { namespace chain { class database; } }
using graphene::chain::primary_wallet_vout_object;
using graphene::chain::primary_wallet_vout_id_type;
namespace sidechain {
class primary_wallet_vout_manager
{
public:
primary_wallet_vout_manager( graphene::chain::database& _db ) : db( _db ) {}
bool is_max_vouts() const;
fc::optional< primary_wallet_vout_object > get_latest_unused_vout() const;
fc::optional< primary_wallet_vout_object > get_vout( fc::sha256 hash_id ) const;
void create_new_vout( const sidechain::prev_out& out );
void delete_vouts_after( fc::sha256 hash_id );
void confirm_vout( fc::sha256 hash_id );
void mark_as_used_vout( fc::sha256 hash_id );
void mark_as_unused_vout( fc::sha256 hash_id );
private:
fc::sha256 create_hash_id( const std::string& hash_tx, const uint32_t& n_vout, const uint64_t& id ) const;
fc::optional< primary_wallet_vout_id_type > get_vout_id( fc::sha256 hash_id ) const;
graphene::chain::database& db;
};
}

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/types.hpp>
#include <sidechain/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,52 @@
#pragma once
#include <sidechain/input_withdrawal_info.hpp>
#include <sidechain/bitcoin_transaction.hpp>
using namespace sidechain;
namespace sidechain {
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, bool front = true );
void create_pw_vout( const uint64_t amount, const bytes& wit_script_out, bool front = true );
void create_vouts_for_witness_fee( const accounts_keys& witness_active_keys, bool front = true );
static uint64_t get_estimate_tx_size( bitcoin_transaction tx, size_t number_witness );
void subtract_fee( const uint64_t& fee, const uint16_t& witnesses_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;
};
}

View file

@ -0,0 +1,30 @@
#pragma once
#include <graphene/chain/protocol/types.hpp>
namespace sidechain {
struct sidechain_parameters_extension {
uint8_t maximum_condensing_tx_vins = SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VINS;
uint8_t maximum_condensing_tx_vouts = SIDECHAIN_DEFAULT_MAX_CONDENSING_TX_VOUTS;
uint8_t maximum_unconfirmed_vouts = SIDECHAIN_DEFAULT_MAX_UNCONFIRMED_VOUTS;
uint16_t percent_payment_to_witnesses = SIDECHAIN_DEFAULT_PERCENT_PAYMENT_TO_WITNESSES;
uint8_t multisig_sigs_num = SIDECHAIN_DEFAULT_NUMBER_SIG_MULTISIG;
uint8_t confirmations_num = SIDECHAIN_DEFAULT_NUMBER_OF_CONFIRMATIONS;
graphene::chain::account_id_type managing_account;
graphene::chain::asset_id_type asset_id;
};
}
FC_REFLECT( sidechain::sidechain_parameters_extension,
(maximum_condensing_tx_vins)
(maximum_condensing_tx_vouts)
(maximum_unconfirmed_vouts)
(percent_payment_to_witnesses)
(multisig_sigs_num)
(confirmations_num)
(managing_account)
(asset_id)
)

View file

@ -0,0 +1,52 @@
#pragma once
#include <sidechain/types.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <graphene/chain/protocol/bitcoin_transaction.hpp>
#include <graphene/chain/protocol/operations.hpp>
namespace graphene { namespace chain { class database; } };
namespace sidechain {
class sidechain_proposal_checker
{
public:
sidechain_proposal_checker( graphene::chain::database& _db ) : db(_db) {}
bool check_bitcoin_transaction_send_operation( const bitcoin_transaction_send_operation& op );
bool check_reuse( const graphene::chain::operation& op );
bool check_witness_opportunity_to_approve( const witness_object& current_witness, const proposal_object& proposal );
bool check_witnesses_keys( const witness_object& current_witness, const proposal_object& proposal );
bool check_bitcoin_transaction_revert_operation( const bitcoin_transaction_revert_operation& op );
private:
bool check_info_for_pw_vin( const info_for_vin& info_for_vin );
bool check_info_for_vins( const std::vector<info_for_vin>& info_for_vins );
bool check_info_for_vouts( const std::vector<info_for_vout_id_type>& info_for_vout_ids );
bool check_transaction( const bitcoin_transaction_send_operation& btc_trx_op );
bool check_reuse_pw_vin( const fc::sha256& pw_vin );
bool check_reuse_user_vins( const std::vector<fc::sha256>& user_vin_identifiers );
bool check_reuse_vouts( const std::vector<info_for_vout_id_type>& user_vout_ids );
std::set<fc::sha256> pw_vin_ident;
std::set<fc::sha256> user_vin_ident;
std::set<info_for_vout_id_type> vout_ids;
graphene::chain::database& db;
};
}

View file

@ -0,0 +1,27 @@
#pragma once
#include <sidechain/types.hpp>
#include <sidechain/input_withdrawal_info.hpp>
#include <secp256k1.h>
namespace sidechain {
class bitcoin_transaction;
fc::sha256 get_signature_hash( const bitcoin_transaction& tx, const bytes& scriptPubKey, int64_t amount,
size_t in_index, int hash_type, bool is_witness );
std::vector<char> privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign = nullptr );
std::vector<bytes> sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
const std::vector<uint64_t>& amounts, const bytes& privkey,
const secp256k1_context_t* context_sign = nullptr, int hash_type = 1 );
void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts );
bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context );
std::vector<std::vector<bytes>> sort_sigs( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
const std::vector<uint64_t>& amounts, const secp256k1_context_t* context );
}

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

@ -0,0 +1,15 @@
#pragma once
#include <sidechain/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 );
}

View file

@ -0,0 +1,207 @@
#include <sidechain/input_withdrawal_info.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <sidechain/bitcoin_transaction_confirmations.hpp>
namespace sidechain {
uint64_t info_for_vin::count_id_info_for_vin = 0;
uint64_t bitcoin_transaction_confirmations::count_id_tx_conf = 0;
info_for_vin::info_for_vin( const prev_out& _out, const std::string& _address, bytes _script, bool _resend ) :
id( count_id_info_for_vin++ ), out( _out ), address( _address ), script( _script ), resend( _resend )
{
identifier = fc::sha256::hash( out.hash_tx + std::to_string( out.n_vout ) );
}
bool info_for_vin::operator!=( const info_for_vin& obj ) const
{
if( this->identifier != obj.identifier ||
this->out.hash_tx != obj.out.hash_tx ||
this->out.n_vout != obj.out.n_vout ||
this->out.amount != obj.out.amount ||
this->address != obj.address ||
this->script != obj.script )
{
return true;
}
return false;
}
bool info_for_vin::comparer::operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const
{
if( lhs.used != rhs.used ) {
return lhs.used < rhs.used;
} else if ( lhs.resend != rhs.resend ) {
return lhs.resend > rhs.resend;
}
return lhs.id < rhs.id;
}
std::vector<bytes> input_withdrawal_info::get_redeem_scripts( const std::vector<info_for_vin>& info_vins )
{
std::vector<bytes> redeem_scripts;
const auto& bitcoin_address_idx = db.get_index_type<bitcoin_address_index>().indices().get< by_address >();
for( const auto& v : info_vins ) {
const auto& pbtc_address = bitcoin_address_idx.find( v.address );
redeem_scripts.push_back( pbtc_address->address.get_redeem_script() );
}
return redeem_scripts;
}
std::vector<uint64_t> input_withdrawal_info::get_amounts( const std::vector<info_for_vin>& info_vins )
{
std::vector<uint64_t> amounts;
for( const auto& v : info_vins ) {
amounts.push_back( v.out.amount );
}
return amounts;
}
fc::optional<info_for_vin> input_withdrawal_info::get_info_for_pw_vin()
{
fc::optional< primary_wallet_vout_object > vout = db.pw_vout_manager.get_latest_unused_vout();
if( !vout.valid() || db.pw_vout_manager.is_max_vouts() ) {
return fc::optional<info_for_vin>();
}
const auto& pw_address = db.get_latest_PW().address;
info_for_vin vin;
vin.identifier = vout->hash_id;
vin.out = vout->vout;
vin.address = pw_address.get_address();
vin.script = pw_address.get_witness_script();
return vin;
}
void input_withdrawal_info::insert_info_for_vin( const prev_out& out, const std::string& address, bytes script, bool resend )
{
info_for_vins.insert( info_for_vin( out, address, script, resend ) );
}
void input_withdrawal_info::modify_info_for_vin( const info_for_vin& obj, const std::function<void( info_for_vin& e )>& func )
{
info_for_vins.modify<by_identifier>( obj.identifier, func );
}
void input_withdrawal_info::mark_as_used_vin( const info_for_vin& obj )
{
info_for_vins.modify<by_identifier>( obj.identifier, [&]( info_for_vin& o ) {
o.used = true;
});
}
void input_withdrawal_info::mark_as_unused_vin( const info_for_vin& obj )
{
info_for_vins.modify<by_identifier>( obj.identifier, [&]( info_for_vin& o ) {
o.used = false;
});
}
void input_withdrawal_info::remove_info_for_vin( const info_for_vin& obj )
{
info_for_vins.remove<by_identifier>( obj.identifier );
}
fc::optional<info_for_vin> input_withdrawal_info::find_info_for_vin( fc::sha256 identifier )
{
return info_for_vins.find<by_identifier>( identifier );
}
std::vector<info_for_vin> input_withdrawal_info::get_info_for_vins()
{
std::vector<info_for_vin> result;
const auto max_vins = db.get_sidechain_params().maximum_condensing_tx_vins;
info_for_vins.safe_for<by_id_resend_not_used>( [&]( info_for_vin_index::index<by_id_resend_not_used>::type::iterator itr_b,
info_for_vin_index::index<by_id_resend_not_used>::type::iterator itr_e )
{
for( size_t i = 0; itr_b != itr_e && i < max_vins && !itr_b->used; i++ ) {
info_for_vin vin;
vin.identifier = itr_b->identifier;
vin.out.hash_tx = itr_b->out.hash_tx;
vin.out.n_vout = itr_b->out.n_vout;
vin.out.amount = itr_b->out.amount;
vin.address = itr_b->address;
vin.script = itr_b->script;
result.push_back( vin );
++itr_b;
}
} );
return result;
}
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.address = bitcoin_address( data );
obj.amount = amount;
});
}
void input_withdrawal_info::mark_as_used_vout( const graphene::chain::info_for_vout_object& obj )
{
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
auto itr = info_for_vout_idx.find( obj.id );
db.modify<graphene::chain::info_for_vout_object>( *itr, [&]( graphene::chain::info_for_vout_object& o ) {
o.used = true;
});
}
void input_withdrawal_info::mark_as_unused_vout( const graphene::chain::info_for_vout_object& obj )
{
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
auto itr = info_for_vout_idx.find( obj.id );
db.modify<graphene::chain::info_for_vout_object>( *itr, [&]( graphene::chain::info_for_vout_object& o ) {
o.used = false;
});
}
void input_withdrawal_info::remove_info_for_vout( const info_for_vout& obj )
{
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
auto itr = info_for_vout_idx.find( obj.id );
db.remove( *itr );
}
fc::optional<graphene::chain::info_for_vout_object> input_withdrawal_info::find_info_for_vout( graphene::chain::info_for_vout_id_type id )
{
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
auto itr = info_for_vout_idx.find( id );
if( itr != info_for_vout_idx.end() )
return fc::optional<graphene::chain::info_for_vout_object>( *itr );
return fc::optional<graphene::chain::info_for_vout_object>();
}
size_t input_withdrawal_info::size_info_for_vouts()
{
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id >();
return info_for_vout_idx.size();
}
std::vector<info_for_vout> input_withdrawal_info::get_info_for_vouts()
{
std::vector<info_for_vout> result;
const auto& info_for_vout_idx = db.get_index_type<graphene::chain::info_for_vout_index>().indices().get< graphene::chain::by_id_and_not_used >();
const auto max_vouts = db.get_sidechain_params().maximum_condensing_tx_vouts;
auto itr = info_for_vout_idx.begin();
for(size_t i = 0; i < max_vouts && itr != info_for_vout_idx.end() && !itr->used; i++) {
result.push_back( *itr );
++itr;
}
return result;
}
}

View file

@ -0,0 +1,7 @@
file( GLOB HEADERS "include/sidechain/network/*.hpp" )
file( GLOB SOURCES "*.cpp" )
add_library( sidechain_network STATIC ${SOURCES} ${HEADERS} )
target_link_libraries( sidechain_network PUBLIC graphene_chain fc zmq )
target_include_directories( sidechain_network PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )

View file

@ -0,0 +1,140 @@
#include <sidechain/network/bitcoin_rpc_client.hpp>
#include <sstream>
#include <fc/network/ip.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
namespace sidechain {
bitcoin_rpc_client::bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password ):
ip( _ip ), rpc_port( _rpc ), user( _user ), password( _password )
{
authorization.key = "Authorization";
authorization.val = "Basic " + fc::base64_encode( user + ":" + password );
}
std::string bitcoin_rpc_client::receive_full_block( const std::string& block_hash )
{
fc::http::connection conn;
conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) );
const auto url = "http://" + ip + ":" + std::to_string( rpc_port ) + "/rest/block/" + block_hash + ".json";
const auto reply = conn.request( "GET", url );
if ( reply.status != 200 )
return "";
ilog( "Receive Bitcoin block: ${hash}", ( "hash", block_hash ) );
return std::string( reply.body.begin(), reply.body.end() );
}
int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash )
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getrawtransaction\", \"params\": [") +
std::string("\"") + tx_hash + std::string("\"") + ", " + "true" + std::string("] }");
const auto reply = send_post_request( body );
if ( reply.status != 200 )
return 0;
const auto result = std::string( reply.body.begin(), reply.body.end() );
std::stringstream ss( result );
boost::property_tree::ptree tx;
boost::property_tree::read_json( ss, tx );
if( tx.count( "result" ) ) {
if( tx.get_child( "result" ).count( "confirmations" ) ) {
return tx.get_child( "result" ).get_child( "confirmations" ).get_value<int64_t>();
}
}
return 0;
}
bool bitcoin_rpc_client::receive_mempool_entry_tx( const std::string& tx_hash )
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", \"method\": \"getmempoolentry\", \"params\": [") +
std::string("\"") + tx_hash + std::string("\"") + std::string("] }");
const auto reply = send_post_request( body );
if ( reply.status != 200 )
return false;
return true;
}
uint64_t bitcoin_rpc_client::receive_estimated_fee()
{
static const auto confirmation_target_blocks = 6;
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"estimated_feerate\", \"method\": \"estimatesmartfee\", \"params\": [") +
std::to_string(confirmation_target_blocks) + std::string("] }");
const auto reply = send_post_request( body );
if( reply.status != 200 )
return 0;
std::stringstream ss( std::string( reply.body.begin(), reply.body.end() ) );
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( json.count( "result" ) )
if ( json.get_child( "result" ).count( "feerate" ) ) {
auto feerate_str = json.get_child( "result" ).get_child( "feerate" ).get_value<std::string>();
feerate_str.erase( std::remove( feerate_str.begin(), feerate_str.end(), '.' ), feerate_str.end() );
return std::stoll( feerate_str );
}
return 0;
}
void bitcoin_rpc_client::send_btc_tx( const std::string& tx_hex )
{
const auto body = std::string("{\"jsonrpc\": \"1.0\", \"id\":\"send_tx\", \"method\": \"sendrawtransaction\", \"params\": [") +
std::string("\"") + tx_hex + std::string("\"") + std::string("] }");
const auto reply = send_post_request( body );
if( reply.body.empty() )
return;
std::string reply_str( reply.body.begin(), reply.body.end() );
std::stringstream ss(reply_str);
boost::property_tree::ptree json;
boost::property_tree::read_json( ss, json );
if( reply.status == 200 ) {
idump(( tx_hex ));
return;
} else if( json.count( "error" ) && !json.get_child( "error" ).empty() ) {
const auto error_code = json.get_child( "error" ).get_child( "code" ).get_value<int>();
if( error_code == -27 ) // transaction already in block chain
return;
wlog( "BTC tx is not sent! Reply: ${msg}", ("msg", reply_str) );
}
}
bool bitcoin_rpc_client::connection_is_not_defined() const
{
return ip.empty() || rpc_port == 0 || user.empty() || password.empty();
}
fc::http::reply bitcoin_rpc_client::send_post_request( std::string body )
{
fc::http::connection conn;
conn.connect_to( fc::ip::endpoint( fc::ip::address( ip ), rpc_port ) );
const auto url = "http://" + ip + ":" + std::to_string( rpc_port );
return conn.request( "POST", url, body, fc::http::headers{authorization} );
}
}

View file

@ -0,0 +1,40 @@
#pragma once
#include <string>
#include <fc/network/http/connection.hpp>
namespace sidechain {
class bitcoin_rpc_client
{
public:
bitcoin_rpc_client( std::string _ip, uint32_t _rpc, std::string _user, std::string _password) ;
std::string receive_full_block( const std::string& block_hash );
int32_t receive_confirmations_tx( const std::string& tx_hash );
bool receive_mempool_entry_tx( const std::string& tx_hash );
uint64_t receive_estimated_fee();
void send_btc_tx( const std::string& tx_hex );
bool connection_is_not_defined() const;
private:
fc::http::reply send_post_request( std::string body );
std::string ip;
uint32_t rpc_port;
std::string user;
std::string password;
fc::http::header authorization;
};
}

View file

@ -0,0 +1,52 @@
#pragma once
#include <sidechain/network/zmq_listener.hpp>
#include <sidechain/network/bitcoin_rpc_client.hpp>
#include <graphene/chain/database.hpp>
namespace sidechain {
class sidechain_net_manager
{
public:
sidechain_net_manager() {};
sidechain_net_manager( graphene::chain::database* _db, std::string _ip,
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password );
~sidechain_net_manager() { db = nullptr; }
void initialize_manager( graphene::chain::database* _db, std::string _ip,
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password );
void update_tx_infos( const std::string& block_hash );
void update_tx_approvals();
void update_estimated_fee();
void send_btc_tx( const sidechain::bitcoin_transaction& trx );
bool connection_is_not_defined() const;
private:
void handle_block( const std::string& block_hash );
std::vector<info_for_vin> extract_info_from_block( const std::string& _block );
void update_transaction_status( std::vector<fc::sha256> trx_for_check );
std::set<fc::sha256> get_valid_vins( const std::string tx_hash );
inline uint64_t parse_amount(std::string raw);
std::unique_ptr<zmq_listener> listener;
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
graphene::chain::database* db;
};
}

View file

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <vector>
#include <fc/signals.hpp>
#include <zmq.hpp>
namespace sidechain {
class zmq_listener
{
public:
zmq_listener( std::string _ip, uint32_t _zmq );
bool connection_is_not_defined() const { return zmq_port == 0; }
fc::signal<void( const std::string& )> block_received;
private:
void handle_zmq();
std::vector<zmq::message_t> receive_multipart();
std::string ip;
uint32_t zmq_port;
zmq::context_t ctx;
zmq::socket_t socket;
};
}

View file

@ -0,0 +1,202 @@
#include <sidechain/network/sidechain_net_manager.hpp>
#include <sidechain/serialize.hpp>
#include <sidechain/sidechain_parameters.hpp>
#include <fc/network/http/connection.hpp>
#include <fc/network/ip.hpp>
#include <thread>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
namespace sidechain {
sidechain_net_manager::sidechain_net_manager( graphene::chain::database* _db, std::string _ip,
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password )
{
initialize_manager(_db, _ip, _zmq, _rpc, _user, _password );
}
void sidechain_net_manager::initialize_manager( graphene::chain::database* _db, std::string _ip,
uint32_t _zmq, uint32_t _rpc, std::string _user, std::string _password )
{
fc::http::connection conn;
try {
conn.connect_to( fc::ip::endpoint( fc::ip::address( _ip ), _rpc ) );
} catch ( fc::exception e ) {
elog( "No BTC node running at ${ip} or wrong rpc port: ${port}", ("ip", _ip) ("port", _rpc) );
FC_ASSERT( false );
}
listener = std::unique_ptr<zmq_listener>( new zmq_listener( _ip, _zmq ) );
bitcoin_client = std::unique_ptr<bitcoin_rpc_client>( new bitcoin_rpc_client( _ip, _rpc, _user, _password ) );
db = _db;
listener->block_received.connect([this]( const std::string& block_hash ) {
std::thread( &sidechain_net_manager::handle_block, this, block_hash ).detach();
} );
db->send_btc_tx.connect([this]( const sidechain::bitcoin_transaction& trx ) {
std::thread( &sidechain_net_manager::send_btc_tx, this, trx ).detach();
} );
}
void sidechain_net_manager::update_tx_infos( const std::string& block_hash )
{
std::string block = bitcoin_client->receive_full_block( block_hash );
if( block != "" ) {
const auto& vins = extract_info_from_block( block );
const auto& addr_idx = db->get_index_type<bitcoin_address_index>().indices().get<by_address>();
for( const auto& v : vins ) {
const auto& addr_itr = addr_idx.find( v.address );
FC_ASSERT( addr_itr != addr_idx.end() );
db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address, addr_itr->address.get_witness_script() );
}
}
}
void sidechain_net_manager::update_tx_approvals()
{
std::vector<fc::sha256> trx_for_check;
const auto& confirmations_num = db->get_sidechain_params().confirmations_num;
db->bitcoin_confirmations.safe_for<by_hash>([&]( btc_tx_confirmations_index::iterator itr_b, btc_tx_confirmations_index::iterator itr_e ){
for(auto iter = itr_b; iter != itr_e; iter++) {
db->bitcoin_confirmations.modify<by_hash>( iter->transaction_id, [&]( bitcoin_transaction_confirmations& obj ) {
obj.count_block++;
});
if( iter->count_block == confirmations_num ) {
trx_for_check.push_back( iter->transaction_id );
}
}
});
update_transaction_status( trx_for_check );
}
void sidechain_net_manager::update_estimated_fee()
{
db->estimated_feerate = bitcoin_client->receive_estimated_fee();
}
void sidechain_net_manager::send_btc_tx( const sidechain::bitcoin_transaction& trx )
{
std::set<fc::sha256> valid_vins;
for( const auto& v : trx.vin ) {
valid_vins.insert( v.prevout.hash );
}
db->bitcoin_confirmations.insert( bitcoin_transaction_confirmations( trx.get_txid(), valid_vins ) );
FC_ASSERT( !bitcoin_client->connection_is_not_defined() );
const auto tx_hex = fc::to_hex( pack( trx ) );
bitcoin_client->send_btc_tx( tx_hex );
}
bool sidechain_net_manager::connection_is_not_defined() const
{
return listener->connection_is_not_defined() && bitcoin_client->connection_is_not_defined();
}
void sidechain_net_manager::handle_block( const std::string& block_hash )
{
update_tx_approvals();
update_estimated_fee();
update_tx_infos( block_hash );
}
std::vector<info_for_vin> sidechain_net_manager::extract_info_from_block( const std::string& _block )
{
std::stringstream ss( _block );
boost::property_tree::ptree block;
boost::property_tree::read_json( ss, block );
std::vector<info_for_vin> result;
const auto& addr_idx = db->get_index_type<bitcoin_address_index>().indices().get<by_address>();
for (const auto& tx_child : block.get_child("tx")) {
const auto& tx = tx_child.second;
for ( const auto& o : tx.get_child("vout") ) {
const auto script = o.second.get_child("scriptPubKey");
if( !script.count("addresses") ) continue;
for (const auto& addr : script.get_child("addresses")) { // in which cases there can be more addresses?
const auto address_base58 = addr.second.get_value<std::string>();
if( !addr_idx.count( address_base58 ) ) continue;
info_for_vin vin;
vin.out.hash_tx = tx.get_child("txid").get_value<std::string>();
vin.out.amount = parse_amount( o.second.get_child( "value" ).get_value<std::string>() );
vin.out.n_vout = o.second.get_child( "n" ).get_value<uint32_t>();
vin.address = address_base58;
result.push_back( vin );
}
}
}
return result;
}
std::set<fc::sha256> sidechain_net_manager::get_valid_vins( const std::string tx_hash )
{
const auto& confirmations_obj = db->bitcoin_confirmations.find<sidechain::by_hash>( fc::sha256( tx_hash ) );
FC_ASSERT( confirmations_obj.valid() );
std::set<fc::sha256> valid_vins;
for( const auto& v : confirmations_obj->valid_vins ) {
auto confirmations = bitcoin_client->receive_confirmations_tx( v.str() );
if( confirmations == 0 ) {
continue;
}
valid_vins.insert( v );
}
return valid_vins;
}
void sidechain_net_manager::update_transaction_status( std::vector<fc::sha256> trx_for_check )
{
const auto& confirmations_num = db->get_sidechain_params().confirmations_num;
for( const auto& trx : trx_for_check ) {
auto confirmations = bitcoin_client->receive_confirmations_tx( trx.str() );
db->bitcoin_confirmations.modify<by_hash>( trx, [&]( bitcoin_transaction_confirmations& obj ) {
obj.count_block = confirmations;
});
if( confirmations >= confirmations_num ) {
db->bitcoin_confirmations.modify<by_hash>( trx, [&]( bitcoin_transaction_confirmations& obj ) {
obj.confirmed = true;
});
} else if( confirmations == 0 ) {
auto is_in_mempool = bitcoin_client->receive_mempool_entry_tx( trx.str() );
std::set<fc::sha256> valid_vins;
if( !is_in_mempool ) {
valid_vins = get_valid_vins( trx.str() );
}
db->bitcoin_confirmations.modify<by_hash>( trx, [&]( bitcoin_transaction_confirmations& obj ) {
obj.missing = !is_in_mempool;
obj.valid_vins = valid_vins;
});
}
}
}
// Removes dot from amount output: "50.00000000"
inline uint64_t sidechain_net_manager::parse_amount(std::string raw) {
raw.erase(std::remove(raw.begin(), raw.end(), '.'), raw.end());
return std::stoll(raw);
}
}

View file

@ -0,0 +1,45 @@
#include <sidechain/network/zmq_listener.hpp>
#include <boost/algorithm/hex.hpp>
#include <thread>
namespace sidechain {
zmq_listener::zmq_listener( std::string _ip, uint32_t _zmq ): ip( _ip ), zmq_port( _zmq ), ctx( 1 ), socket( ctx, ZMQ_SUB )
{
std::thread( &zmq_listener::handle_zmq, this ).detach();
}
std::vector<zmq::message_t> zmq_listener::receive_multipart()
{
std::vector<zmq::message_t> msgs;
int32_t more;
size_t more_size = sizeof( more );
while ( true ) {
zmq::message_t msg;
socket.recv( &msg, 0 );
socket.getsockopt( ZMQ_RCVMORE, &more, &more_size );
if ( !more )
break;
msgs.push_back( std::move(msg) );
}
return msgs;
}
void zmq_listener::handle_zmq()
{
socket.setsockopt( ZMQ_SUBSCRIBE, "hashblock", 0 );
socket.connect( "tcp://" + ip + ":" + std::to_string( zmq_port ) );
while ( true ) {
auto msg = receive_multipart();
const auto header = std::string( static_cast<char*>( msg[0].data() ), msg[0].size() );
const auto hash = boost::algorithm::hex( std::string( static_cast<char*>( msg[1].data() ), msg[1].size() ) );
block_received( hash );
}
}
}

View file

@ -0,0 +1,136 @@
#include <sidechain/primary_wallet_vout_manager.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <graphene/chain/config.hpp>
namespace sidechain {
bool primary_wallet_vout_manager::is_max_vouts() const
{
const auto& PW_vout_idx = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
return !( ( PW_vout_idx.size() - 1 ) <= db.get_sidechain_params().maximum_unconfirmed_vouts );
}
fc::optional< primary_wallet_vout_object > primary_wallet_vout_manager::get_latest_unused_vout() const
{
const auto& PW_vout_idx = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto itr = PW_vout_idx.end();
FC_ASSERT( itr != PW_vout_idx.begin() );
itr--;
if( itr->used )
return fc::optional< primary_wallet_vout_object >();
return fc::optional< primary_wallet_vout_object > (*itr);
}
fc::optional< primary_wallet_vout_object > primary_wallet_vout_manager::get_vout( fc::sha256 hash_id ) const
{
const auto& PW_vout_by_hash_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_hash_id >();
const auto& itr_hash_id = PW_vout_by_hash_id.find( hash_id );
if( itr_hash_id == PW_vout_by_hash_id.end() )
return fc::optional< primary_wallet_vout_object >();
return fc::optional< primary_wallet_vout_object >( *itr_hash_id );
}
void primary_wallet_vout_manager::create_new_vout( const sidechain::prev_out& out )
{
const auto& next_pw_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().get_next_id().instance();
db.create<primary_wallet_vout_object>([&]( primary_wallet_vout_object& obj ) {
obj.vout = out;
obj.hash_id = create_hash_id( out.hash_tx, out.n_vout, next_pw_id );
obj.confirmed = false;
obj.used = false;
});
}
void primary_wallet_vout_manager::delete_vouts_after( fc::sha256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
if( !vout_id.valid() )
return;
auto itr = PW_vout_by_id.find( *vout_id );
itr++;
while( itr != PW_vout_by_id.end() )
{
auto temp = itr;
itr++;
db.remove( *temp );
}
}
void primary_wallet_vout_manager::confirm_vout( fc::sha256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
FC_ASSERT( vout_id.valid() );
auto itr = PW_vout_by_id.find( *vout_id );
db.modify(*itr, [&]( primary_wallet_vout_object& PW_vout ) {
PW_vout.confirmed = true;
});
if( itr != PW_vout_by_id.begin() ){
itr--;
FC_ASSERT( itr->confirmed == true );
db.remove(*itr);
}
}
void primary_wallet_vout_manager::mark_as_used_vout( fc::sha256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
FC_ASSERT( vout_id.valid() );
auto itr = PW_vout_by_id.find( *vout_id );
db.modify(*itr, [&]( primary_wallet_vout_object& PW_vout ) {
PW_vout.used = true;
});
if( itr != PW_vout_by_id.begin() ){
itr--;
FC_ASSERT( itr->used == true );
}
}
void primary_wallet_vout_manager::mark_as_unused_vout( fc::sha256 hash_id )
{
const auto& PW_vout_by_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_id >();
auto vout_id = get_vout_id( hash_id );
FC_ASSERT( vout_id.valid() );
auto itr = PW_vout_by_id.find( *vout_id );
db.modify(*itr, [&]( primary_wallet_vout_object& PW_vout ) {
PW_vout.used = false;
});
}
fc::optional< graphene::chain::primary_wallet_vout_id_type > primary_wallet_vout_manager::get_vout_id( fc::sha256 hash_id ) const
{
const auto& PW_vout_by_hash_id = db.get_index_type<graphene::chain::primary_wallet_vout_index>().indices().get< graphene::chain::by_hash_id >();
const auto& itr_hash_id = PW_vout_by_hash_id.find( hash_id );
if( itr_hash_id == PW_vout_by_hash_id.end() )
return fc::optional< graphene::chain::primary_wallet_vout_id_type >();
return fc::optional< graphene::chain::primary_wallet_vout_id_type >( itr_hash_id->get_id() );
}
fc::sha256 primary_wallet_vout_manager::create_hash_id( const std::string& hash_tx, const uint32_t& n_vout, const uint64_t& id ) const
{
std::stringstream ss;
ss << std::hex << id;
std::string id_hex = std::string( 16 - ss.str().size(), '0' ) + ss.str();
std::string hash_str = fc::sha256::hash( hash_tx + std::to_string( n_vout ) ).str();
std::string final_hash_id = std::string( hash_str.begin(), hash_str.begin() + 48 ) + id_hex;
return fc::sha256( final_hash_id );
}
}

View file

@ -0,0 +1,81 @@
/* Copyright (c) 2017 Pieter Wuille
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <sidechain/segwit_addr.hpp>
#include <sidechain/bech32.hpp>
namespace
{
typedef std::vector<uint8_t> data;
/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad>
bool convertbits(data& out, const data& in) {
int acc = 0;
int bits = 0;
const int maxv = (1 << tobits) - 1;
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
for (size_t i = 0; i < in.size(); ++i) {
int value = in[i];
acc = ((acc << frombits) | value) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
out.push_back((acc >> bits) & maxv);
}
}
if (pad) {
if (bits) out.push_back((acc << (tobits - bits)) & maxv);
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
return false;
}
return true;
}
}
namespace sidechain { namespace segwit_addr {
/** Decode a SegWit address. */
std::pair<int, data> decode(const std::string& hrp, const std::string& addr) {
std::pair<std::string, data> dec = sidechain::bech32::Decode(addr);
if (dec.first != hrp || dec.second.size() < 1) return std::make_pair(-1, data());
data conv;
if (!convertbits<5, 8, false>(conv, data(dec.second.begin() + 1, dec.second.end())) ||
conv.size() < 2 || conv.size() > 40 || dec.second[0] > 16 || (dec.second[0] == 0 &&
conv.size() != 20 && conv.size() != 32)) {
return std::make_pair(-1, data());
}
return std::make_pair(dec.second[0], conv);
}
/** Encode a SegWit address. */
std::string encode(const std::string& hrp, int witver, const data& witprog) {
data enc;
enc.push_back(witver);
convertbits<8, 5, true>(enc, witprog);
std::string ret = sidechain::bech32::Encode(hrp, enc);
if (decode(hrp, ret).first == -1) return "";
return ret;
}
} }

View file

@ -0,0 +1,107 @@
#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, bool front )
{
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, front );
amount_vins += vin_info.out.amount;
}
void sidechain_condensing_tx::create_pw_vout( const uint64_t amount, const bytes& wit_script_out, bool front )
{
tb.add_out( payment_type::P2WSH, amount, bytes(wit_script_out.begin() + 2, wit_script_out.end()), front );
}
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, bool front )
{
for( auto& key : witness_active_keys ) {
tb.add_out( payment_type::P2PK, 0, key.second, front );
count_witness_vout++;
}
}
uint64_t sidechain_condensing_tx::get_estimate_tx_size( bitcoin_transaction tx, size_t number_witness )
{
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} };
for( auto& vin : tx.vin ) {
vin.scriptWitness = temp_scriptWitness;
}
return tx.get_vsize();
}
void sidechain_condensing_tx::subtract_fee( const uint64_t& fee, const uint16_t& witnesses_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;
}
bool is_pw_vin = tx.vout.size() > ( count_witness_vout + count_transfer_vout );
if( is_pw_vin ) {
tx.vout[0].value = tx.vout[0].value - fee_size * count_transfer_vin;
}
uint64_t fee_witnesses = 0;
size_t offset = is_pw_vin ? 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 * witnesses_percentage ) / GRAPHENE_100_PERCENT;
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 = is_pw_vin ? 1 + count_witness_vout : count_witness_vout;
size_t offset = is_pw_vin ? 1 : 0;
FC_ASSERT( fee_witness > 0 );
for( ; offset < limit; offset++ ) {
tx.vout[offset].value += fee_witness;
}
}
tb = std::move( bitcoin_transaction_builder( tx ) );
}
}

View file

@ -0,0 +1,214 @@
#include <sidechain/sidechain_proposal_checker.hpp>
#include <sidechain/sidechain_condensing_tx.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/primary_wallet_vout_object.hpp>
#include <graphene/chain/bitcoin_address_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/bitcoin_transaction_object.hpp>
#include <graphene/chain/info_for_used_vin_object.hpp>
namespace sidechain {
bool sidechain_proposal_checker::check_bitcoin_transaction_send_operation( const bitcoin_transaction_send_operation& op )
{
bool info_for_pw_vin = check_info_for_pw_vin( op.pw_vin );
bool info_for_vins = check_info_for_vins( op.vins );
bool info_for_vouts = check_info_for_vouts( op.vouts );
bool transaction = check_transaction( op );
return info_for_pw_vin && info_for_vins && info_for_vouts && transaction;
}
bool sidechain_proposal_checker::check_reuse_pw_vin( const fc::sha256& pw_vin )
{
const auto& pw_vin_status = pw_vin_ident.insert( pw_vin );
if( !pw_vin_status.second ) {
return false;
}
return true;
}
bool sidechain_proposal_checker::check_reuse_user_vins( const std::vector<fc::sha256>& user_vin_identifiers )
{
for( const auto& vin : user_vin_identifiers ) {
const auto& user_vin_status = user_vin_ident.insert( vin );
if( !user_vin_status.second ) {
return false;
}
}
return true;
}
bool sidechain_proposal_checker::check_reuse_vouts( const std::vector<info_for_vout_id_type>& user_vout_ids )
{
for( const auto& vout : user_vout_ids ) {
const auto& user_vout_status = vout_ids.insert( vout );
if( !user_vout_status.second ) {
return false;
}
}
return true;
}
bool sidechain_proposal_checker::check_reuse( const operation& op )
{
fc::sha256 pw_vin_identifier;
std::vector<fc::sha256> user_vin_identifiers;
std::vector<info_for_vout_id_type> user_vout_ids;
auto get_bto_tx_info = [ & ]( fc::sha256 trx_id ) {
const auto& bto_itr_idx = db.get_index_type<bitcoin_transaction_index>().indices().get<graphene::chain::by_transaction_id>();
const auto& bto_itr = bto_itr_idx.find( trx_id );
if( bto_itr == bto_itr_idx.end() ) {
return false;
}
pw_vin_identifier = bto_itr->pw_vin;
user_vout_ids = bto_itr->vouts;
user_vin_identifiers = bto_itr->vins;
return true;
};
if( op.which() == operation::tag<bitcoin_transaction_send_operation>::value ) {
bitcoin_transaction_send_operation btc_tx_send_op = op.get<bitcoin_transaction_send_operation>();
pw_vin_identifier = btc_tx_send_op.pw_vin.identifier;
user_vout_ids = btc_tx_send_op.vouts;
for( const auto& vin : btc_tx_send_op.vins ) {
user_vin_identifiers.push_back( vin.identifier );
}
} else if ( op.which() == operation::tag<bitcoin_issue_operation>::value ) {
bitcoin_issue_operation btc_issue_op = op.get<bitcoin_issue_operation>();
for( const auto& id : btc_issue_op.transaction_ids ) {
if ( !get_bto_tx_info( id ) )
return false;
}
} else if ( op.which() == operation::tag<bitcoin_transaction_revert_operation>::value ) {
bitcoin_transaction_revert_operation btc_tx_revert_op = op.get<bitcoin_transaction_revert_operation>();
for( auto trx_info: btc_tx_revert_op.transactions_info ) {
if ( !get_bto_tx_info( trx_info.transaction_id ) )
return false;
}
}
return check_reuse_pw_vin( pw_vin_identifier ) &&
check_reuse_user_vins( user_vin_identifiers ) &&
check_reuse_vouts( user_vout_ids );
}
bool sidechain_proposal_checker::check_info_for_pw_vin( const info_for_vin& info_for_vin )
{
const auto& prevout = db.pw_vout_manager.get_vout( info_for_vin.identifier );
const auto& pw_address = db.get_latest_PW().address;
if( !prevout.valid() || info_for_vin.out != prevout->vout ||
info_for_vin.address != pw_address.get_address() || info_for_vin.script != pw_address.get_witness_script() ) {
return false;
}
return true;
}
bool sidechain_proposal_checker::check_info_for_vins( const std::vector<info_for_vin>& info_for_vins )
{
for( const auto& vin : info_for_vins ) {
const auto& v = db.i_w_info.find_info_for_vin( vin.identifier );
if( !v.valid() || *v != vin ) {
return false;
}
}
return true;
}
bool sidechain_proposal_checker::check_info_for_vouts( const std::vector<info_for_vout_id_type>& info_for_vout_ids )
{
const auto& info_for_vout_idx = db.get_index_type<info_for_vout_index>().indices().get<graphene::chain::by_id>();
for( const auto& id : info_for_vout_ids ) {
const auto& itr = info_for_vout_idx.find( id );
if( itr == info_for_vout_idx.end() ) {
return false;
}
}
return true;
}
bool sidechain_proposal_checker::check_transaction( const bitcoin_transaction_send_operation& btc_trx_op )
{
std::vector<info_for_vout> info_vouts;
const auto& info_for_vout_idx = db.get_index_type<info_for_vout_index>().indices().get<graphene::chain::by_id>();
for( const auto& vout_id : btc_trx_op.vouts ) {
const auto& vout_itr = info_for_vout_idx.find( vout_id );
if( vout_itr == info_for_vout_idx.end() ) {
return false;
}
info_vouts.push_back( *vout_itr );
}
const auto& temp_full_tx = db.create_btc_transaction( btc_trx_op.vins, info_vouts, btc_trx_op.pw_vin );
if( temp_full_tx.first != btc_trx_op.transaction || temp_full_tx.second != btc_trx_op.fee_for_size ) {
return false;
}
return true;
}
bool sidechain_proposal_checker::check_witness_opportunity_to_approve( const witness_object& current_witness, const proposal_object& proposal )
{
auto is_active_witness = [ & ]() {
return db.get_global_properties().active_witnesses.find( current_witness.id ) != db.get_global_properties().active_witnesses.end();
};
// Checks can witness approve this proposal or not
auto does_the_witness_have_authority = [ & ]() {
const auto& accounts_index = db.get_index_type<account_index>().indices().get<graphene::chain::by_id>();
auto account_pBTC_issuer = accounts_index.find( db.get_sidechain_account_id() );
return account_pBTC_issuer->owner.account_auths.count( current_witness.witness_account ) &&
!proposal.available_active_approvals.count( current_witness.witness_account );
};
return is_active_witness() && does_the_witness_have_authority();
}
bool sidechain_proposal_checker::check_witnesses_keys( const witness_object& current_witness, const proposal_object& proposal )
{
bitcoin_transaction_send_operation op = proposal.proposed_transaction.operations.back().get<bitcoin_transaction_send_operation>();
auto vins = op.vins;
if( op.pw_vin.identifier.str().compare( 0, 48, SIDECHAIN_NULL_VIN_IDENTIFIER ) != 0 ) {
vins.insert( vins.begin(), op.pw_vin );
}
const auto& bitcoin_address_idx = db.get_index_type<bitcoin_address_index>().indices().get< by_address >();
for ( const auto& info_vins : vins ) {
const auto& address_itr = bitcoin_address_idx.find( info_vins.address );
if ( address_itr != bitcoin_address_idx.end() ) {
const auto& witness_key = current_witness.signing_key;
const auto& witnesses_keys = address_itr->address.witnesses_keys;
const auto& witnesses_keys_itr = witnesses_keys.find( current_witness.witness_account );
if ( witnesses_keys_itr == witnesses_keys.end() || witnesses_keys_itr->second != witness_key ) {
return false;
}
}
}
return true;
}
bool sidechain_proposal_checker::check_bitcoin_transaction_revert_operation( const bitcoin_transaction_revert_operation& op )
{
const auto& btc_trx_idx = db.get_index_type<bitcoin_transaction_index>().indices().get<by_transaction_id>();
for( auto trx_info: op.transactions_info )
{
auto value = db.bitcoin_confirmations.find<sidechain::by_hash>( trx_info.transaction_id );
if( !value.valid() ||
btc_trx_idx.find( value->transaction_id ) == btc_trx_idx.end() ||
trx_info != revert_trx_info( value->transaction_id, value->valid_vins ) )
return false;
}
return !op.transactions_info.empty();
}
}

View file

@ -0,0 +1,125 @@
#include <sidechain/sign_bitcoin_transaction.hpp>
#include <sidechain/serialize.hpp>
namespace sidechain {
fc::sha256 get_signature_hash( const bitcoin_transaction& tx, const bytes& scriptCode, int64_t amount,
size_t in_index, int hash_type, bool is_witness )
{
fc::datastream<size_t> ps;
if ( is_witness )
pack_tx_witness_signature( ps, scriptCode, tx, in_index, amount, hash_type );
else
pack_tx_signature( ps, scriptCode, tx, in_index, hash_type );
std::vector<char> vec( ps.tellp() );
if ( !vec.empty() ) {
fc::datastream<char*> ds( vec.data(), vec.size() );
if ( is_witness )
pack_tx_witness_signature( ds, scriptCode, tx, in_index, amount, hash_type );
else
pack_tx_signature( ds, scriptCode, tx, in_index, hash_type );
}
return fc::sha256::hash( fc::sha256::hash( vec.data(), vec.size() ) );
}
std::vector<char> privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign )
{
bytes sig;
sig.resize( 72 );
int sig_len = sig.size();
FC_ASSERT( secp256k1_ecdsa_sign(
context_sign,
reinterpret_cast<unsigned char*>( hash.data() ),
reinterpret_cast<unsigned char*>( sig.data() ),
&sig_len,
reinterpret_cast<const unsigned char*>( privkey.data() ),
secp256k1_nonce_function_rfc6979,
nullptr ) ); // TODO: replace assert with exception
sig.resize( sig_len );
return sig;
}
std::vector<bytes> sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
const std::vector<uint64_t>& amounts, const bytes& privkey,
const secp256k1_context_t* context_sign, int hash_type )
{
FC_ASSERT( tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size() );
FC_ASSERT( !privkey.empty() );
std::vector< bytes > signatures;
for( size_t i = 0; i < tx.vin.size(); i++ ) {
const auto sighash = get_signature_hash( tx, redeem_scripts[i], static_cast<int64_t>( amounts[i] ), i, hash_type, true );
auto sig = privkey_sign( privkey, sighash, context_sign );
sig.push_back( static_cast<uint8_t>( hash_type ) );
signatures.push_back( sig );
}
return signatures;
}
void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts )
{
FC_ASSERT( tx.vin.size() == redeem_scripts.size() );
for( size_t i = 0; i < tx.vin.size(); i++ ) {
tx.vin[i].scriptWitness.insert( tx.vin[i].scriptWitness.begin(), bytes() ); // Bitcoin workaround CHECKMULTISIG bug
tx.vin[i].scriptWitness.push_back( redeem_scripts[i] );
}
}
bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context )
{
std::vector<unsigned char> sig_temp( sig.begin(), sig.end() );
std::vector<unsigned char> pubkey_temp( pubkey.begin(), pubkey.end() );
std::vector<unsigned char> msg_temp( msg.begin(), msg.end() );
int result = secp256k1_ecdsa_verify( context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size() );
return result == 1;
}
std::vector<std::vector<bytes>> sort_sigs( const bitcoin_transaction& tx, const std::vector<bytes>& redeem_scripts,
const std::vector<uint64_t>& amounts, const secp256k1_context_t* context )
{
FC_ASSERT( redeem_scripts.size() == amounts.size() );
using data = std::pair<size_t, bytes>;
struct comp {
bool operator() (const data& lhs, const data& rhs) const { return lhs.first < rhs.first; }
};
std::vector<std::vector<bytes>> new_stacks;
for( size_t i = 0; i < redeem_scripts.size(); i++ ) {
const std::vector<bytes>& keys = get_pubkey_from_redeemScript( redeem_scripts[i] );
const auto& sighash = get_signature_hash( tx, redeem_scripts[i], static_cast<int64_t>( amounts[i] ), i, 1, true ).str();
bytes sighash_temp( parse_hex( sighash ) );
std::vector<bytes> stack( tx.vin[i].scriptWitness );
std::vector<bool> marker( tx.vin[i].scriptWitness.size(), false );
std::set<data, comp> sigs;
for( size_t j = 0; j < keys.size(); j++ ) {
for( size_t l = 0; l < stack.size(); l++ ) {
if( !verify_sig( stack[l], keys[j], sighash_temp, context ) || marker[l] )
continue;
sigs.insert(std::make_pair(j, stack[l]));
marker[l] = true;
break;
}
}
std::vector<bytes> temp_sig;
for( auto s : sigs ) {
temp_sig.push_back( s.second );
}
new_stacks.push_back( temp_sig );
}
return new_stacks;
}
}

View file

@ -0,0 +1,35 @@
#include <sidechain/utils.hpp>
namespace sidechain {
bytes parse_hex( const std::string& str )
{
bytes vec( str.size() / 2 );
fc::from_hex( str, vec.data(), vec.size() );
return vec;
}
std::vector<bytes> get_pubkey_from_redeemScript( bytes script )
{
FC_ASSERT( script.size() >= 37 );
script.erase( script.begin() );
script.erase( script.end() - 2, script.end() );
std::vector<bytes> result;
uint64_t count = script.size() / 34;
for( size_t i = 0; i < count; i++ ) {
result.push_back( bytes( script.begin() + (34 * i) + 1, script.begin() + (34 * (i + 1)) ) );
}
return result;
}
bytes public_key_data_to_bytes( const fc::ecc::public_key_data& key )
{
bytes result;
result.resize( key.size() );
std::copy( key.begin(), key.end(), result.begin() );
return result;
}
}

View file

@ -11,7 +11,7 @@ endif()
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
target_link_libraries( witness_node
PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc peerplays_sidechain ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
PRIVATE graphene_app graphene_account_history graphene_affiliate_stats graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc peerplays_sidechain sidechain_network ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
install( TARGETS