From 9a5c9092e10fff5ea9fbd7735a54a937b081d350 Mon Sep 17 00:00:00 2001 From: Anton Shkinder Date: Thu, 7 Feb 2019 17:48:28 +0300 Subject: [PATCH] Added approval proposal checker --- .../chain/bitcoin_transaction_evaluator.cpp | 7 +- libraries/chain/db_sidechain.cpp | 2 +- .../chain/include/graphene/chain/database.hpp | 1 + .../sidechain/sidechain_proposal_checker.hpp | 2 + .../sidechain/sidechain_proposal_checker.cpp | 18 +++ .../sidechain_proposal_checker_tests.cpp | 107 ++++++++++++++++++ 6 files changed, 133 insertions(+), 4 deletions(-) diff --git a/libraries/chain/bitcoin_transaction_evaluator.cpp b/libraries/chain/bitcoin_transaction_evaluator.cpp index 43613115..b8174b44 100644 --- a/libraries/chain/bitcoin_transaction_evaluator.cpp +++ b/libraries/chain/bitcoin_transaction_evaluator.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -127,10 +128,10 @@ void_result bitcoin_transaction_sign_evaluator::do_evaluate( const bitcoin_trans vins.insert( vins.begin(), btc_send_op.pw_vin ); } - FC_ASSERT( check_sigs( public_key, op.signatures, vins, btc_send_op.transaction ) ); // Add pw_vin + FC_ASSERT( check_sigs( public_key, op.signatures, vins, btc_send_op.transaction ) ); - // const auto& proposal = sidechain_proposal_itr->proposal_id( d ); - // FC_ASSERT( d.check_witness( witness_obj, *btc_tx, proposal ), "Can't sign this 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(); } diff --git a/libraries/chain/db_sidechain.cpp b/libraries/chain/db_sidechain.cpp index b1c35d9f..46014115 100644 --- a/libraries/chain/db_sidechain.cpp +++ b/libraries/chain/db_sidechain.cpp @@ -152,7 +152,7 @@ void database::processing_sidechain_proposals( const witness_object& current_wit } 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 ) ) + if( checker.check_bitcoin_transaction_send_operation( op ) && 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 ) ); diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 8d8ecf2e..e5d15cd5 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -545,6 +545,7 @@ namespace graphene { namespace chain { fc::optional create_bitcoin_issue_proposals( const witness_object& current_witness ); + fc::signal send_btc_tx; sidechain::input_withdrawal_info i_w_info; diff --git a/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp b/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp index 64fac4f7..53c26e2d 100644 --- a/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp +++ b/libraries/sidechain/include/sidechain/sidechain_proposal_checker.hpp @@ -17,6 +17,8 @@ public: 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 ); private: diff --git a/libraries/sidechain/sidechain_proposal_checker.cpp b/libraries/sidechain/sidechain_proposal_checker.cpp index 1647856c..d3615138 100644 --- a/libraries/sidechain/sidechain_proposal_checker.cpp +++ b/libraries/sidechain/sidechain_proposal_checker.cpp @@ -141,4 +141,22 @@ bool sidechain_proposal_checker::check_transaction( const bitcoin_transaction_se 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(); +} + } diff --git a/tests/tests/sidechain_proposal_checker_tests.cpp b/tests/tests/sidechain_proposal_checker_tests.cpp index 99c27d46..efc3432e 100644 --- a/tests/tests/sidechain_proposal_checker_tests.cpp +++ b/tests/tests/sidechain_proposal_checker_tests.cpp @@ -2,6 +2,10 @@ #include "../common/database_fixture.hpp" #include +#include +#include +#include + using namespace sidechain; BOOST_FIXTURE_TEST_SUITE( sidechain_proposal_checker_tests, database_fixture ) @@ -137,4 +141,107 @@ BOOST_AUTO_TEST_CASE( check_btc_tx_send_op_incorrect_tx_test ) BOOST_CHECK( !checker.check_bitcoin_transaction_send_operation( op ) ); } +BOOST_AUTO_TEST_CASE( twice_approve_btc_tx_send_test ) +{ + sidechain_proposal_checker checker( db ); + const flat_set& active_witnesses = db.get_global_properties().active_witnesses; + const auto& proposal_idx = db.get_index_type().indices().get< graphene::chain::by_id >(); + transaction_evaluation_state context(&db); + + auto propose = db.create( [&]( proposal_object& obj ){ + obj.expiration_time = db.head_block_time() + fc::days(1); + obj.review_period_time = db.head_block_time() + fc::days(1); + } ); + + const witness_id_type& witness_id = *active_witnesses.begin(); + const witness_object witness = witness_id(db); + const account_object& witness_account = witness.witness_account(db); + + proposal_update_operation op; + { + op.proposal = propose.id; + op.fee_paying_account = witness_account.id; + op.active_approvals_to_add.insert( witness_account.id ); + } + + BOOST_CHECK( proposal_idx.size() == 1 ); + BOOST_CHECK_EQUAL( proposal_idx.begin()->available_active_approvals.size(), 0 ); + BOOST_CHECK( checker.check_witness_opportunity_to_approve( witness, *proposal_idx.begin() ) ); + + db.apply_operation( context, op ); + + BOOST_CHECK( proposal_idx.size() == 1 ); + BOOST_CHECK_EQUAL( proposal_idx.begin()->available_active_approvals.size(), 1 ); + BOOST_CHECK( !checker.check_witness_opportunity_to_approve( witness, *proposal_idx.begin() ) ); +} + +BOOST_AUTO_TEST_CASE( not_active_wit_try_to_approve_btc_send_test ) +{ + ACTOR(nathan); + upgrade_to_lifetime_member( nathan_id ); + trx.clear(); + witness_id_type nathan_witness_id = create_witness( nathan_id, nathan_private_key ).id; + + const auto& witnesses = db.get_global_properties().active_witnesses; + BOOST_CHECK( std::find(witnesses.begin(), witnesses.end(), nathan_witness_id) == witnesses.end() ); + + const auto& witness_idx = db.get_index_type().indices().get(); + auto itr = witness_idx.find( nathan_witness_id ); + BOOST_CHECK( itr != witness_idx.end() ); + + sidechain_proposal_checker checker( db ); + const auto& proposal_idx = db.get_index_type().indices().get< graphene::chain::by_id >(); + auto propose = db.create( [&]( proposal_object& obj ){ + obj.expiration_time = db.head_block_time() + fc::days(1); + obj.review_period_time = db.head_block_time() + fc::days(1); + } ); + + BOOST_CHECK( !checker.check_witness_opportunity_to_approve( *itr, *proposal_idx.begin() ) ); +} + +BOOST_AUTO_TEST_CASE( not_account_auths_wit_try_to_approve_btc_send_test ) +{ + ACTOR(nathan); + upgrade_to_lifetime_member(nathan_id); + trx.clear(); + witness_id_type nathan_witness_id = create_witness(nathan_id, nathan_private_key).id; + // Give nathan some voting stake + transfer(committee_account, nathan_id, asset(10000000)); + generate_block(); + test::set_expiration( db, trx ); + + { + account_update_operation op; + op.account = nathan_id; + op.new_options = nathan_id(db).options; + op.new_options->votes.insert(nathan_witness_id(db).vote_id); + op.new_options->num_witness = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(), + [](vote_id_type id) { return id.type() == vote_id_type::witness; }); + op.new_options->num_committee = std::count_if(op.new_options->votes.begin(), op.new_options->votes.end(), + [](vote_id_type id) { return id.type() == vote_id_type::committee; }); + trx.operations.push_back(op); + sign( trx, nathan_private_key ); + PUSH_TX( db, trx ); + trx.clear(); + } + + generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); + const auto& witnesses = db.get_global_properties().active_witnesses; + auto itr_ = std::find(witnesses.begin(), witnesses.end(), nathan_witness_id); + BOOST_CHECK(itr_ != witnesses.end()); + + const auto& witness_idx = db.get_index_type().indices().get(); + auto itr = witness_idx.find( nathan_witness_id ); + BOOST_CHECK( itr != witness_idx.end() ); + + sidechain_proposal_checker checker( db ); + const auto& proposal_idx = db.get_index_type().indices().get< graphene::chain::by_id >(); + auto propose = db.create( [&]( proposal_object& obj ){ + obj.expiration_time = db.head_block_time() + fc::days(1); + obj.review_period_time = db.head_block_time() + fc::days(1); + } ); + + BOOST_CHECK( !checker.check_witness_opportunity_to_approve( *itr, *proposal_idx.begin() ) ); +} + BOOST_AUTO_TEST_SUITE_END()