From 22adce712c5bfc381a5198e7460b84a1b66ff782 Mon Sep 17 00:00:00 2001 From: Srdjan Obucina Date: Wed, 18 Sep 2019 16:46:40 +0200 Subject: [PATCH] WIP, Moving Bitcoin listener to SON plugin - All sidechain code moved to plugin --- libraries/app/database_api.cpp | 39 +- .../app/include/graphene/app/database_api.hpp | 7 + libraries/chain/CMakeLists.txt | 13 +- libraries/chain/bitcoin_address_evaluator.cpp | 46 ++ .../chain/bitcoin_transaction_evaluator.cpp | 274 +++++++++++ libraries/chain/database.cpp | 3 +- libraries/chain/db_balance.cpp | 10 +- libraries/chain/db_block.cpp | 94 ++-- libraries/chain/db_getter.cpp | 2 +- libraries/chain/db_init.cpp | 23 + libraries/chain/db_management.cpp | 16 +- libraries/chain/db_notify.cpp | 24 + libraries/chain/db_sidechain.cpp | 432 ++++++++++++++++++ libraries/chain/db_update.cpp | 1 + libraries/chain/hardfork.d/sidechain.hf | 4 + .../chain/bitcoin_address_evaluator.hpp | 20 + .../graphene/chain/bitcoin_address_object.hpp | 47 ++ .../chain/bitcoin_transaction_evaluator.hpp | 55 +++ .../chain/bitcoin_transaction_object.hpp | 41 ++ .../chain/include/graphene/chain/config.hpp | 15 + .../chain/include/graphene/chain/database.hpp | 80 +++- .../chain/info_for_used_vin_object.hpp | 39 ++ .../graphene/chain}/info_for_vout_object.hpp | 5 +- .../chain/primary_wallet_vout_object.hpp | 44 ++ .../graphene/chain/proposal_evaluator.hpp | 19 + .../chain/protocol}/bitcoin_address.hpp | 0 .../chain/protocol}/bitcoin_transaction.hpp | 5 +- .../chain/protocol/chain_parameters.hpp | 2 + .../graphene/chain/protocol/operations.hpp | 12 +- .../graphene/chain/protocol/sidechain.hpp | 28 ++ .../graphene/chain/protocol/withdraw_pbtc.hpp | 33 ++ .../graphene/chain/sidechain_evaluator.hpp | 26 ++ .../chain/sidechain_proposal_object.hpp | 35 ++ .../chain/withdraw_pbtc_evaluator.hpp | 27 ++ libraries/chain/sidechain_evaluator.cpp | 141 ++++++ libraries/chain/withdraw_pbtc_evaluator.cpp | 85 ++++ .../peerplays_sidechain/CMakeLists.txt | 5 +- .../peerplays_sidechain_plugin.hpp | 1 - .../peerplays_sidechain_plugin.cpp | 2 +- .../sidechain/CMakeLists.txt | 9 + .../peerplays_sidechain/sidechain/bech32.cpp | 192 ++++++++ .../{ => sidechain}/bitcoin_address.cpp | 14 +- .../{ => sidechain}/bitcoin_script.cpp | 4 +- .../{ => sidechain}/bitcoin_transaction.cpp | 6 +- .../sidechain/include/sidechain/bech32.hpp | 24 + .../include/sidechain/bitcoin_address.hpp | 133 ++++++ .../include/sidechain}/bitcoin_script.hpp | 2 +- .../include/sidechain/bitcoin_transaction.hpp | 100 ++++ .../bitcoin_transaction_confirmations.hpp | 61 +++ .../sidechain}/input_withdrawal_info.hpp | 9 +- .../sidechain/primary_wallet_vout_manager.hpp | 46 ++ .../include/sidechain}/segwit_addr.hpp | 0 .../include/sidechain}/serialize.hpp | 4 +- .../sidechain/sidechain_condensing_tx.hpp | 52 +++ .../sidechain/sidechain_parameters.hpp | 30 ++ .../sidechain/sidechain_proposal_checker.hpp | 52 +++ .../sidechain/sign_bitcoin_transaction.hpp | 27 ++ .../include/sidechain}/thread_safe_index.hpp | 2 +- .../include/sidechain}/types.hpp | 0 .../sidechain/include/sidechain/utils.hpp | 15 + .../sidechain/input_withdrawal_info.cpp | 207 +++++++++ .../sidechain/network/CMakeLists.txt | 7 + .../network}/bitcoin_rpc_client.cpp | 8 +- .../sidechain/network}/bitcoin_rpc_client.hpp | 0 .../network/sidechain_net_manager.hpp} | 11 +- .../sidechain/network}/zmq_listener.hpp | 0 .../network/sidechain_net_manager.cpp} | 12 +- .../{ => sidechain/network}/zmq_listener.cpp | 4 +- .../sidechain/primary_wallet_vout_manager.cpp | 136 ++++++ .../{ => sidechain}/segwit_addr.cpp | 4 +- .../sidechain/sidechain_condensing_tx.cpp | 107 +++++ .../sidechain/sidechain_proposal_checker.cpp | 214 +++++++++ .../sidechain/sign_bitcoin_transaction.cpp | 125 +++++ .../peerplays_sidechain/sidechain/utils.cpp | 35 ++ 74 files changed, 3307 insertions(+), 100 deletions(-) create mode 100644 libraries/chain/bitcoin_address_evaluator.cpp create mode 100644 libraries/chain/bitcoin_transaction_evaluator.cpp create mode 100644 libraries/chain/db_sidechain.cpp create mode 100644 libraries/chain/hardfork.d/sidechain.hf create mode 100644 libraries/chain/include/graphene/chain/bitcoin_address_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/bitcoin_address_object.hpp create mode 100644 libraries/chain/include/graphene/chain/bitcoin_transaction_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp create mode 100644 libraries/chain/include/graphene/chain/info_for_used_vin_object.hpp rename libraries/{plugins/peerplays_sidechain/include/graphene/peerplays_sidechain => chain/include/graphene/chain}/info_for_vout_object.hpp (94%) create mode 100644 libraries/chain/include/graphene/chain/primary_wallet_vout_object.hpp rename libraries/{plugins/peerplays_sidechain/include/graphene/peerplays_sidechain => chain/include/graphene/chain/protocol}/bitcoin_address.hpp (100%) rename libraries/{plugins/peerplays_sidechain/include/graphene/peerplays_sidechain => chain/include/graphene/chain/protocol}/bitcoin_transaction.hpp (96%) create mode 100644 libraries/chain/include/graphene/chain/protocol/sidechain.hpp create mode 100644 libraries/chain/include/graphene/chain/protocol/withdraw_pbtc.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_evaluator.hpp create mode 100644 libraries/chain/include/graphene/chain/sidechain_proposal_object.hpp create mode 100644 libraries/chain/include/graphene/chain/withdraw_pbtc_evaluator.hpp create mode 100644 libraries/chain/sidechain_evaluator.cpp create mode 100644 libraries/chain/withdraw_pbtc_evaluator.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/CMakeLists.txt create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/bech32.cpp rename libraries/plugins/peerplays_sidechain/{ => sidechain}/bitcoin_address.cpp (97%) rename libraries/plugins/peerplays_sidechain/{ => sidechain}/bitcoin_script.cpp (93%) rename libraries/plugins/peerplays_sidechain/{ => sidechain}/bitcoin_transaction.cpp (97%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bech32.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_address.hpp rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/include/sidechain}/bitcoin_script.hpp (95%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction_confirmations.hpp rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/include/sidechain}/input_withdrawal_info.hpp (93%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/primary_wallet_vout_manager.hpp rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/include/sidechain}/segwit_addr.hpp (100%) rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/include/sidechain}/serialize.hpp (98%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_condensing_tx.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_parameters.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_proposal_checker.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sign_bitcoin_transaction.hpp rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/include/sidechain}/thread_safe_index.hpp (99%) rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/include/sidechain}/types.hpp (100%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/utils.hpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/input_withdrawal_info.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/network/CMakeLists.txt rename libraries/plugins/peerplays_sidechain/{ => sidechain/network}/bitcoin_rpc_client.cpp (98%) rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/network/include/sidechain/network}/bitcoin_rpc_client.hpp (100%) rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain/sidechain_network_manager.hpp => sidechain/network/include/sidechain/network/sidechain_net_manager.hpp} (83%) rename libraries/plugins/peerplays_sidechain/{include/graphene/peerplays_sidechain => sidechain/network/include/sidechain/network}/zmq_listener.hpp (100%) rename libraries/plugins/peerplays_sidechain/{sidechain_network_manager.cpp => sidechain/network/sidechain_net_manager.cpp} (96%) rename libraries/plugins/peerplays_sidechain/{ => sidechain/network}/zmq_listener.cpp (95%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/primary_wallet_vout_manager.cpp rename libraries/plugins/peerplays_sidechain/{ => sidechain}/segwit_addr.cpp (96%) create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/sidechain_condensing_tx.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/sidechain_proposal_checker.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/sign_bitcoin_transaction.cpp create mode 100644 libraries/plugins/peerplays_sidechain/sidechain/utils.cpp diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 3f95a8c1..982b3148 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -105,7 +105,7 @@ class database_api_impl : public std::enable_shared_from_this vector> lookup_asset_symbols(const vector& symbols_or_ids)const; // Peerplays - vector list_sports() const; + vector list_sports() const; vector list_event_groups(sport_id_type sport_id) const; vector list_events_in_group(event_group_id_type event_group_id) const; vector list_betting_market_groups(event_id_type) const; @@ -161,6 +161,9 @@ class database_api_impl : public std::enable_shared_from_this vector get_tournaments_by_state(tournament_id_type stop, unsigned limit, tournament_id_type start, tournament_state state); vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; + //Sidechain + vector 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 database_api_impl::get_full_accounts( const acnt.withdraws.emplace_back(withdraw); }); - auto pending_payouts_range = + auto pending_payouts_range = _db.get_index_type().indices().get().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 database_api::get_tournaments(tournament_id_type stop, vector database_api_impl::get_tournaments(tournament_id_type stop, unsigned limit, - tournament_id_type start) + tournament_id_type start) { vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); @@ -1992,7 +1995,7 @@ vector database_api_impl::get_tournaments_by_state(tournament unsigned limit, tournament_id_type start, tournament_state state) -{ +{ vector result; const auto& tournament_idx = _db.get_index_type().indices().get(); for (auto elem: tournament_idx) { @@ -2023,6 +2026,32 @@ vector database_api_impl::get_registered_tournaments(account return tournament_ids; } +////////////////////////////////////////////////////////////////////// +// // +// Sidechain // +// // +////////////////////////////////////////////////////////////////////// + +vector database_api::get_bitcoin_addresses(const account_id_type& acc) const +{ + return my->get_bitcoin_addresses(acc); +} + +vector database_api_impl::get_bitcoin_addresses(const account_id_type& acc) const +{ + vector result; + + const auto& btc_addr_idx = _db.get_index_type().indices().get(); + + 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) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 3fac4b5f..15012bdc 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -651,6 +652,11 @@ class database_api */ vector get_registered_tournaments(account_id_type account_filter, uint32_t limit) const; + /** + * @return the list of bitcoin addresses which belong to acc_id + */ + vector 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) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index a328cf1f..07c35dd1 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -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" ) \ No newline at end of file diff --git a/libraries/chain/bitcoin_address_evaluator.cpp b/libraries/chain/bitcoin_address_evaluator.cpp new file mode 100644 index 00000000..ab9cb6e1 --- /dev/null +++ b/libraries/chain/bitcoin_address_evaluator.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +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().indices().get(); + 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& 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 diff --git a/libraries/chain/bitcoin_transaction_evaluator.cpp b/libraries/chain/bitcoin_transaction_evaluator.cpp new file mode 100644 index 00000000..95ea9510 --- /dev/null +++ b/libraries/chain/bitcoin_transaction_evaluator.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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( 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 bitcoin_transaction_send_evaluator::create_transaction_vins( bitcoin_transaction_send_operation& op ) +{ + database& d = db(); + std::vector 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 redeem_scripts( d.i_w_info.get_redeem_scripts( vins ) ); + std::vector amounts( d.i_w_info.get_amounts( vins ) ); + + std::vector> 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().indices().get(); + 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(); + + 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(); + 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& sigs, + const std::vector& 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().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( 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 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().indices().get(); + const auto& vouts_info_idx = d.get_index_type().indices().get(); + const auto& vins_info_idx = d.get_index_type().indices().get(); + + 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().indices().get(); + const auto& vouts_info_idx = d.get_index_type().indices().get(); + const auto& vins_info_idx = d.get_index_type().indices().get(); + + 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( trx_info.transaction_id ); + d.remove( btc_trx_obj ); + } + + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 7711f543..e2bdfa62 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -33,4 +33,5 @@ #include "db_market.cpp" #include "db_update.cpp" #include "db_witness_schedule.cpp" -#include "db_notify.cpp" \ No newline at end of file +#include "db_notify.cpp" +#include "db_sidechain.cpp" diff --git a/libraries/chain/db_balance.cpp b/libraries/chain/db_balance.cpp index a70f077b..fb723a2b 100644 --- a/libraries/chain/db_balance.cpp +++ b/libraries/chain/db_balance.cpp @@ -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) ) } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 2650d47c..a377e959 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -44,11 +44,11 @@ #include 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 - void operator()(const T&) + void operator()(const T&) {} - + std::vector proposed_operations_digests; }; - + std::vector 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::value @@ -78,7 +78,7 @@ namespace { else edump( ("Found dup")); } - + return digest_accumulator.proposed_operations_digests; } } @@ -148,24 +148,24 @@ std::vector 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(); std::set existed_operations_digests; - + proposal_index.inspect_all_objects( [&](const object& obj){ const proposal_object& proposal = static_cast(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) ) diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp index 9516e256..62e259d7 100644 --- a/libraries/chain/db_getter.cpp +++ b/libraries/chain/db_getter.cpp @@ -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(); } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index d58c68f7..ec745584 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -49,6 +49,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include @@ -76,6 +82,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -237,6 +247,12 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -301,6 +317,13 @@ void database::initialize_indexes() //add_index< primary_index >(); add_index< primary_index >(); add_index< primary_index >(); + + add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); } void database::init_genesis(const genesis_state_type& genesis_state) diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 6bcee4bd..f528b7c9 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -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) ) } diff --git a/libraries/chain/db_notify.cpp b/libraries/chain/db_notify.cpp index 53ec524d..f0c02a12 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -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& result ) diff --git a/libraries/chain/db_sidechain.cpp b/libraries/chain/db_sidechain.cpp new file mode 100644 index 00000000..f1d7b311 --- /dev/null +++ b/libraries/chain/db_sidechain.cpp @@ -0,0 +1,432 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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().indices().get(); + 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& obj ) { + obj.name = "sidechain_account"; + obj.statistics = create([&]( 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& 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& 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().indices().get(); + std::vector 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& 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().indices().get(); + 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().indices().get< by_id >(); + const auto& proposal_idx = get_index_type().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(); + 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(); + if( checker.check_bitcoin_transaction_revert_operation( op ) ) + approve_propose( proposal->id ); + break; + } + } + } +} + +full_btc_transaction database::create_btc_transaction( const std::vector& info_vins, + const std::vector& 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_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_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_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 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 + 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(); + + 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(); +} + +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().indices().get(); + const auto& proposal_itr = proposal_idx.find( proposal_id ); + bitcoin_transaction_send_operation op = proposal_itr->proposed_transaction.operations.back().get(); + + 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 redeem_scripts( i_w_info.get_redeem_scripts( vins ) ); + std::vector 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::value || + proposal.proposed_transaction.operations.back().which() == operation::tag::value || + proposal.proposed_transaction.operations.back().which() == operation::tag::value ) ) + { + const auto& sidechain_proposal_idx = get_index_type().indices().get(); + 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::value ) + { + bitcoin_transaction_send_operation op = proposal.proposed_transaction.operations.back().get(); + + 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 database::create_bitcoin_issue_proposals( const witness_object& current_witness ) +{ + std::vector trx_ids; + bitcoin_confirmations.safe_for([&]( btc_tx_confirmations_index::index::type::iterator itr_b, btc_tx_confirmations_index::index::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().indices().get(); + 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( proposal_op ); + } + + return fc::optional(); +} + +fc::optional database::create_bitcoin_revert_proposals( const witness_object& current_witness ) +{ + using iter_by_missing = btc_tx_confirmations_index::index::type::iterator; + std::vector trx_info; + + bitcoin_confirmations.safe_for([&]( 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().indices().get(); + 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( proposal_op ); + } + + return fc::optional(); +} + + +void database::restore_bitcoin_transaction_status() +{ + const auto& btc_tx_idx = get_index_type().indices().get(); + for( const auto& bto : btc_tx_idx ) { + std::set 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 ) ); + } +} + +} } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index ad98837e..e7a1575e 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -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); } } diff --git a/libraries/chain/hardfork.d/sidechain.hf b/libraries/chain/hardfork.d/sidechain.hf new file mode 100644 index 00000000..44400d44 --- /dev/null +++ b/libraries/chain/hardfork.d/sidechain.hf @@ -0,0 +1,4 @@ +// sidechain fardfork +#ifndef HARDFORK_SIDECHAIN_TIME +#define HARDFORK_SIDECHAIN_TIME (fc::time_point_sec( 1550000000 )) +#endif diff --git a/libraries/chain/include/graphene/chain/bitcoin_address_evaluator.hpp b/libraries/chain/include/graphene/chain/bitcoin_address_evaluator.hpp new file mode 100644 index 00000000..8055c6c5 --- /dev/null +++ b/libraries/chain/include/graphene/chain/bitcoin_address_evaluator.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace graphene { namespace chain { + +class bitcoin_address_create_evaluator : public 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 diff --git a/libraries/chain/include/graphene/chain/bitcoin_address_object.hpp b/libraries/chain/include/graphene/chain/bitcoin_address_object.hpp new file mode 100644 index 00000000..5fab6c67 --- /dev/null +++ b/libraries/chain/include/graphene/chain/bitcoin_address_object.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include + +namespace graphene { namespace chain { + +class bitcoin_address_object : public abstract_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, member< object, object_id_type, &object::id > >, + ordered_unique< tag, const_mem_fun< bitcoin_address_object, std::string, &bitcoin_address_object::get_address > >, + ordered_non_unique< tag, member< bitcoin_address_object, account_id_type, &bitcoin_address_object::owner > > + > +> bitcoin_address_multi_index_container; +typedef generic_index bitcoin_address_index; + +} } + +FC_REFLECT_DERIVED( graphene::chain::bitcoin_address_object, (graphene::chain::object), (owner)(address)(count_invalid_pub_key) ) + diff --git a/libraries/chain/include/graphene/chain/bitcoin_transaction_evaluator.hpp b/libraries/chain/include/graphene/chain/bitcoin_transaction_evaluator.hpp new file mode 100644 index 00000000..973a5fcf --- /dev/null +++ b/libraries/chain/include/graphene/chain/bitcoin_transaction_evaluator.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include + +namespace sidechain { class info_for_vin; } + +namespace graphene { namespace chain { + +class bitcoin_transaction_send_evaluator : public 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 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 +{ + +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& sigs, + const std::vector& info_for_vins, const sidechain::bitcoin_transaction& tx ); + +}; + +class bitcoin_transaction_revert_evaluator : public 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 \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp b/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp new file mode 100644 index 00000000..fa440bc9 --- /dev/null +++ b/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace chain { + +class bitcoin_transaction_object : public abstract_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_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::bitcoin_transaction_object, (graphene::chain::object), (pw_vin)(vins)(vouts)(transaction)(transaction_id)(fee_for_size) ) diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 7b3e8743..1d86db2a 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -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 ) +////////////////////////////////////////////////////////////////////// diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 179fb2df..8652416f 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -40,9 +40,20 @@ #include +#include +#include +#include +#include + #include #include +#include + +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& checkpts ); const flat_map 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 >& 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 >& 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 helpers); ///@} ///@} + //////////////////// db_sidechain.cpp //////////////////// + public: + + using full_btc_tx_and_new_vins = std::pair< sidechain::full_btc_transaction, std::vector >; + + 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_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_vouts, + const info_for_vin& info_pw_vin ); + + sidechain::full_btc_transaction create_btc_transaction( const std::vector& info_vins, + const std::vector& info_vouts, + const info_for_vin& info_pw_vin ); + fc::optional 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 create_bitcoin_issue_proposals( const witness_object& current_witness ); + + fc::optional create_bitcoin_revert_proposals( const witness_object& current_witness ); + + fc::signal send_btc_tx; + + sidechain::input_withdrawal_info i_w_info; + + sidechain::thread_safe_index bitcoin_confirmations; + + sidechain::primary_wallet_vout_manager pw_vout_manager; + + std::atomic 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; diff --git a/libraries/chain/include/graphene/chain/info_for_used_vin_object.hpp b/libraries/chain/include/graphene/chain/info_for_used_vin_object.hpp new file mode 100644 index 00000000..abd4ff67 --- /dev/null +++ b/libraries/chain/include/graphene/chain/info_for_used_vin_object.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include + +namespace graphene { namespace chain { + +class info_for_used_vin_object : public abstract_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, member> + > +> info_for_used_vin_multi_index_container; +typedef generic_index info_for_used_vin_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::info_for_used_vin_object, (graphene::chain::object), (identifier)(out)(address)(script) ) diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/info_for_vout_object.hpp b/libraries/chain/include/graphene/chain/info_for_vout_object.hpp similarity index 94% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/info_for_vout_object.hpp rename to libraries/chain/include/graphene/chain/info_for_vout_object.hpp index fcc06afe..ba0ecf88 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/info_for_vout_object.hpp +++ b/libraries/chain/include/graphene/chain/info_for_vout_object.hpp @@ -2,8 +2,8 @@ #include #include -#include -#include +#include +#include namespace graphene { namespace chain { @@ -52,3 +52,4 @@ typedef generic_index } } // graphene::chain FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(address)(amount)(used) ) + diff --git a/libraries/chain/include/graphene/chain/primary_wallet_vout_object.hpp b/libraries/chain/include/graphene/chain/primary_wallet_vout_object.hpp new file mode 100644 index 00000000..7d9068b0 --- /dev/null +++ b/libraries/chain/include/graphene/chain/primary_wallet_vout_object.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace chain { + +class primary_wallet_vout_object : public abstract_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_index; + +} } + +FC_REFLECT_DERIVED( graphene::chain::primary_wallet_vout_object, (graphene::chain::object), (vout)(hash_id)(confirmed)(used) ) + diff --git a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp index bf6fc547..afabacc1 100644 --- a/libraries/chain/include/graphene/chain/proposal_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/proposal_evaluator.hpp @@ -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 + 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 { public: diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_address.hpp b/libraries/chain/include/graphene/chain/protocol/bitcoin_address.hpp similarity index 100% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_address.hpp rename to libraries/chain/include/graphene/chain/protocol/bitcoin_address.hpp diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp similarity index 96% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_transaction.hpp rename to libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp index 5ec8d9ac..9115f415 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include -#include +#include +#include namespace graphene { namespace chain { @@ -94,4 +94,3 @@ FC_REFLECT( graphene::chain::bitcoin_transaction_sign_operation, (fee)(payer)(pr 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) ) - diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 87c2e3fe..9075203f 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -39,6 +40,7 @@ namespace graphene { namespace chain { optional< uint16_t > betting_rake_fee_percentage; optional< flat_map > 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; diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 104a2ec3..f9fc8b73 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -44,6 +44,10 @@ #include #include #include +#include +#include +#include +#include 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 diff --git a/libraries/chain/include/graphene/chain/protocol/sidechain.hpp b/libraries/chain/include/graphene/chain/protocol/sidechain.hpp new file mode 100644 index 00000000..8475fef8 --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/sidechain.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +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 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) ) diff --git a/libraries/chain/include/graphene/chain/protocol/withdraw_pbtc.hpp b/libraries/chain/include/graphene/chain/protocol/withdraw_pbtc.hpp new file mode 100644 index 00000000..567b2aac --- /dev/null +++ b/libraries/chain/include/graphene/chain/protocol/withdraw_pbtc.hpp @@ -0,0 +1,33 @@ +#pragma once +#include + +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) ) diff --git a/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp new file mode 100644 index 00000000..a8c7787f --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +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 get_amounts_to_issue( std::vector vins_identifier ); + + std::vector get_accounts_to_issue( std::vector vins_identifier ); +}; + +} } // graphene::chain \ No newline at end of file diff --git a/libraries/chain/include/graphene/chain/sidechain_proposal_object.hpp b/libraries/chain/include/graphene/chain/sidechain_proposal_object.hpp new file mode 100644 index 00000000..456e941e --- /dev/null +++ b/libraries/chain/include/graphene/chain/sidechain_proposal_object.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +namespace graphene { namespace chain { + +class sidechain_proposal_object : public abstract_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_index; + +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::sidechain_proposal_object, (graphene::chain::object), (proposal_id)(proposal_type) ) diff --git a/libraries/chain/include/graphene/chain/withdraw_pbtc_evaluator.hpp b/libraries/chain/include/graphene/chain/withdraw_pbtc_evaluator.hpp new file mode 100644 index 00000000..2812d675 --- /dev/null +++ b/libraries/chain/include/graphene/chain/withdraw_pbtc_evaluator.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +using namespace sidechain; + +namespace graphene { namespace chain { + +class withdraw_pbtc_evaluator : public 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 diff --git a/libraries/chain/sidechain_evaluator.cpp b/libraries/chain/sidechain_evaluator.cpp new file mode 100644 index 00000000..b084269f --- /dev/null +++ b/libraries/chain/sidechain_evaluator.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include + +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().indices().get(); + const auto& btc_addr_idx = d.get_index_type().indices().get(); + const auto& vins_info_idx = d.get_index_type().indices().get(); + const auto& vouts_info_idx = d.get_index_type().indices().get(); + 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().indices().get(); + + 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().indices().get(); + const auto& vouts_info_idx = d.get_index_type().indices().get(); + + 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( btc_obj.transaction_id ); + if( trx_approvals.valid() ) { + d.bitcoin_confirmations.remove( btc_obj.transaction_id ); + } + + d.remove( btc_obj ); +} + +std::vector bitcoin_issue_evaluator::get_amounts_to_issue( std::vector vins_identifier ) +{ + database& d = db(); + const auto& vins_info_idx = d.get_index_type().indices().get(); + + std::vector 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 bitcoin_issue_evaluator::get_accounts_to_issue( std::vector vins_identifier ) +{ + database& d = db(); + const auto& btc_addr_idx = d.get_index_type().indices().get(); + const auto& vins_info_idx = d.get_index_type().indices().get(); + + std::vector 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 \ No newline at end of file diff --git a/libraries/chain/withdraw_pbtc_evaluator.cpp b/libraries/chain/withdraw_pbtc_evaluator.cpp new file mode 100644 index 00000000..b1b517fd --- /dev/null +++ b/libraries/chain/withdraw_pbtc_evaluator.cpp @@ -0,0 +1,85 @@ +#include +#include +#include + +#include +#include +#include +#include + +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 diff --git a/libraries/plugins/peerplays_sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/CMakeLists.txt index cdb17b0a..ad48ca62 100644 --- a/libraries/plugins/peerplays_sidechain/CMakeLists.txt +++ b/libraries/plugins/peerplays_sidechain/CMakeLists.txt @@ -4,7 +4,7 @@ add_library( peerplays_sidechain peerplays_sidechain_plugin.cpp ) -target_link_libraries( peerplays_sidechain graphene_chain graphene_app zmq ) +target_link_libraries( peerplays_sidechain sidechain graphene_chain graphene_app ) target_include_directories( peerplays_sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) @@ -15,5 +15,4 @@ install( TARGETS LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) -INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/peerplays_sidechain" ) - +INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/peerplays_sidechain" ) \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp index d32fb09d..75d7abc1 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp @@ -31,4 +31,3 @@ class peerplays_sidechain_plugin : public graphene::app::plugin }; } } //graphene::peerplays_sidechain_plugin - diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index d94b0e0a..8851f697 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -1,6 +1,6 @@ #include -#include +#include namespace bpo = boost::program_options; diff --git a/libraries/plugins/peerplays_sidechain/sidechain/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/sidechain/CMakeLists.txt new file mode 100644 index 00000000..515acbe2 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/CMakeLists.txt @@ -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" ) diff --git a/libraries/plugins/peerplays_sidechain/sidechain/bech32.cpp b/libraries/plugins/peerplays_sidechain/sidechain/bech32.cpp new file mode 100644 index 00000000..c861169d --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/bech32.cpp @@ -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 + +// #include + +namespace +{ + +typedef std::vector 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 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 diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_address.cpp b/libraries/plugins/peerplays_sidechain/sidechain/bitcoin_address.cpp similarity index 97% rename from libraries/plugins/peerplays_sidechain/bitcoin_address.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/bitcoin_address.cpp index d24ff2c7..1ebab95b 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_address.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/bitcoin_address.cpp @@ -1,8 +1,8 @@ -#include +#include #include #include -#include -#include +#include +#include namespace sidechain { @@ -85,7 +85,7 @@ bool bitcoin_address::check_segwit_address( const size_segwit_address& size ) co 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; } @@ -158,7 +158,7 @@ btc_multisig_address::btc_multisig_address( const size_t n_required, const accou 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 ); @@ -240,7 +240,7 @@ 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 ) ); } @@ -251,7 +251,7 @@ bytes btc_multisig_segwit_address::get_address_bytes( const bytes& script_hash ) 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; } diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_script.cpp b/libraries/plugins/peerplays_sidechain/sidechain/bitcoin_script.cpp similarity index 93% rename from libraries/plugins/peerplays_sidechain/bitcoin_script.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/bitcoin_script.cpp index b71e8387..367e5ff8 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_script.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/bitcoin_script.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include namespace sidechain { diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/sidechain/bitcoin_transaction.cpp similarity index 97% rename from libraries/plugins/peerplays_sidechain/bitcoin_transaction.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/bitcoin_transaction.cpp index 9d7407de..e099cd3f 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_transaction.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/bitcoin_transaction.cpp @@ -1,7 +1,7 @@ -#include +#include #include -#include -#include +#include +#include namespace sidechain { diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bech32.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bech32.hpp new file mode 100644 index 00000000..d5361312 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bech32.hpp @@ -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 +#include +#include + +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& values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair> Decode(const std::string& str); + +} } // namespace sidechain::bech32 diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_address.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_address.hpp new file mode 100644 index 00000000..16041e54 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_address.hpp @@ -0,0 +1,133 @@ +#pragma once + +#include +#include + +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 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) ); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_script.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_script.hpp similarity index 95% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_script.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_script.hpp index 42db8974..1bd8e061 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_script.hpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_script.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace sidechain { diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction.hpp new file mode 100644 index 00000000..6271398a --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction.hpp @@ -0,0 +1,100 @@ +#pragma once +#include + +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 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 vin; + std::vector 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) ) diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction_confirmations.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction_confirmations.hpp new file mode 100644 index 00000000..5b32aad2 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/bitcoin_transaction_confirmations.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +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& 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 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, member>, + ordered_non_unique, identity< bitcoin_transaction_confirmations >, bitcoin_transaction_confirmations::comparer >, + ordered_non_unique, identity< bitcoin_transaction_confirmations >, bitcoin_transaction_confirmations::comparer > + > +>; + +} \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/input_withdrawal_info.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/input_withdrawal_info.hpp similarity index 93% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/input_withdrawal_info.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/input_withdrawal_info.hpp index 208aa21b..4210002b 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/input_withdrawal_info.hpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/input_withdrawal_info.hpp @@ -4,11 +4,12 @@ #include #include +#include +#include #include + +#include #include -#include -#include -#include using boost::multi_index_container; using namespace boost::multi_index; @@ -110,4 +111,4 @@ private: } -FC_REFLECT( sidechain::info_for_vin, (identifier)(out)(address)(script)(used)(resend) ) +FC_REFLECT( sidechain::info_for_vin, (identifier)(out)(address)(script)(used)(resend) ) \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/primary_wallet_vout_manager.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/primary_wallet_vout_manager.hpp new file mode 100644 index 00000000..874c4c88 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/primary_wallet_vout_manager.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +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; + +}; + +} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/segwit_addr.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/segwit_addr.hpp similarity index 100% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/segwit_addr.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/segwit_addr.hpp diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/serialize.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/serialize.hpp similarity index 98% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/serialize.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/serialize.hpp index 7853a673..12b6e7fb 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/serialize.hpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/serialize.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include namespace sidechain { diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_condensing_tx.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_condensing_tx.hpp new file mode 100644 index 00000000..5ce0fac2 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_condensing_tx.hpp @@ -0,0 +1,52 @@ +#pragma once +#include +#include + +using namespace sidechain; + +namespace sidechain { + +class sidechain_condensing_tx +{ + +public: + + sidechain_condensing_tx( const std::vector& vin_info, const std::vector& 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& vin_info ); + + void create_vouts_for_condensing_tx( const std::vector& 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; + +}; + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_parameters.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_parameters.hpp new file mode 100644 index 00000000..1aedc117 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_parameters.hpp @@ -0,0 +1,30 @@ +#pragma once +#include + + +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) +) \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_proposal_checker.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_proposal_checker.hpp new file mode 100644 index 00000000..fce1c425 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sidechain_proposal_checker.hpp @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include + +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_vins ); + + bool check_info_for_vouts( const std::vector& 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& user_vin_identifiers ); + + bool check_reuse_vouts( const std::vector& user_vout_ids ); + + std::set pw_vin_ident; + std::set user_vin_ident; + std::set vout_ids; + + graphene::chain::database& db; + +}; + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sign_bitcoin_transaction.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sign_bitcoin_transaction.hpp new file mode 100644 index 00000000..6c2ade49 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/sign_bitcoin_transaction.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +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 privkey_sign( const bytes& privkey, const fc::sha256 &hash, const secp256k1_context_t* context_sign = nullptr ); + +std::vector sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector& redeem_scripts, + const std::vector& 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& redeem_scripts ); + +bool verify_sig( const bytes& sig, const bytes& pubkey, const bytes& msg, const secp256k1_context_t* context ); + +std::vector> sort_sigs( const bitcoin_transaction& tx, const std::vector& redeem_scripts, + const std::vector& amounts, const secp256k1_context_t* context ); + +} diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/thread_safe_index.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/thread_safe_index.hpp similarity index 99% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/thread_safe_index.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/thread_safe_index.hpp index c5508a38..1742695b 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/thread_safe_index.hpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/thread_safe_index.hpp @@ -74,7 +74,7 @@ private: } std::recursive_mutex lock; - + T1 data; }; diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/types.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/types.hpp similarity index 100% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/types.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/types.hpp diff --git a/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/utils.hpp b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/utils.hpp new file mode 100644 index 00000000..276f888f --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/include/sidechain/utils.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +namespace sidechain { + +bytes parse_hex( const std::string& str ); + +std::vector get_pubkey_from_redeemScript( bytes script ); + +bytes public_key_data_to_bytes( const fc::ecc::public_key_data& key ); + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/input_withdrawal_info.cpp b/libraries/plugins/peerplays_sidechain/sidechain/input_withdrawal_info.cpp new file mode 100644 index 00000000..4711f027 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/input_withdrawal_info.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include + +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 input_withdrawal_info::get_redeem_scripts( const std::vector& info_vins ) +{ + std::vector redeem_scripts; + const auto& bitcoin_address_idx = db.get_index_type().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 input_withdrawal_info::get_amounts( const std::vector& info_vins ) +{ + std::vector amounts; + for( const auto& v : info_vins ) { + amounts.push_back( v.out.amount ); + } + return amounts; +} + +fc::optional 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(); + } + + 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& func ) +{ + info_for_vins.modify( obj.identifier, func ); +} + +void input_withdrawal_info::mark_as_used_vin( const info_for_vin& obj ) +{ + info_for_vins.modify( 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( 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( obj.identifier ); +} + +fc::optional input_withdrawal_info::find_info_for_vin( fc::sha256 identifier ) +{ + return info_for_vins.find( identifier ); +} + +std::vector input_withdrawal_info::get_info_for_vins() +{ + std::vector result; + const auto max_vins = db.get_sidechain_params().maximum_condensing_tx_vins; + + info_for_vins.safe_for( [&]( info_for_vin_index::index::type::iterator itr_b, + info_for_vin_index::index::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& 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().indices().get< graphene::chain::by_id >(); + auto itr = info_for_vout_idx.find( obj.id ); + + db.modify( *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().indices().get< graphene::chain::by_id >(); + auto itr = info_for_vout_idx.find( obj.id ); + + db.modify( *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().indices().get< graphene::chain::by_id >(); + auto itr = info_for_vout_idx.find( obj.id ); + db.remove( *itr ); +} + +fc::optional 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().indices().get< graphene::chain::by_id >(); + auto itr = info_for_vout_idx.find( id ); + if( itr != info_for_vout_idx.end() ) + return fc::optional( *itr ); + return fc::optional(); +} + +size_t input_withdrawal_info::size_info_for_vouts() +{ + const auto& info_for_vout_idx = db.get_index_type().indices().get< graphene::chain::by_id >(); + return info_for_vout_idx.size(); +} + +std::vector input_withdrawal_info::get_info_for_vouts() +{ + std::vector result; + + const auto& info_for_vout_idx = db.get_index_type().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; +} + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/network/CMakeLists.txt b/libraries/plugins/peerplays_sidechain/sidechain/network/CMakeLists.txt new file mode 100644 index 00000000..c7e3ca91 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/network/CMakeLists.txt @@ -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" ) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin_rpc_client.cpp b/libraries/plugins/peerplays_sidechain/sidechain/network/bitcoin_rpc_client.cpp similarity index 98% rename from libraries/plugins/peerplays_sidechain/bitcoin_rpc_client.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/network/bitcoin_rpc_client.cpp index 73ce4b66..9a1609e3 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin_rpc_client.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/network/bitcoin_rpc_client.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -10,7 +10,7 @@ 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 ) + ip( _ip ), rpc_port( _rpc ), user( _user ), password( _password ) { authorization.key = "Authorization"; authorization.val = "Basic " + fc::base64_encode( user + ":" + password ); @@ -42,7 +42,7 @@ int32_t bitcoin_rpc_client::receive_confirmations_tx( const std::string& tx_hash 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 ); @@ -137,4 +137,4 @@ fc::http::reply bitcoin_rpc_client::send_post_request( std::string body ) return conn.request( "POST", url, body, fc::http::headers{authorization} ); } -} +} \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_rpc_client.hpp b/libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/bitcoin_rpc_client.hpp similarity index 100% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin_rpc_client.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/bitcoin_rpc_client.hpp diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_network_manager.hpp b/libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/sidechain_net_manager.hpp similarity index 83% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_network_manager.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/sidechain_net_manager.hpp index 5c8604a1..16c524c7 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_network_manager.hpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/sidechain_net_manager.hpp @@ -1,25 +1,24 @@ #pragma once -#include -#include -#include +#include +#include #include namespace sidechain { -class sidechain_net_manager +class sidechain_net_manager { public: sidechain_net_manager() {}; - sidechain_net_manager( graphene::chain::database* _db, std::string _ip, + 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, + 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 ); diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/zmq_listener.hpp b/libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/zmq_listener.hpp similarity index 100% rename from libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/zmq_listener.hpp rename to libraries/plugins/peerplays_sidechain/sidechain/network/include/sidechain/network/zmq_listener.hpp diff --git a/libraries/plugins/peerplays_sidechain/sidechain_network_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain/network/sidechain_net_manager.cpp similarity index 96% rename from libraries/plugins/peerplays_sidechain/sidechain_network_manager.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/network/sidechain_net_manager.cpp index 25d65374..0deb5960 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_network_manager.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/network/sidechain_net_manager.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include +#include +#include #include #include @@ -14,13 +14,13 @@ namespace sidechain { -sidechain_net_manager::sidechain_net_manager( graphene::chain::database* _db, std::string _ip, +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, +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; @@ -199,4 +199,4 @@ inline uint64_t sidechain_net_manager::parse_amount(std::string raw) { return std::stoll(raw); } -} +} \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/zmq_listener.cpp b/libraries/plugins/peerplays_sidechain/sidechain/network/zmq_listener.cpp similarity index 95% rename from libraries/plugins/peerplays_sidechain/zmq_listener.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/network/zmq_listener.cpp index 36f076e8..eb697a75 100644 --- a/libraries/plugins/peerplays_sidechain/zmq_listener.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/network/zmq_listener.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -42,4 +42,4 @@ void zmq_listener::handle_zmq() } } -} +} \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/sidechain/primary_wallet_vout_manager.cpp b/libraries/plugins/peerplays_sidechain/sidechain/primary_wallet_vout_manager.cpp new file mode 100644 index 00000000..c7c9ccaa --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/primary_wallet_vout_manager.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include + + +namespace sidechain { + +bool primary_wallet_vout_manager::is_max_vouts() const +{ + const auto& PW_vout_idx = db.get_index_type().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().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().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().get_next_id().instance(); + db.create([&]( 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().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().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().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().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().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 ); +} + +} \ No newline at end of file diff --git a/libraries/plugins/peerplays_sidechain/segwit_addr.cpp b/libraries/plugins/peerplays_sidechain/sidechain/segwit_addr.cpp similarity index 96% rename from libraries/plugins/peerplays_sidechain/segwit_addr.cpp rename to libraries/plugins/peerplays_sidechain/sidechain/segwit_addr.cpp index 2401f1ae..ce7b33ad 100644 --- a/libraries/plugins/peerplays_sidechain/segwit_addr.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain/segwit_addr.cpp @@ -19,8 +19,8 @@ * THE SOFTWARE. */ -#include -#include +#include +#include namespace { diff --git a/libraries/plugins/peerplays_sidechain/sidechain/sidechain_condensing_tx.cpp b/libraries/plugins/peerplays_sidechain/sidechain/sidechain_condensing_tx.cpp new file mode 100644 index 00000000..cb742727 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/sidechain_condensing_tx.cpp @@ -0,0 +1,107 @@ +#include + +namespace sidechain { + +sidechain_condensing_tx::sidechain_condensing_tx( const std::vector& vin_info, const std::vector& 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& 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& 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 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 ) ); +} + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/sidechain_proposal_checker.cpp b/libraries/plugins/peerplays_sidechain/sidechain/sidechain_proposal_checker.cpp new file mode 100644 index 00000000..214a1808 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/sidechain_proposal_checker.cpp @@ -0,0 +1,214 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +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& 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& 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 user_vin_identifiers; + std::vector user_vout_ids; + + auto get_bto_tx_info = [ & ]( fc::sha256 trx_id ) { + const auto& bto_itr_idx = db.get_index_type().indices().get(); + 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::value ) { + bitcoin_transaction_send_operation btc_tx_send_op = op.get(); + 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::value ) { + bitcoin_issue_operation btc_issue_op = op.get(); + for( const auto& id : btc_issue_op.transaction_ids ) { + if ( !get_bto_tx_info( id ) ) + return false; + } + } else if ( op.which() == operation::tag::value ) { + bitcoin_transaction_revert_operation btc_tx_revert_op = op.get(); + 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_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_ids ) +{ + const auto& info_for_vout_idx = db.get_index_type().indices().get(); + 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_vouts; + const auto& info_for_vout_idx = db.get_index_type().indices().get(); + 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().indices().get(); + 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(); + + 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().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().indices().get(); + + for( auto trx_info: op.transactions_info ) + { + auto value = db.bitcoin_confirmations.find( 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(); +} + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/sign_bitcoin_transaction.cpp b/libraries/plugins/peerplays_sidechain/sidechain/sign_bitcoin_transaction.cpp new file mode 100644 index 00000000..2c22d2b1 --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/sign_bitcoin_transaction.cpp @@ -0,0 +1,125 @@ +#include +#include + +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 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 vec( ps.tellp() ); + if ( !vec.empty() ) { + fc::datastream 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 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( hash.data() ), + reinterpret_cast( sig.data() ), + &sig_len, + reinterpret_cast( privkey.data() ), + secp256k1_nonce_function_rfc6979, + nullptr ) ); // TODO: replace assert with exception + + sig.resize( sig_len ); + + return sig; +} + +std::vector sign_witness_transaction_part( const bitcoin_transaction& tx, const std::vector& redeem_scripts, + const std::vector& 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( amounts[i] ), i, hash_type, true ); + auto sig = privkey_sign( privkey, sighash, context_sign ); + sig.push_back( static_cast( hash_type ) ); + + signatures.push_back( sig ); + } + return signatures; +} + +void sign_witness_transaction_finalize( bitcoin_transaction& tx, const std::vector& 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 sig_temp( sig.begin(), sig.end() ); + std::vector pubkey_temp( pubkey.begin(), pubkey.end() ); + std::vector 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> sort_sigs( const bitcoin_transaction& tx, const std::vector& redeem_scripts, + const std::vector& amounts, const secp256k1_context_t* context ) +{ + FC_ASSERT( redeem_scripts.size() == amounts.size() ); + + using data = std::pair; + struct comp { + bool operator() (const data& lhs, const data& rhs) const { return lhs.first < rhs.first; } + }; + + std::vector> new_stacks; + + for( size_t i = 0; i < redeem_scripts.size(); i++ ) { + const std::vector& keys = get_pubkey_from_redeemScript( redeem_scripts[i] ); + const auto& sighash = get_signature_hash( tx, redeem_scripts[i], static_cast( amounts[i] ), i, 1, true ).str(); + bytes sighash_temp( parse_hex( sighash ) ); + + std::vector stack( tx.vin[i].scriptWitness ); + std::vector marker( tx.vin[i].scriptWitness.size(), false ); + std::set 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 temp_sig; + for( auto s : sigs ) { + temp_sig.push_back( s.second ); + } + new_stacks.push_back( temp_sig ); + } + return new_stacks; +} + +} diff --git a/libraries/plugins/peerplays_sidechain/sidechain/utils.cpp b/libraries/plugins/peerplays_sidechain/sidechain/utils.cpp new file mode 100644 index 00000000..b13c63fb --- /dev/null +++ b/libraries/plugins/peerplays_sidechain/sidechain/utils.cpp @@ -0,0 +1,35 @@ +#include + +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 get_pubkey_from_redeemScript( bytes script ) +{ + FC_ASSERT( script.size() >= 37 ); + + script.erase( script.begin() ); + script.erase( script.end() - 2, script.end() ); + + std::vector 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; +} + +}