From ec3b7e8b80ca29cd55096e44250b368296d7a7f3 Mon Sep 17 00:00:00 2001 From: Anzhy Cherrnyavski Date: Thu, 7 Feb 2019 11:49:10 +0300 Subject: [PATCH] Added class sidechain_proposal_checker --- .../chain/bitcoin_transaction_evaluator.cpp | 15 +- libraries/chain/db_sidechain.cpp | 17 ++- .../chain/bitcoin_transaction_evaluator.hpp | 2 +- .../chain/bitcoin_transaction_object.hpp | 2 +- .../graphene/chain/sidechain_evaluator.hpp | 4 +- libraries/chain/sidechain_evaluator.cpp | 20 +-- libraries/sidechain/bitcoin_address.cpp | 32 +++- libraries/sidechain/bitcoin_transaction.cpp | 43 ++++++ .../include/sidechain/bitcoin_address.hpp | 8 +- .../include/sidechain/bitcoin_transaction.hpp | 23 ++- .../sidechain/input_withdrawal_info.hpp | 7 +- .../sidechain/sidechain_condensing_tx.hpp | 2 +- .../sidechain/sidechain_proposal_checker.hpp | 45 ++++++ .../sidechain/include/sidechain/types.hpp | 11 ++ libraries/sidechain/input_withdrawal_info.cpp | 26 +++- .../network/sidechain_net_manager.cpp | 5 +- .../sidechain/sidechain_condensing_tx.cpp | 7 +- .../sidechain/sidechain_proposal_checker.cpp | 144 ++++++++++++++++++ tests/tests/bitcoin_issue_tests.cpp | 8 +- .../sidechain_proposal_checker_tests.cpp | 140 +++++++++++++++++ 20 files changed, 508 insertions(+), 53 deletions(-) create mode 100644 libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp create mode 100644 libraries/sidechain/sidechain_proposal_checker.cpp create mode 100644 tests/tests/sidechain_proposal_checker_tests.cpp diff --git a/libraries/chain/bitcoin_transaction_evaluator.cpp b/libraries/chain/bitcoin_transaction_evaluator.cpp index 8693b648..f86429f7 100644 --- a/libraries/chain/bitcoin_transaction_evaluator.cpp +++ b/libraries/chain/bitcoin_transaction_evaluator.cpp @@ -52,24 +52,25 @@ object_id_type bitcoin_transaction_send_evaluator::do_apply( const bitcoin_trans return btc_tx.id; } -std::vector< info_for_used_vin_id_type > bitcoin_transaction_send_evaluator::create_transaction_vins( bitcoin_transaction_send_operation& op ) +std::vector bitcoin_transaction_send_evaluator::create_transaction_vins( bitcoin_transaction_send_operation& op ) { database& d = db(); - std::vector< info_for_used_vin_id_type > new_vins; + std::vector new_vins; - for( auto itr : op.vins ){ - auto itr_id = d.create< info_for_used_vin_object >( [&]( info_for_used_vin_object& obj ) + 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; - }).get_id(); - new_vins.push_back( itr_id ); + }); + 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() ) + if( obj_itr.valid() ) { d.i_w_info.remove_info_for_vin( *obj_itr ); + } } return new_vins; diff --git a/libraries/chain/db_sidechain.cpp b/libraries/chain/db_sidechain.cpp index a1a7016a..b1c35d9f 100644 --- a/libraries/chain/db_sidechain.cpp +++ b/libraries/chain/db_sidechain.cpp @@ -7,6 +7,7 @@ #include #include +#include using namespace sidechain; @@ -129,11 +130,17 @@ void database::processing_sidechain_proposals( const witness_object& current_wit 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 ); + 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 :{ proposal_update_operation puo; @@ -144,8 +151,12 @@ void database::processing_sidechain_proposals( const witness_object& current_wit break; } case sidechain_proposal_type::SEND_BTC_TRANSACTION :{ - 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 ) ); + bitcoin_transaction_send_operation op = proposal->proposed_transaction.operations.back().get(); + if( checker.check_bitcoin_transaction_send_operation( op ) ) + { + 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::RETURN_PBTC_BACK :{} @@ -174,7 +185,7 @@ full_btc_transaction database::create_btc_transaction( const std::vector create_transaction_vins( bitcoin_transaction_send_operation& op ); + std::vector create_transaction_vins( bitcoin_transaction_send_operation& op ); void finalize_bitcoin_transaction( bitcoin_transaction_send_operation& op ); diff --git a/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp b/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp index 8a0898f0..fa440bc9 100644 --- a/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp +++ b/libraries/chain/include/graphene/chain/bitcoin_transaction_object.hpp @@ -16,7 +16,7 @@ class bitcoin_transaction_object : public abstract_object vins; + std::vector< fc::sha256 > vins; std::vector< info_for_vout_id_type > vouts; sidechain::bitcoin_transaction transaction; diff --git a/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp b/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp index 8b61fc1d..a8c7787f 100644 --- a/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/sidechain_evaluator.hpp @@ -18,9 +18,9 @@ struct bitcoin_issue_evaluator : public evaluator< bitcoin_issue_evaluator > void clear_btc_transaction_information( const bitcoin_transaction_object& btc_obj ); - std::vector get_amounts_to_issue( std::vector vins_id ); + std::vector get_amounts_to_issue( std::vector vins_identifier ); - std::vector get_accounts_to_issue( std::vector vins_id ); + std::vector get_accounts_to_issue( std::vector vins_identifier ); }; } } // graphene::chain \ No newline at end of file diff --git a/libraries/chain/sidechain_evaluator.cpp b/libraries/chain/sidechain_evaluator.cpp index 3e10c936..b084269f 100644 --- a/libraries/chain/sidechain_evaluator.cpp +++ b/libraries/chain/sidechain_evaluator.cpp @@ -11,7 +11,7 @@ void_result bitcoin_issue_evaluator::do_evaluate( const bitcoin_issue_operation& 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& 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() ); @@ -82,7 +82,7 @@ void bitcoin_issue_evaluator::add_issue( const bitcoin_transaction_object& btc_o 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& 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 ) { @@ -103,31 +103,31 @@ void bitcoin_issue_evaluator::clear_btc_transaction_information( const bitcoin_t d.remove( btc_obj ); } -std::vector bitcoin_issue_evaluator::get_amounts_to_issue( std::vector vins_id ) +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(); + const auto& vins_info_idx = d.get_index_type().indices().get(); std::vector result; - for( auto& id : vins_id ) { - auto vin_itr = vins_info_idx.find( id ); + 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_id ) +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(); + const auto& vins_info_idx = d.get_index_type().indices().get(); std::vector result; - for( auto& id : vins_id ) { - auto vin_itr = vins_info_idx.find( id ); + 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 ); diff --git a/libraries/sidechain/bitcoin_address.cpp b/libraries/sidechain/bitcoin_address.cpp index ce115172..f1fb848c 100644 --- a/libraries/sidechain/bitcoin_address.cpp +++ b/libraries/sidechain/bitcoin_address.cpp @@ -2,9 +2,31 @@ #include #include #include +#include namespace sidechain { +bytes bitcoin_address::get_script() const +{ + switch ( type ) { + case payment_type::NULLDATA: + return script_builder() << op::RETURN << raw_address; + case payment_type::P2PK: + return script_builder() << raw_address << op::CHECKSIG; + case payment_type::P2PKH: + return script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG; + case payment_type::P2SH: + case payment_type::P2SH_WPKH: + case payment_type::P2SH_WSH: + return script_builder() << op::HASH160 << raw_address << op::EQUAL; + case payment_type::P2WPKH: + case payment_type::P2WSH: + return script_builder() << op::_0 << raw_address; + default: + return raw_address; + } +} + payment_type bitcoin_address::determine_type() { if( is_p2pk() ) { @@ -22,7 +44,7 @@ payment_type bitcoin_address::determine_type() } } -bytes bitcoin_address::determine_raw_address( const payment_type& type ) +bytes bitcoin_address::determine_raw_address() { bytes result; switch( type ) { @@ -192,6 +214,14 @@ bool btc_multisig_segwit_address::operator==( const btc_multisig_segwit_address& return true; } +std::vector btc_multisig_segwit_address::get_keys() { + std::vector keys; + for( const auto& k : witnesses_keys ) { + keys.push_back( k.second ); + } + return keys; +} + void btc_multisig_segwit_address::create_witness_script() { const auto redeem_sha256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() ); diff --git a/libraries/sidechain/bitcoin_transaction.cpp b/libraries/sidechain/bitcoin_transaction.cpp index 40f59dee..e099cd3f 100644 --- a/libraries/sidechain/bitcoin_transaction.cpp +++ b/libraries/sidechain/bitcoin_transaction.cpp @@ -5,6 +5,37 @@ namespace sidechain { +bool out_point::operator==( const out_point& op ) const +{ + if( this->hash == op.hash && + this->n == op.n ) + { + return true; + } + return false; +} + +bool tx_in::operator==( const tx_in& ti ) const +{ + if( this->prevout == ti.prevout && + this->scriptSig == ti.scriptSig && + this->nSequence == ti.nSequence ) + { + return true; + } + return false; +} + +bool tx_out::operator==( const tx_out& to ) const +{ + if( this->value == to.value && + this->scriptPubKey == to.scriptPubKey ) + { + return true; + } + return false; +} + bool tx_out::is_p2wsh() const { if( scriptPubKey.size() == 34 && scriptPubKey[0] == static_cast(0x00) && scriptPubKey[1] == static_cast(0x20) ) { @@ -61,6 +92,18 @@ bytes tx_out::get_data_or_script() const return scriptPubKey; } +bool bitcoin_transaction::operator!=( const bitcoin_transaction& bt ) const +{ + if( this->nVersion != bt.nVersion || + this->vin != bt.vin || + this->vout != bt.vout || + this->nLockTime != bt.nLockTime ) + { + return true; + } + return false; +} + fc::sha256 bitcoin_transaction::get_hash() const { const auto bytes = pack( *this, true) ; diff --git a/libraries/sidechain/include/sidechain/bitcoin_address.hpp b/libraries/sidechain/include/sidechain/bitcoin_address.hpp index d3216544..47db31c1 100644 --- a/libraries/sidechain/include/sidechain/bitcoin_address.hpp +++ b/libraries/sidechain/include/sidechain/bitcoin_address.hpp @@ -17,7 +17,7 @@ public: bitcoin_address() = default; bitcoin_address( const std::string& addr ) : address( addr ), type( determine_type() ), - raw_address( determine_raw_address( type ) ) {} + raw_address( determine_raw_address() ) {} payment_type get_type() const { return type; } @@ -25,13 +25,15 @@ public: 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( const payment_type& type ); + bytes determine_raw_address(); bool check_segwit_address( const size_segwit_address& size ) const; @@ -103,6 +105,8 @@ public: bytes get_witness_script() const { return witness_script; } + std::vector get_keys(); + private: void create_witness_script(); diff --git a/libraries/sidechain/include/sidechain/bitcoin_transaction.hpp b/libraries/sidechain/include/sidechain/bitcoin_transaction.hpp index 467dd116..6271398a 100644 --- a/libraries/sidechain/include/sidechain/bitcoin_transaction.hpp +++ b/libraries/sidechain/include/sidechain/bitcoin_transaction.hpp @@ -8,9 +8,13 @@ struct out_point { 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; @@ -21,6 +25,8 @@ struct tx_out { int64_t value = 0; bytes scriptPubKey; + bool operator==( const tx_out& to ) const; + bool is_p2wsh() const; bool is_p2wpkh() const; @@ -35,14 +41,17 @@ struct tx_out { }; struct bitcoin_transaction { - 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; + 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 diff --git a/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp b/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp index 6a62fa60..bf6aabb0 100644 --- a/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp +++ b/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp @@ -22,10 +22,9 @@ struct info_for_vin { info_for_vin() = default; - info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes() ) : - id( count_id_info_for_vin++ ), out( _out ), address( _address ), script( _script ) { - identifier = fc::sha256::hash( out.hash_tx + std::to_string( out.n_vout ) ); - } + info_for_vin( const prev_out& _out, const std::string& _address, bytes _script = bytes() ); + + bool operator!=( const info_for_vin& obj ) const; struct comparer { bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const; diff --git a/libraries/sidechain/include/sidechain/sidechain_condensing_tx.hpp b/libraries/sidechain/include/sidechain/sidechain_condensing_tx.hpp index 305aaba0..5ce0fac2 100644 --- a/libraries/sidechain/include/sidechain/sidechain_condensing_tx.hpp +++ b/libraries/sidechain/include/sidechain/sidechain_condensing_tx.hpp @@ -19,7 +19,7 @@ public: void create_vouts_for_witness_fee( const accounts_keys& witness_active_keys, bool front = true ); - uint64_t get_estimate_tx_size( size_t number_witness ) const; + 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 ); diff --git a/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp b/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp new file mode 100644 index 00000000..64fac4f7 --- /dev/null +++ b/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp @@ -0,0 +1,45 @@ +#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 ); + +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/sidechain/include/sidechain/types.hpp b/libraries/sidechain/include/sidechain/types.hpp index 0364d049..63317798 100644 --- a/libraries/sidechain/include/sidechain/types.hpp +++ b/libraries/sidechain/include/sidechain/types.hpp @@ -37,6 +37,17 @@ enum class sidechain_proposal_type struct prev_out { + bool operator!=( const prev_out& obj ) const + { + if( this->hash_tx != obj.hash_tx || + this->n_vout != obj.n_vout || + this->amount != obj.amount ) + { + return true; + } + return false; + } + std::string hash_tx; uint32_t n_vout; uint64_t amount; diff --git a/libraries/sidechain/input_withdrawal_info.cpp b/libraries/sidechain/input_withdrawal_info.cpp index e2fc55e7..7eebf539 100644 --- a/libraries/sidechain/input_withdrawal_info.cpp +++ b/libraries/sidechain/input_withdrawal_info.cpp @@ -9,6 +9,26 @@ 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 ) : + id( count_id_info_for_vin++ ), out( _out ), address( _address ), script( _script ) +{ + 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 ) { @@ -92,8 +112,6 @@ fc::optional input_withdrawal_info::find_info_for_vin( fc::sha256 std::vector input_withdrawal_info::get_info_for_vins() { std::vector result; - - const auto& addr_idx = db.get_index_type().indices().get(); 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, @@ -106,9 +124,7 @@ std::vector input_withdrawal_info::get_info_for_vins() vin.out.n_vout = itr_b->out.n_vout; vin.out.amount = itr_b->out.amount; vin.address = itr_b->address; - const auto& address_itr = addr_idx.find( itr_b->address ); - FC_ASSERT( address_itr != addr_idx.end() ); - vin.script = address_itr->address.get_witness_script(); + vin.script = itr_b->script; result.push_back( vin ); ++itr_b; diff --git a/libraries/sidechain/network/sidechain_net_manager.cpp b/libraries/sidechain/network/sidechain_net_manager.cpp index 93ea3d9e..73c6aa0d 100644 --- a/libraries/sidechain/network/sidechain_net_manager.cpp +++ b/libraries/sidechain/network/sidechain_net_manager.cpp @@ -49,8 +49,11 @@ void sidechain_net_manager::update_tx_infos( const std::string& block_hash ) std::string block = bitcoin_client->receive_full_block( block_hash ); if( block != "" ) { const auto& vins = extract_info_from_block( block ); + const auto& addr_idx = db->get_index_type().indices().get(); for( const auto& v : vins ) { - db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address ); + const auto& addr_itr = addr_idx.find( v.address ); + FC_ASSERT( addr_itr != addr_idx.end() ); + db->i_w_info.insert_info_for_vin( prev_out{ v.out.hash_tx, v.out.n_vout, v.out.amount }, v.address, addr_itr->address.get_witness_script() ); } } } diff --git a/libraries/sidechain/sidechain_condensing_tx.cpp b/libraries/sidechain/sidechain_condensing_tx.cpp index 41afe321..cb742727 100644 --- a/libraries/sidechain/sidechain_condensing_tx.cpp +++ b/libraries/sidechain/sidechain_condensing_tx.cpp @@ -50,7 +50,7 @@ void sidechain_condensing_tx::create_vouts_for_witness_fee( const accounts_keys& } } -uint64_t sidechain_condensing_tx::get_estimate_tx_size( size_t number_witness ) const +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); @@ -60,12 +60,11 @@ uint64_t sidechain_condensing_tx::get_estimate_tx_size( size_t number_witness ) } std::vector temp_scriptWitness = { {},{temp_sig},{temp_sig},{temp_sig},{temp_sig},{temp_sig},{temp_script} }; - bitcoin_transaction temp_tx = get_transaction(); - for( auto& vin : temp_tx.vin ) { + for( auto& vin : tx.vin ) { vin.scriptWitness = temp_scriptWitness; } - return temp_tx.get_vsize(); + return tx.get_vsize(); } void sidechain_condensing_tx::subtract_fee( const uint64_t& fee, const uint16_t& witnesses_percentage ) diff --git a/libraries/sidechain/sidechain_proposal_checker.cpp b/libraries/sidechain/sidechain_proposal_checker.cpp new file mode 100644 index 00000000..1647856c --- /dev/null +++ b/libraries/sidechain/sidechain_proposal_checker.cpp @@ -0,0 +1,144 @@ +#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; + + 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(); + const auto& bto_itr_idx = db.get_index_type().indices().get(); + + for( const auto& id : btc_issue_op.transaction_ids ) { + const auto& bto_itr = bto_itr_idx.find( 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 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; +} + +} diff --git a/tests/tests/bitcoin_issue_tests.cpp b/tests/tests/bitcoin_issue_tests.cpp index e84e6ab8..c88d5c5f 100644 --- a/tests/tests/bitcoin_issue_tests.cpp +++ b/tests/tests/bitcoin_issue_tests.cpp @@ -15,7 +15,7 @@ BOOST_FIXTURE_TEST_SUITE( bitcoin_issue_tests, database_fixture ) void create_bitcoin_issue_operation_environment( database& db ) { - std::vector< info_for_used_vin_id_type > vins; + std::vector< fc::sha256 > vins; std::vector< info_for_vout_id_type > vouts; for( auto i = 0; i < 3; i++ ){ @@ -25,10 +25,10 @@ void create_bitcoin_issue_operation_environment( database& db ) }); auto vin_id = db.create([&]( info_for_used_vin_object& obj ) { - obj.identifier = fc::sha256( std::to_string( i ) ); + obj.identifier = fc::sha256( std::string( 64, std::to_string( i )[0] ) ); obj.out.amount = 100000 + i * 100000; obj.address = std::to_string( i ); - }).get_id(); + }); auto vout_id = db.create([&]( info_for_vout_object& obj ) { obj.payer = account_id_type( i ); @@ -36,7 +36,7 @@ void create_bitcoin_issue_operation_environment( database& db ) obj.address = std::to_string( i ); }).get_id(); - vins.push_back( vin_id ); + vins.push_back( vin_id.identifier ); vouts.push_back( vout_id ); } diff --git a/tests/tests/sidechain_proposal_checker_tests.cpp b/tests/tests/sidechain_proposal_checker_tests.cpp new file mode 100644 index 00000000..99c27d46 --- /dev/null +++ b/tests/tests/sidechain_proposal_checker_tests.cpp @@ -0,0 +1,140 @@ +#include +#include "../common/database_fixture.hpp" +#include + +using namespace sidechain; + +BOOST_FIXTURE_TEST_SUITE( sidechain_proposal_checker_tests, database_fixture ) + +class test_environment +{ + +public: + + test_environment( database& db ) + { + + for( size_t i = 1; i < 6; i++ ) { + prev_out out{ std::string( 64, std::to_string( i )[0] ), static_cast( i ), static_cast( i * 10000 ) }; + db.i_w_info.insert_info_for_vin( out, std::to_string( i ), { 0x00, 0x01, 0x02 } ); + db.i_w_info.insert_info_for_vout( account_id_type( i ), "2Mt57VSFqBe7UpDad9QaYHev21E1VscAZMU", i * 5000 ); + } + + vins = db.i_w_info.get_info_for_vins(); + pw_vin = *db.i_w_info.get_info_for_pw_vin(); + vouts = db.i_w_info.get_info_for_vouts(); + full_tx = db.create_btc_transaction( vins, vouts, pw_vin ); + } + + std::vector vins; + info_for_vin pw_vin; + std::vector vouts; + full_btc_transaction full_tx; + +}; + +BOOST_AUTO_TEST_CASE( check_reuse_normal_test ) +{ + sidechain_proposal_checker checker( db ); + bitcoin_transaction_send_operation op; + + op.pw_vin = info_for_vin( { std::string( 64, '1' ), 0, 0 }, "", bytes() ); + for( size_t i = 0; i < 5; i++ ) { + op.vins.push_back( info_for_vin( { std::string( 64, std::to_string( i )[0] ), 0, 0 }, "", bytes() ) ); + op.vouts.push_back( info_for_vout_id_type( i ) ); + } + + BOOST_CHECK( checker.check_reuse( op ) ); +} + +BOOST_AUTO_TEST_CASE( check_reuse_not_normal_test ) +{ + sidechain_proposal_checker checker( db ); + bitcoin_transaction_send_operation op; + + op.pw_vin = info_for_vin( { std::string( 64, '1' ), 0, 0 }, "", bytes() ); + for( size_t i = 0; i < 5; i++ ) { + op.vins.push_back( info_for_vin( { std::string( 64, std::to_string( i )[0] ), 0, 0 }, "", bytes() ) ); + op.vouts.push_back( info_for_vout_id_type( i ) ); + } + + BOOST_CHECK( checker.check_reuse( op ) ); + BOOST_CHECK( !checker.check_reuse( op ) ); +} + +BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_normal_test ) +{ + sidechain_proposal_checker checker( db ); + test_environment env( db ); + + bitcoin_transaction_send_operation op; + op.pw_vin = env.pw_vin; + op.vins = env.vins; + for( const auto& vout : env.vouts ) { + op.vouts.push_back( vout.get_id() ); + } + op.transaction = env.full_tx.first; + op.fee_for_size = env.full_tx.second; + + BOOST_CHECK( checker.check_bitcoin_transaction_send_operation( op ) ); +} + +BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_info_for_vin_test ) +{ + sidechain_proposal_checker checker( db ); + test_environment env( db ); + + env.vins[3].out.amount = 13; + + bitcoin_transaction_send_operation op; + op.pw_vin = env.pw_vin; + op.vins = env.vins; + for( const auto& vout : env.vouts ) { + op.vouts.push_back( vout.get_id() ); + } + op.transaction = env.full_tx.first; + op.fee_for_size = env.full_tx.second; + + BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) ); +} + +BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_info_for_pw_vin_test ) +{ + sidechain_proposal_checker checker( db ); + test_environment env( db ); + + env.pw_vin.out.amount = 13; + + bitcoin_transaction_send_operation op; + op.pw_vin = env.pw_vin; + op.vins = env.vins; + for( const auto& vout : env.vouts ) { + op.vouts.push_back( vout.get_id() ); + } + op.transaction = env.full_tx.first; + op.fee_for_size = env.full_tx.second; + + BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) ); +} + +BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_tx_test ) +{ + sidechain_proposal_checker checker( db ); + test_environment env( db ); + + bitcoin_transaction_send_operation op; + op.pw_vin = env.pw_vin; + op.vins = env.vins; + for( const auto& vout : env.vouts ) { + op.vouts.push_back( vout.get_id() ); + } + + env.full_tx.first.vout[0].value = 13; + + op.transaction = env.full_tx.first; + op.fee_for_size = env.full_tx.second; + + BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) ); +} + +BOOST_AUTO_TEST_SUITE_END()