diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index c997eafc..d7ea2180 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -333,6 +333,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 ) { diff --git a/libraries/chain/db_sidechain.cpp b/libraries/chain/db_sidechain.cpp index cb50791f..4ce17bef 100644 --- a/libraries/chain/db_sidechain.cpp +++ b/libraries/chain/db_sidechain.cpp @@ -146,7 +146,7 @@ full_btc_transaction database::create_btc_transaction( const std::vectoridentifier != SIDECHAIN_NULL_HASH ) { + if( info_pw_vin->identifier.str().compare( 0, 24, SIDECHAIN_NULL_VIN_IDENTIFIER ) == 0 ) { ctx.create_pw_vin( *info_pw_vin ); } @@ -177,7 +177,7 @@ fc::optional database::create_send_btc_tx_proposal( const witness_obj bitcoin_transaction_send_operation btc_send_op; btc_send_op.payer = get_sidechain_account_id(); - btc_send_op.pw_vin = info_pw_vin->identifier != SIDECHAIN_NULL_HASH ? info_pw_vin->identifier : fc::optional< fc::sha256 >(); + btc_send_op.pw_vin = info_pw_vin->identifier; btc_send_op.vins = info_vins; for( auto& out : info_vouts ) { btc_send_op.vouts.push_back( out.get_id() ); @@ -209,4 +209,47 @@ signed_transaction database::create_signed_transaction( const private_key& signi return processed_trx; } +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 ) + { + 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 ).valid() ) { + pw_vout_manager.mark_as_unused_vout( op.pw_vin ); + } + + 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 ); + } +} + } } \ No newline at end of file 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/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 36c29415..7affde31 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -237,5 +237,5 @@ #define SIDECHAIN_DEFAULT_CONDENSING_TX_VINS_NUMBER 5 #define SIDECHAIN_DEFAULT_CONDENSING_TX_VOUTS_NUMBER 5 #define SIDECHAIN_DEFAULT_PERCENTAGE_PAYMENT_TO_WIT 0.001 -#define SIDECHAIN_NULL_HASH fc::sha256( "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9" ) // fc::sha256::hash( "" + std::to_string( 0 ) ); +#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 6b6d42de..da819fee 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -534,6 +534,10 @@ namespace graphene { namespace chain { fc::optional create_send_btc_tx_proposal( const witness_object& current_witness ); 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::signal send_btc_tx; diff --git a/libraries/chain/include/graphene/chain/info_for_vout_object.hpp b/libraries/chain/include/graphene/chain/info_for_vout_object.hpp index 5c49ad29..7521c444 100644 --- a/libraries/chain/include/graphene/chain/info_for_vout_object.hpp +++ b/libraries/chain/include/graphene/chain/info_for_vout_object.hpp @@ -15,8 +15,8 @@ class info_for_vout_object : public abstract_object struct comparer { bool operator()( const info_for_vout_object& lhs, const info_for_vout_object& rhs ) const { - if( lhs.created != rhs.created ) - return lhs.created < rhs.created; + if( lhs.used != rhs.used ) + return lhs.used < rhs.used; return lhs.id < rhs.id; } }; @@ -27,23 +27,23 @@ class info_for_vout_object : public abstract_object sidechain::bitcoin_address address; uint64_t amount; - bool created = false; + bool used = false; }; struct by_created; -struct by_id_and_not_created; +struct by_id_and_not_used; typedef boost::multi_index_container< info_for_vout_object, indexed_by< ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag< by_created >, member< info_for_vout_object, bool, &info_for_vout_object::created > >, - ordered_non_unique, identity< info_for_vout_object >, info_for_vout_object::comparer > + ordered_non_unique< tag< by_created >, member< info_for_vout_object, bool, &info_for_vout_object::used > >, + ordered_non_unique, identity< info_for_vout_object >, info_for_vout_object::comparer > > > info_for_vout_multi_index_container; typedef generic_index info_for_vout_index; } } // graphene::chain -FC_REFLECT_DERIVED( graphene::chain::info_for_vout_object, (graphene::chain::object), (payer)(address)(amount)(created) ) +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 index 1830a3d6..7d9068b0 100644 --- a/libraries/chain/include/graphene/chain/primary_wallet_vout_object.hpp +++ b/libraries/chain/include/graphene/chain/primary_wallet_vout_object.hpp @@ -19,7 +19,7 @@ public: primary_wallet_vout_id_type get_id() const { return id; } sidechain::prev_out vout; - fc::uint256 hash_id; // sha256(hash + n_vout) + fc::sha256 hash_id; // ( sha256(hash + n_vout) - 8 bytes ) + id_obj bool confirmed; bool used; diff --git a/libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp b/libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp index 61129c73..a4c4b3b1 100644 --- a/libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/bitcoin_transaction.hpp @@ -15,7 +15,7 @@ namespace graphene { namespace chain { asset fee; account_id_type payer; - fc::optional< fc::sha256 > pw_vin; + fc::sha256 pw_vin; std::vector< sidechain::info_for_vin > vins; std::vector< info_for_vout_id_type > vouts; diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 6314f6ae..c78bc9dd 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -162,8 +162,7 @@ void sidechain_hardfork_visitor::operator()( const bitcoin_transaction_send_oper db.i_w_info.mark_as_used_vout( *obj ); } - fc::sha256 hashid = v.pw_vin.valid() ? *v.pw_vin : SIDECHAIN_NULL_HASH; - db.pw_vout_manager.use_latest_vout( hashid ); + db.pw_vout_manager.mark_as_used_vout( v.pw_vin ); } void_result proposal_create_evaluator::do_evaluate(const proposal_create_operation& o) diff --git a/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp b/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp index 461122b0..f9565a99 100644 --- a/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp +++ b/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp @@ -40,18 +40,18 @@ struct info_for_vin std::string address; bytes script; - bool created = false; + bool used = false; }; struct by_id; struct by_identifier; -struct by_id_and_not_created; +struct by_id_and_not_used; using info_for_vin_index = boost::multi_index_container, member>, ordered_unique, member>, - ordered_non_unique, identity< info_for_vin >, info_for_vin::comparer > + ordered_non_unique, identity< info_for_vin >, info_for_vin::comparer > > >; @@ -69,6 +69,8 @@ public: void mark_as_used_vin( const info_for_vin& obj ); + void mark_as_unused_vin( const info_for_vin& obj ); + void remove_info_for_vin( const info_for_vin& obj ); fc::optional find_info_for_vin( fc::sha256 identifier ); @@ -82,6 +84,8 @@ public: void mark_as_used_vout( const graphene::chain::info_for_vout_object& obj ); + void mark_as_unused_vout( const graphene::chain::info_for_vout_object& obj ); + void remove_info_for_vout( const info_for_vout& obj ); fc::optional find_info_for_vout( graphene::chain::info_for_vout_id_type id ); @@ -100,4 +104,4 @@ private: } -FC_REFLECT( sidechain::info_for_vin, (identifier)(out)(address)(script)(created) ) \ No newline at end of file +FC_REFLECT( sidechain::info_for_vin, (identifier)(out)(address)(script)(used) ) \ No newline at end of file diff --git a/libraries/sidechain/include/sidechain/primary_wallet_vout_manager.hpp b/libraries/sidechain/include/sidechain/primary_wallet_vout_manager.hpp index 96504ed8..76151275 100644 --- a/libraries/sidechain/include/sidechain/primary_wallet_vout_manager.hpp +++ b/libraries/sidechain/include/sidechain/primary_wallet_vout_manager.hpp @@ -29,10 +29,14 @@ public: void confirm_vout( fc::sha256 hash_id ); - void use_latest_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/sidechain/input_withdrawal_info.cpp b/libraries/sidechain/input_withdrawal_info.cpp index 40657f52..3cc45398 100644 --- a/libraries/sidechain/input_withdrawal_info.cpp +++ b/libraries/sidechain/input_withdrawal_info.cpp @@ -9,8 +9,8 @@ uint64_t info_for_vin::count_id_info_for_vin = 0; bool info_for_vin::comparer::operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const { - if( lhs.created != rhs.created ) { - return lhs.created < rhs.created; + if( lhs.used != rhs.used ) { + return lhs.used < rhs.used; } return lhs.id < rhs.id; } @@ -46,7 +46,14 @@ void input_withdrawal_info::modify_info_for_vin( const info_for_vin& obj, const void input_withdrawal_info::mark_as_used_vin( const info_for_vin& obj ) { info_for_vins.modify( obj.identifier, [&]( info_for_vin& o ) { - o.created = true; + 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; }); } @@ -66,12 +73,11 @@ std::vector input_withdrawal_info::get_info_for_vins() const auto& addr_idx = db.get_index_type().indices().get(); - info_for_vins.safe_for( [&]( info_for_vin_index::index::type::iterator itr_b, - info_for_vin_index::index::type::iterator itr_e ) + 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 < 5 && !itr_b->created; i++ ) { // 5 amount vins to bitcoin transaction + for( size_t i = 0; itr_b != itr_e && i < 5 && !itr_b->used; i++ ) { // 5 amount vins to bitcoin transaction info_for_vin vin; - vin.id = itr_b->id; vin.identifier = itr_b->identifier; vin.out.hash_tx = itr_b->out.hash_tx; vin.out.n_vout = itr_b->out.n_vout; @@ -104,7 +110,17 @@ void input_withdrawal_info::mark_as_used_vout( const graphene::chain::info_for_v auto itr = info_for_vout_idx.find( obj.id ); db.modify( *itr, [&]( graphene::chain::info_for_vout_object& o ) { - o.created = true; + 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; }); } @@ -134,9 +150,9 @@ 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_created >(); + const auto& info_for_vout_idx = db.get_index_type().indices().get< graphene::chain::by_id_and_not_used >(); auto itr = info_for_vout_idx.begin(); - for(size_t i = 0; i < 5 && itr != info_for_vout_idx.end() && !itr->created; i++) { + for(size_t i = 0; i < 5 && itr != info_for_vout_idx.end() && !itr->used; i++) { info_for_vout vout; vout.payer = itr->payer; vout.address = itr->address; diff --git a/libraries/sidechain/primary_wallet_vout_manager.cpp b/libraries/sidechain/primary_wallet_vout_manager.cpp index 788a92a6..35524ffc 100644 --- a/libraries/sidechain/primary_wallet_vout_manager.cpp +++ b/libraries/sidechain/primary_wallet_vout_manager.cpp @@ -35,9 +35,10 @@ fc::optional< primary_wallet_vout_object > primary_wallet_vout_manager::get_vout 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 = fc::sha256::hash( out.hash_tx + std::to_string( out.n_vout ) ); + obj.hash_id = create_hash_id( out.hash_tx, out.n_vout, next_pw_id ); obj.confirmed = false; obj.used = false; }); @@ -79,7 +80,7 @@ void primary_wallet_vout_manager::confirm_vout( fc::sha256 hash_id ) } } -void primary_wallet_vout_manager::use_latest_vout( fc::sha256 hash_id ) +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 ); @@ -97,6 +98,19 @@ void primary_wallet_vout_manager::use_latest_vout( fc::sha256 hash_id ) } } +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 >(); @@ -106,4 +120,16 @@ fc::optional< graphene::chain::primary_wallet_vout_id_type > primary_wallet_vout 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/tests/tests/bitcoin_address_obj_tests.cpp b/tests/tests/bitcoin_address_obj_tests.cpp index aafb3039..47aadbb8 100644 --- a/tests/tests/bitcoin_address_obj_tests.cpp +++ b/tests/tests/bitcoin_address_obj_tests.cpp @@ -19,12 +19,12 @@ BOOST_AUTO_TEST_CASE( create_bitcoin_address_test ) const auto& idx = db.get_index_type().indices().get< by_id >(); - BOOST_CHECK( idx.size() == 0 ); + BOOST_CHECK( idx.size() == 1 ); db.apply_operation( context, op ); auto btc_address = idx.begin(); - BOOST_CHECK(btc_address->count_invalid_pub_key == 1); + BOOST_CHECK( btc_address->count_invalid_pub_key == 1 ); } BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/tests/input_withdrawal_info_tests.cpp b/tests/tests/input_withdrawal_info_tests.cpp index 2f07bb16..05d43a85 100644 --- a/tests/tests/input_withdrawal_info_tests.cpp +++ b/tests/tests/input_withdrawal_info_tests.cpp @@ -2,6 +2,7 @@ #include #include "../common/database_fixture.hpp" #include +#include using namespace graphene::chain; using namespace sidechain; @@ -138,9 +139,18 @@ BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test ) BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test ) { input_withdrawal_info infos( db ); + + const auto pw_obj = db.get_latest_PW(); + auto witnesses_keys = pw_obj.address.witnesses_keys; + for( size_t i = 0; i < 10; i++ ) { + const auto& address = db.create( [&]( bitcoin_address_object& a ) { + const fc::ecc::private_key petra_private_key = generate_private_key( std::to_string( i ) ); + witnesses_keys.begin()->second = public_key_type( petra_private_key.get_public_key() ); + a.address = sidechain::btc_multisig_segwit_address( 5, witnesses_keys); + }); prev_out out = { std::to_string( i ), static_cast( i ), static_cast< uint64_t >( i ) }; - infos.insert_info_for_vin( out, "addr" + std::to_string( i ), { 0x01, 0x02, 0x03 } ); + infos.insert_info_for_vin( out, address.get_address(), { 0x01, 0x02, 0x03 } ); } const auto& vins = infos.get_info_for_vins(); diff --git a/tests/tests/primary_wallet_vout_manager_tests.cpp b/tests/tests/primary_wallet_vout_manager_tests.cpp index 27d4d29f..a6f2229e 100644 --- a/tests/tests/primary_wallet_vout_manager_tests.cpp +++ b/tests/tests/primary_wallet_vout_manager_tests.cpp @@ -127,14 +127,14 @@ BOOST_AUTO_TEST_CASE( use_pw_vout_objects ) primary_wallet_vout_manager pw_vout_manager( db ); create_primary_wallet_vouts( pw_vout_manager, db, 1 ); - pw_vout_manager.use_latest_vout( fc::sha256::hash( "0" + std::to_string( 0 ))); + pw_vout_manager.mark_as_used_vout( fc::sha256::hash( "0" + std::to_string( 0 ))); auto itr = idx.begin(); BOOST_CHECK( !pw_vout_manager.get_latest_unused_vout().valid() ); BOOST_CHECK( itr->used == true ); create_primary_wallet_vouts( pw_vout_manager, db, 1 ); - pw_vout_manager.use_latest_vout( fc::sha256::hash( "1" + std::to_string( 1 ))); + pw_vout_manager.mark_as_used_vout( fc::sha256::hash( "1" + std::to_string( 1 ))); itr++; BOOST_CHECK( !pw_vout_manager.get_latest_unused_vout().valid() ); @@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE( use_pw_vout_objects ) create_primary_wallet_vouts( pw_vout_manager, db, 2 ); - GRAPHENE_REQUIRE_THROW( pw_vout_manager.use_latest_vout( fc::sha256::hash( "3" + std::to_string( 3 ))), fc::exception ); + GRAPHENE_REQUIRE_THROW( pw_vout_manager.mark_as_used_vout( fc::sha256::hash( "3" + std::to_string( 3 ))), fc::exception ); } BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file