diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index be71012d..6196b4c9 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory( utilities ) add_subdirectory( app ) add_subdirectory( plugins ) add_subdirectory( wallet ) +add_subdirectory( sidechain ) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index a328cf1f..1131d697 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -117,7 +117,8 @@ 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 ) +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" ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index d58c68f7..cda5bd6e 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -85,6 +85,8 @@ #include +#include + namespace graphene { namespace chain { // C++ requires that static class variables declared and initialized @@ -301,6 +303,8 @@ void database::initialize_indexes() //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/include/graphene/chain/info_for_vout_object.hpp b/libraries/chain/include/graphene/chain/info_for_vout_object.hpp new file mode 100644 index 00000000..7bee1ca8 --- /dev/null +++ b/libraries/chain/include/graphene/chain/info_for_vout_object.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +namespace graphene { namespace chain { + +class info_for_vout_object : public abstract_object +{ + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = info_for_vout_object_type; + + struct comparer { + bool operator()( const info_for_vout_object& lhs, const info_for_vout_object& rhs ) const { + if( lhs.created != rhs.created ) + return lhs.created < rhs.created; + return lhs.id < rhs.id; + } + }; + + info_for_vout_id_type get_id()const { return id; } + + account_id_type payer; + // btc::payment_type addr_type; + std::string data; + uint64_t amount; + + bool created = false; +}; + +struct by_created; +struct by_id_and_not_created; + +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 > + > +> 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)(data)(amount)(created) ) + diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 4b6e1589..d32c7ef4 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -145,6 +145,7 @@ namespace graphene { namespace chain { betting_market_group_object_type, betting_market_object_type, bet_object_type, + info_for_vout_object_type, OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types }; @@ -202,6 +203,7 @@ namespace graphene { namespace chain { class betting_market_group_object; class betting_market_object; class bet_object; + class info_for_vout_object; typedef object_id< protocol_ids, account_object_type, account_object> account_id_type; typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type; @@ -228,6 +230,7 @@ namespace graphene { namespace chain { typedef object_id< protocol_ids, betting_market_group_object_type, betting_market_group_object> betting_market_group_id_type; typedef object_id< protocol_ids, betting_market_object_type, betting_market_object> betting_market_id_type; typedef object_id< protocol_ids, bet_object_type, bet_object> bet_id_type; + typedef object_id< protocol_ids, info_for_vout_object_type, info_for_vout_object> info_for_vout_id_type; // implementation types class global_property_object; @@ -402,6 +405,7 @@ FC_REFLECT_ENUM( graphene::chain::object_type, (betting_market_group_object_type) (betting_market_object_type) (bet_object_type) + (info_for_vout_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, diff --git a/libraries/sidechain/CMakeLists.txt b/libraries/sidechain/CMakeLists.txt new file mode 100644 index 00000000..e12f598c --- /dev/null +++ b/libraries/sidechain/CMakeLists.txt @@ -0,0 +1,7 @@ +file( GLOB SOURCES "*.cpp" ) +file( GLOB HEADERS "include/*.hpp" ) + +add_library( sidechain STATIC ${SOURCES} ${HEADERS} ) + +target_link_libraries( sidechain ${Boost_LIBRARIES} fc graphene_chain ) +target_include_directories( sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) diff --git a/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp b/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp new file mode 100644 index 00000000..19b4b8f3 --- /dev/null +++ b/libraries/sidechain/include/sidechain/input_withdrawal_info.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +using boost::multi_index_container; +using namespace boost::multi_index; +using info_for_vout = graphene::chain::info_for_vout_object; + +namespace graphene { namespace chain { class database; } } + +namespace sidechain { + +struct prev_out +{ + std::string hash_tx; + uint32_t n_vout; + uint64_t amount; +}; + +struct info_for_vin +{ + info_for_vin() = default; + + info_for_vin( const prev_out& _out, const std::string& _address, std::vector _script = std::vector() ) : + 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 ) ); + } + + struct comparer { + bool operator() ( const info_for_vin& lhs, const info_for_vin& rhs ) const; + }; + + static uint64_t count_id_info_for_vin; + uint64_t id; + + fc::sha256 identifier; + + prev_out out; + std::string address; + std::vector script; + + bool created = false; +}; + +struct by_id; +struct by_identifier; +struct by_id_and_not_created; + +using info_for_vin_index = boost::multi_index_container, member>, + ordered_unique, member>, + ordered_non_unique, identity< info_for_vin >, info_for_vin::comparer > + > +>; + +class input_withdrawal_info +{ + +public: + + using iterator_id_vin = typename info_for_vin_index::template index::type::iterator; + using iterator_id_vout = typename graphene::chain::info_for_vout_index::index_type::template index::type::iterator; + + + input_withdrawal_info( graphene::chain::database& _db ) : db( _db ) {} + + + void insert_info_for_vin( const prev_out& out, const std::string& address, std::vector script = std::vector() ); + + void modify_info_for_vin( const info_for_vin& obj, const std::function& func ); + + void mark_as_used_vin( const info_for_vin& obj ); + + void remove_info_for_vin( const info_for_vin& obj ); + + std::pair find_info_for_vin( uint64_t id ); + + size_t size_info_for_vins() { return info_for_vins.size(); } + + std::vector get_info_for_vins(); + + + void insert_info_for_vout( const graphene::chain::account_id_type& payer, /*ayment_type addr_type,*/ const std::string& data, const uint64_t& amount ); + + void mark_as_used_vout( const graphene::chain::info_for_vout_object& obj ); + + void remove_info_for_vout( const info_for_vout& obj ); + + std::pair find_info_for_vout( uint64_t id ); + + size_t size_info_for_vouts(); + + std::vector get_info_for_vouts(); + +private: + + graphene::chain::database& db; + + thread_safe_index info_for_vins; + +}; + +} diff --git a/libraries/sidechain/include/sidechain/thread_safe_index.hpp b/libraries/sidechain/include/sidechain/thread_safe_index.hpp new file mode 100644 index 00000000..c4fdbda9 --- /dev/null +++ b/libraries/sidechain/include/sidechain/thread_safe_index.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +namespace sidechain { + +struct by_id; + +template +class thread_safe_index { + +public: + + using iterator = typename T1::iterator; + using iterator_id = typename T1::template index::type::iterator; + + std::pair insert( const typename T1::value_type& value ) { + std::lock_guard locker( lock ); + return data.insert( value ); + } + + void modify( const typename T1::value_type& obj, const std::function& func ) { + std::lock_guard locker( lock ); + data.modify( data.iterator_to(obj), [&func]( typename T1::value_type& obj ) { func(obj); } ); + } + + void remove( const typename T1::value_type& obj ) { + std::lock_guard locker( lock ); + data.erase( data.iterator_to( obj ) ); + } + + size_t size() { + std::lock_guard locker( lock ); + return data.size(); + } + + std::pair find( uint64_t id ) { + std::lock_guard locker( lock ); + auto& index = data.template get(); + auto it = index.find( id ); + if( it != index.end() ) { + return std::make_pair(true, it); + } + return std::make_pair(false, it); + } + + template + void safe_for(std::function::type::iterator itr1, + typename T1::template index::type::iterator itr2)> func) { + std::lock_guard locker( lock ); + auto& index = data.template get(); + func(index.begin(), index.end()); + } + +private: + + std::recursive_mutex lock; + + T1 data; + +}; + +} diff --git a/libraries/sidechain/input_withdrawal_info.cpp b/libraries/sidechain/input_withdrawal_info.cpp new file mode 100644 index 00000000..3871ac87 --- /dev/null +++ b/libraries/sidechain/input_withdrawal_info.cpp @@ -0,0 +1,122 @@ +#include +#include + +namespace sidechain { + +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; + } + return lhs.id < rhs.id; +} + +void input_withdrawal_info::insert_info_for_vin( const prev_out& out, const std::string& address, std::vector script ) +{ + info_for_vins.insert( info_for_vin( out, address, script ) ); +} + +void input_withdrawal_info::modify_info_for_vin( const info_for_vin& obj, const std::function& func ) +{ + info_for_vins.modify( obj, func ); +} + +void input_withdrawal_info::mark_as_used_vin( const info_for_vin& obj ) +{ + info_for_vins.modify( obj, [&]( info_for_vin& o ) { + o.created = true; + } ); +} + +void input_withdrawal_info::remove_info_for_vin( const info_for_vin& obj ) +{ + info_for_vins.remove( obj ); +} + +std::pair input_withdrawal_info::find_info_for_vin( uint64_t id ) +{ + return info_for_vins.find( id ); +} + +std::vector input_withdrawal_info::get_info_for_vins() +{ + std::vector result; + + 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 + 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 = get account address, from the address get the script + + result.push_back( vin ); + ++itr_b; + } + } ); + + return result; +} + +void input_withdrawal_info::insert_info_for_vout( const graphene::chain::account_id_type& payer, /*ayment_type addr_type,*/ const std::string& data, const uint64_t& amount ) +{ + db.create([&](graphene::chain::info_for_vout_object& obj) { + obj.payer = payer; + // obj.addr_type = addr_type; + obj.data = data; + obj.amount = amount; + }); +} + +void input_withdrawal_info::mark_as_used_vout( const graphene::chain::info_for_vout_object& obj ) +{ + db.modify( obj, [&]( graphene::chain::info_for_vout_object& o ) { + o.created = true; + }); +} + +void input_withdrawal_info::remove_info_for_vout( const info_for_vout& obj ) +{ + db.remove( obj ); +} + +std::pair input_withdrawal_info::find_info_for_vout( uint64_t id ) +{ + const auto& info_for_vout_idx = db.get_index_type().indices().get< graphene::chain::by_id >(); + auto itr = info_for_vout_idx.find( graphene::chain::info_for_vout_id_type( id ) ); + return std::make_pair( itr != info_for_vout_idx.end(), itr ); +} + +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_created >(); + auto itr = info_for_vout_idx.begin(); + for(size_t i = 0; i < 5 && itr != info_for_vout_idx.end() && !itr->created; i++) { + info_for_vout vout; + vout.payer = itr->payer; + // vout.addr_type = itr->addr_type; + vout.data = itr->data; + vout.amount = itr->amount; + + result.push_back( vout ); + ++itr; + } + + return result; +} + +} diff --git a/tests/tests/input_withdrawal_info_tests.cpp b/tests/tests/input_withdrawal_info_tests.cpp new file mode 100644 index 00000000..724790f6 --- /dev/null +++ b/tests/tests/input_withdrawal_info_tests.cpp @@ -0,0 +1,200 @@ +#include +#include +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; + +BOOST_FIXTURE_TEST_SUITE( input_withdrawal_info_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vin_test ) { + sidechain::input_withdrawal_info infos( db ); + sidechain::prev_out out = { "1", 1, 13 }; + infos.insert_info_for_vin( out, "addr1", { 0x01, 0x02, 0x03 } ); + BOOST_CHECK( infos.size_info_for_vins() == 1 ); + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vin_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 1; i <= 10; i++ ) { + sidechain::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 } ); + } + BOOST_CHECK( infos.size_info_for_vins() == 10 ); + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_id_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + sidechain::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 } ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).first ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->id == i ); + } + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_check_data_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + sidechain::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 } ); + } + + for( size_t i = 0; i < 10; i++ ) { + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).first ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->id == i ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->out.hash_tx == std::to_string( i ) ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->out.n_vout == i ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->out.amount == i ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->address == "addr" + std::to_string( i ) ); + std::vector script = { 0x01, 0x02, 0x03 }; + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->script == script ); + } + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_modify_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + sidechain::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 } ); + } + + for( size_t i = 0; i < 10; i++ ) { + if( i % 2 == 0 ) { + auto iter = infos.find_info_for_vin( static_cast< uint64_t >( i ) ); + BOOST_CHECK( iter.first ); + infos.modify_info_for_vin( *iter.second, [&]( sidechain::info_for_vin& obj ) { + obj.out.hash_tx = std::to_string( i + 1 ); + obj.out.n_vout = i + 1; + obj.out.amount = i + 1; + obj.address = "addr" + std::to_string( i ) + std::to_string( i ); + } ); + } + } + + for( size_t i = 0; i < 10; i++ ) { + if( i % 2 == 0 ) { + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).first ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->id == i ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->out.hash_tx == std::to_string( i + 1 ) ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->out.n_vout == i + 1 ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->out.amount == i + 1 ); + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->address == "addr" + std::to_string( i ) + std::to_string( i ) ); + std::vector script = { 0x01, 0x02, 0x03 }; + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).second->script == script ); + } + } + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vin_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + sidechain::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 } ); + } + + for( size_t i = 0; i < 10; i++ ) { + if( i % 2 == 0 ) { + auto iter = infos.find_info_for_vin( static_cast< uint64_t >( i ) ); + BOOST_CHECK( iter.first ); + infos.remove_info_for_vin( *iter.second ); + } + } + + for( size_t i = 0; i < 10; i++ ) { + if( i % 2 == 0 ) { + BOOST_CHECK( !infos.find_info_for_vin( static_cast< uint64_t >( i ) ).first ); + } else { + BOOST_CHECK( infos.find_info_for_vin( static_cast< uint64_t >( i ) ).first ); + } + } + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vins_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + sidechain::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 } ); + } + + const auto& vins = infos.get_info_for_vins(); + BOOST_CHECK( vins.size() == 5 ); // 5 amount vins to bitcoin transaction + + for( size_t i = 0; i < 7; i++ ) { + auto iter = infos.find_info_for_vin( static_cast< uint64_t >( i ) ); + infos.mark_as_used_vin( *iter.second ); + } + + const auto& vins2 = infos.get_info_for_vins(); + BOOST_CHECK( vins2.size() == 3 ); + + sidechain::info_for_vin::count_id_info_for_vin = 0; +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_insert_vout_test ) { + sidechain::input_withdrawal_info infos( db ); + infos.insert_info_for_vout( account_id_type(), "1", 1 ); + BOOST_CHECK( infos.size_info_for_vouts() == 1 ); +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_many_insert_vout_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 1; i <= 10; i++ ) { + infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast( i ) ); + } + BOOST_CHECK( infos.size_info_for_vouts() == 10 ); +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_remove_vout_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast( i ) ); + } + + for( size_t i = 0; i < 10; i++ ) { + if( i % 2 == 0 ) { + auto iter = infos.find_info_for_vout( static_cast< uint64_t >( i ) ); + BOOST_CHECK( iter.first ); + infos.remove_info_for_vout( *iter.second ); + } + } + + for( size_t i = 0; i < 10; i++ ) { + if( i % 2 == 0 ) { + BOOST_CHECK( !infos.find_info_for_vout( static_cast< uint64_t >( i ) ).first ); + } else { + BOOST_CHECK( infos.find_info_for_vout( static_cast< uint64_t >( i ) ).first ); + } + } +} + +BOOST_AUTO_TEST_CASE( input_withdrawal_info_get_info_for_vouts_test ) { + sidechain::input_withdrawal_info infos( db ); + for( size_t i = 0; i < 10; i++ ) { + infos.insert_info_for_vout( account_id_type(i), std::to_string( i ), static_cast( i ) ); + } + + const auto& vouts = infos.get_info_for_vouts(); + BOOST_CHECK( vouts.size() == 5 ); // 5 amount vouts to bitcoin transaction + + for( size_t i = 0; i < 7; i++ ) { + auto iter = infos.find_info_for_vout( static_cast< uint64_t >( i ) ); + infos.mark_as_used_vout( *iter.second ); + } + + const auto& vouts2 = infos.get_info_for_vouts(); + BOOST_CHECK( vouts2.size() == 3 ); +} + +BOOST_AUTO_TEST_SUITE_END()