diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 0cb6ae0d..9d3148db 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -432,7 +432,12 @@ namespace graphene { namespace app { } case balance_object_type:{ /** these are free from any accounts */ break; - } + } case son_member_object_type:{ + const auto& son_object = dynamic_cast(obj); + assert( son_object != nullptr ); + accounts.insert( son_object->son_member_account ); + break; + } case sport_object_type: case event_group_object_type: case event_object_type: diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 3f95a8c1..d6bffe04 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -136,6 +136,9 @@ class database_api_impl : public std::enable_shared_from_this fc::optional get_committee_member_by_account(account_id_type account)const; map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; + // SON members + fc::optional get_son_member_by_account(account_id_type account)const; + // Votes vector lookup_vote_ids( const vector& votes )const; @@ -1577,6 +1580,26 @@ map database_api_impl::lookup_committee_member return committee_members_by_account_name; } +////////////////////////////////////////////////////////////////////// +// // +// SON members // +// // +////////////////////////////////////////////////////////////////////// + +fc::optional database_api::get_son_member_by_account(account_id_type account)const +{ + return my->get_son_member_by_account( account ); +} + +fc::optional database_api_impl::get_son_member_by_account(account_id_type account) const +{ + const auto& idx = _db.get_index_type().indices().get(); + auto itr = idx.find(account); + if( itr != idx.end() ) + return *itr; + return {}; +} + ////////////////////////////////////////////////////////////////////// // // // Votes // diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 9d64cf11..1b7797ff 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -282,6 +282,12 @@ struct get_impacted_account_visitor _impacted.insert( op.affiliate ); } void operator()( const affiliate_referral_payout_operation& op ) { } + void operator()( const son_member_create_operation& op ){ + _impacted.insert( op.owner_account ); + } + void operator()( const son_member_delete_operation& op ){ + _impacted.insert( op.owner_account ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 3fac4b5f..0500023e 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -44,6 +44,8 @@ #include #include +#include + #include #include #include @@ -546,6 +548,17 @@ class database_api */ map lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const; + ///////////////// + // SON members // + ///////////////// + + /** + * @brief Get the son_member owned by a given account + * @param account The ID of the account whose son_member should be retrieved + * @return The son_member object, or null if the account does not have a son_member + */ + fc::optional get_son_member_by_account(account_id_type account)const; + /// WORKERS @@ -757,6 +770,9 @@ FC_API(graphene::app::database_api, (get_committee_member_by_account) (lookup_committee_member_accounts) + // SON members + (get_son_member_by_account) + // workers (get_workers_by_account) // Votes diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index a328cf1f..c7c8e7f8 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -111,6 +111,8 @@ add_library( graphene_chain affiliate_payout.cpp + btc-sidechain/son_operations_evaluator.cpp + ${HEADERS} ${PROTOCOL_HEADERS} "${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp" diff --git a/libraries/chain/btc-sidechain/son_operations_evaluator.cpp b/libraries/chain/btc-sidechain/son_operations_evaluator.cpp new file mode 100644 index 00000000..9ce855b3 --- /dev/null +++ b/libraries/chain/btc-sidechain/son_operations_evaluator.cpp @@ -0,0 +1,45 @@ +#include + +#include +#include + +namespace graphene { namespace chain { + +void_result create_son_member_evaluator::do_evaluate(const son_member_create_operation& op) +{ try{ + FC_ASSERT(db().get(op.owner_account).is_lifetime_member(), "Only Lifetime members may register a SON."); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +object_id_type create_son_member_evaluator::do_apply(const son_member_create_operation& op) +{ try { + vote_id_type vote_id; + db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) { + vote_id = get_next_vote_id(p, vote_id_type::son); + }); + + const auto& new_son_object = db().create( [&]( son_member_object& obj ){ + obj.son_member_account = op.owner_account; + obj.vote_id = vote_id; + obj.url = op.url; + }); + return new_son_object.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result delete_son_member_evaluator::do_evaluate(const son_member_delete_operation& op) +{ try { + database& d = db(); + const auto& idx = d.get_index_type().indices().get(); + FC_ASSERT( idx.find(op.owner_account) != idx.end() ); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result delete_son_member_evaluator::do_apply(const son_member_delete_operation& op) +{ try { + const auto& idx = db().get_index_type().indices().get(); + db().remove(*idx.find(op.owner_account)); + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +} } // namespace graphene::chain + \ No newline at end of file diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index d58c68f7..d3f79796 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -77,8 +77,12 @@ #include #include +#include + #include +#include + #include #include #include @@ -237,6 +241,9 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + + register_evaluator(); + register_evaluator(); } void database::initialize_indexes() @@ -301,6 +308,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/db_notify.cpp b/libraries/chain/db_notify.cpp index 53ec524d..2f3d89c3 100644 --- a/libraries/chain/db_notify.cpp +++ b/libraries/chain/db_notify.cpp @@ -269,6 +269,12 @@ struct get_impacted_account_visitor _impacted.insert( op.affiliate ); } void operator()( const affiliate_referral_payout_operation& op ) { } + void operator()( const son_member_create_operation& op ) { + _impacted.insert( op.owner_account ); + } + void operator()( const son_member_delete_operation& op ) { + _impacted.insert( op.owner_account ); + } }; void operation_get_impacted_accounts( const operation& op, flat_set& result ) @@ -357,6 +363,11 @@ void get_relevant_accounts( const object* obj, flat_set& accoun } case balance_object_type:{ /** these are free from any accounts */ break; + } case son_member_object_type:{ + const auto& son_object = dynamic_cast(obj); + assert( son_object != nullptr ); + accounts.insert( son_object->son_member_account ); + break; } } } diff --git a/libraries/chain/include/graphene/chain/btc-sidechain/son.hpp b/libraries/chain/include/graphene/chain/btc-sidechain/son.hpp new file mode 100644 index 00000000..6b6d7bac --- /dev/null +++ b/libraries/chain/include/graphene/chain/btc-sidechain/son.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include + +namespace graphene { namespace chain { + using namespace graphene::db; + + /** + * @class son_member_object + * @brief tracks information about a son_member account. + * @ingroup object + */ + class son_member_object : public abstract_object + { + public: + static const uint8_t space_id = protocol_ids; + static const uint8_t type_id = son_member_object_type; + + account_id_type son_member_account; + vote_id_type vote_id; + uint64_t total_votes = 0; + string url; + }; + + struct by_account; + struct by_vote_id; + using son_member_multi_index_type = multi_index_container< + son_member_object, + indexed_by< + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + >, + ordered_unique< tag, + member + > + > + >; + using son_member_index = generic_index; +} } // graphene::chain + +FC_REFLECT_DERIVED( graphene::chain::son_member_object, (graphene::db::object), + (son_member_account)(vote_id)(total_votes)(url) ) diff --git a/libraries/chain/include/graphene/chain/btc-sidechain/son_operations.hpp b/libraries/chain/include/graphene/chain/btc-sidechain/son_operations.hpp new file mode 100644 index 00000000..700dc5fc --- /dev/null +++ b/libraries/chain/include/graphene/chain/btc-sidechain/son_operations.hpp @@ -0,0 +1,35 @@ +#pragma once +#include + +namespace graphene { namespace chain { + + struct son_member_create_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type owner_account; + std::string url; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + + struct son_member_delete_operation : public base_operation + { + struct fee_parameters_type { uint64_t fee = 0; }; + + asset fee; + account_id_type owner_account; + + account_id_type fee_payer()const { return owner_account; } + share_type calculate_fee(const fee_parameters_type& k)const { return 0; } + }; + +} } // namespace graphene::chain + +FC_REFLECT( graphene::chain::son_member_create_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::son_member_create_operation, (fee)(owner_account)(url) ) + +FC_REFLECT( graphene::chain::son_member_delete_operation::fee_parameters_type, (fee) ) +FC_REFLECT( graphene::chain::son_member_delete_operation, (fee)(owner_account) ) diff --git a/libraries/chain/include/graphene/chain/btc-sidechain/son_operations_evaluator.hpp b/libraries/chain/include/graphene/chain/btc-sidechain/son_operations_evaluator.hpp new file mode 100644 index 00000000..f6a3e63e --- /dev/null +++ b/libraries/chain/include/graphene/chain/btc-sidechain/son_operations_evaluator.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +namespace graphene { namespace chain { + +class create_son_member_evaluator : public evaluator +{ +public: + typedef son_member_create_operation operation_type; + + void_result do_evaluate(const son_member_create_operation& o); + object_id_type do_apply(const son_member_create_operation& o); +}; + +class delete_son_member_evaluator : public evaluator +{ +public: + typedef son_member_delete_operation operation_type; + + void_result do_evaluate(const son_member_delete_operation& o); + void_result do_apply(const son_member_delete_operation& o); +}; + +} } // namespace graphene::chain diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 77f85df6..277f6cf5 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -52,6 +52,8 @@ namespace graphene { namespace chain { vector active_committee_members; // updated once per maintenance interval flat_set active_witnesses; // updated once per maintenance interval // n.b. witness scheduling is done by witness_schedule object + + flat_set active_son_members; // updated once per maintenance interval }; /** diff --git a/libraries/chain/include/graphene/chain/protocol/account.hpp b/libraries/chain/include/graphene/chain/protocol/account.hpp index 6d13a4d3..496e9067 100644 --- a/libraries/chain/include/graphene/chain/protocol/account.hpp +++ b/libraries/chain/include/graphene/chain/protocol/account.hpp @@ -52,6 +52,9 @@ namespace graphene { namespace chain { /// The number of active committee members this account votes the blockchain should appoint /// Must not exceed the actual number of committee members voted for in @ref votes uint16_t num_committee = 0; + /// The number of active son members this account votes the blockchain should appoint + /// Must not exceed the actual number of son members voted for in @ref votes + uint16_t num_son = 0; /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// account's balance of core asset. flat_set votes; diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 104a2ec3..73258a9d 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -45,6 +45,8 @@ #include #include +#include + namespace graphene { namespace chain { /** @@ -129,7 +131,9 @@ 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 + son_member_create_operation, + son_member_delete_operation > operation; /// @} // operations group diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 4b6e1589..cdac849b 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, + son_member_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 son_member_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, son_member_object_type, son_member_object> son_member_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) + (son_member_object_type) (OBJECT_TYPE_COUNT) ) FC_REFLECT_ENUM( graphene::chain::impl_object_type, diff --git a/libraries/chain/include/graphene/chain/protocol/vote.hpp b/libraries/chain/include/graphene/chain/protocol/vote.hpp index 215d4902..d970ed74 100644 --- a/libraries/chain/include/graphene/chain/protocol/vote.hpp +++ b/libraries/chain/include/graphene/chain/protocol/vote.hpp @@ -64,6 +64,7 @@ struct vote_id_type committee, witness, worker, + son, VOTE_TYPE_COUNT }; @@ -148,5 +149,5 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo ); FC_REFLECT_TYPENAME( fc::flat_set ) -FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) ) +FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) ) FC_REFLECT( graphene::chain::vote_id_type, (content) ) diff --git a/libraries/net/CMakeLists.txt b/libraries/net/CMakeLists.txt index 7aa617d7..f7f549ed 100644 --- a/libraries/net/CMakeLists.txt +++ b/libraries/net/CMakeLists.txt @@ -10,7 +10,7 @@ set(SOURCES node.cpp add_library( graphene_net ${SOURCES} ${HEADERS} ) target_link_libraries( graphene_net - PUBLIC fc graphene_db ) + PUBLIC graphene_chain ) target_include_directories( graphene_net PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../chain/include" "${CMAKE_CURRENT_BINARY_DIR}/../chain/include" diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index a7189138..c15c3954 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1378,6 +1378,28 @@ class wallet_api bool approve, bool broadcast = false); + /** Vote for a given son_member. + * + * An account can publish a list of all son_memberes they approve of. This + * command allows you to add or remove son_memberes from this list. + * Each account's vote is weighted according to the number of shares of the + * core asset owned by that account at the time the votes are tallied. + * + * @note you cannot vote against a son_member, you can only vote for the son_member + * or not vote for the son_member. + * + * @param voting_account the name or id of the account who is voting with their shares + * @param son_member the name or id of the son_member' owner account + * @param approve true if you wish to vote in favor of that son_member, false to + * remove your vote in favor of that son_member + * @param broadcast true if you wish to broadcast the transaction + * @return the signed transaction changing your vote for the given son_member + */ + signed_transaction vote_for_son_member(string voting_account, + string son_member, + bool approve, + bool broadcast = false); + /** Vote for a given witness. * * An account can publish a list of all witnesses they approve of. This @@ -1965,6 +1987,7 @@ FC_API( graphene::wallet::wallet_api, (get_vesting_balances) (withdraw_vesting) (vote_for_committee_member) + (vote_for_son_member) (vote_for_witness) (update_witness_votes) (set_voting_proxy) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 812740e6..3c0629db 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2024,6 +2024,40 @@ public: return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(committee_member)(approve)(broadcast) ) } + signed_transaction vote_for_son_member(string voting_account, + string son_member, + bool approve, + bool broadcast /* = false */) + { try { + account_object voting_account_object = get_account(voting_account); + account_id_type son_member_owner_account_id = get_account_id(son_member); + fc::optional son_member_obj = _remote_db->get_son_member_by_account(son_member_owner_account_id); + if (!son_member_obj) + FC_THROW("Account ${son_member} is not registered as a son_member", ("son_member", son_member)); + if (approve) + { + auto insert_result = voting_account_object.options.votes.insert(son_member_obj->vote_id); + if (!insert_result.second) + FC_THROW("Account ${account} was already voting for son_member ${son_member}", ("account", voting_account)("son_member", son_member)); + } + else + { + unsigned votes_removed = voting_account_object.options.votes.erase(son_member_obj->vote_id); + if (!votes_removed) + FC_THROW("Account ${account} is already not voting for son_member ${son_member}", ("account", voting_account)("son_member", son_member)); + } + account_update_operation account_update_op; + account_update_op.account = voting_account_object.id; + account_update_op.new_options = voting_account_object.options; + + signed_transaction tx; + tx.operations.push_back( account_update_op ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (voting_account)(son_member)(approve)(broadcast) ) } + signed_transaction vote_for_witness(string voting_account, string witness, bool approve, @@ -4044,6 +4078,14 @@ signed_transaction wallet_api::vote_for_committee_member(string voting_account, return my->vote_for_committee_member(voting_account, witness, approve, broadcast); } +signed_transaction wallet_api::vote_for_son_member(string voting_account, + string son_member, + bool approve, + bool broadcast /* = false */) +{ + return my->vote_for_son_member(voting_account, son_member, approve, broadcast); +} + signed_transaction wallet_api::vote_for_witness(string voting_account, string witness, bool approve, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fef122b5..88c6e8ea 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -45,4 +45,8 @@ file(GLOB RANDOM_SOURCES "random/*.cpp") add_executable( random_test ${RANDOM_SOURCES} ${COMMON_SOURCES} ) target_link_libraries( random_test graphene_chain graphene_app graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} ) +file(GLOB UNIT_TESTS "btc_sidechain_tests/*.cpp") +add_executable( btc_sidechain_test ${UNIT_TESTS} ${COMMON_SOURCES} ) +target_link_libraries( btc_sidechain_test graphene_chain graphene_app graphene_account_history graphene_bookie graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} ) + add_subdirectory( generate_empty_blocks ) diff --git a/tests/btc_sidechain_tests/main.cpp b/tests/btc_sidechain_tests/main.cpp new file mode 100644 index 00000000..40b64f9d --- /dev/null +++ b/tests/btc_sidechain_tests/main.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP; + +boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { + std::srand(time(NULL)); + std::cout << "Random number generator seeded to " << time(NULL) << std::endl; + const char* genesis_timestamp_str = getenv("GRAPHENE_TESTING_GENESIS_TIMESTAMP"); + if( genesis_timestamp_str != nullptr ) + { + GRAPHENE_TESTING_GENESIS_TIMESTAMP = std::stoul( genesis_timestamp_str ); + } + std::cout << "GRAPHENE_TESTING_GENESIS_TIMESTAMP is " << GRAPHENE_TESTING_GENESIS_TIMESTAMP << std::endl; + return nullptr; +} diff --git a/tests/btc_sidechain_tests/son_operations_tests.cpp b/tests/btc_sidechain_tests/son_operations_tests.cpp new file mode 100644 index 00000000..08f9ca68 --- /dev/null +++ b/tests/btc_sidechain_tests/son_operations_tests.cpp @@ -0,0 +1,67 @@ +#include + +#include + +#include "../common/database_fixture.hpp" + +#include +#include + +using namespace graphene::chain; +using namespace graphene::chain::test; + +class test_create_son_member_evaluator: public create_son_member_evaluator { +public: + test_create_son_member_evaluator( database& link_db ): + ptr_trx_state( new transaction_evaluation_state( &link_db ) ) + { + trx_state = ptr_trx_state.get(); + } + std::unique_ptr ptr_trx_state; +}; + +class test_delete_son_member_evaluator: public delete_son_member_evaluator { +public: + test_delete_son_member_evaluator( database& link_db ): + ptr_trx_state( new transaction_evaluation_state( &link_db ) ) + { + trx_state = ptr_trx_state.get(); + } + std::unique_ptr ptr_trx_state; +}; + +BOOST_FIXTURE_TEST_SUITE( son_operation_tests, database_fixture ) + +BOOST_AUTO_TEST_CASE( create_son_test ){ + std::string test_url = "https://create_son_test"; + test_create_son_member_evaluator test_eval( db ); + son_member_create_operation op; + op.owner_account = account_id_type(1); + op.url = test_url; + + BOOST_CHECK_NO_THROW( test_eval.do_evaluate( op ) ); + auto id = test_eval.do_apply( op ); + const auto& idx = db.get_index_type().indices().get(); + + BOOST_REQUIRE( idx.size() == 1 ); + + auto obj = idx.find( op.owner_account ); + BOOST_REQUIRE( obj != idx.end() ); + BOOST_CHECK( obj->url == test_url ); + BOOST_CHECK( id == obj->id ); +} + +BOOST_AUTO_TEST_CASE( delete_son_test ){ + INVOKE(create_son_test); + test_delete_son_member_evaluator test_eval( db ); + + son_member_delete_operation delete_op; + delete_op.owner_account = account_id_type(1); + BOOST_CHECK_NO_THROW( test_eval.do_evaluate( delete_op ) ); + test_eval.do_apply( delete_op ); + + const auto& idx = db.get_index_type().indices().get(); + BOOST_REQUIRE( idx.empty() ); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/tests/common/genesis_file_util.hpp b/tests/common/genesis_file_util.hpp index e058df02..be56eaf4 100644 --- a/tests/common/genesis_file_util.hpp +++ b/tests/common/genesis_file_util.hpp @@ -40,4 +40,4 @@ boost::filesystem::path create_genesis_file(fc::temp_directory& directory) { fc::json::save_to_file(genesis_state, genesis_out); return genesis_path; -} +} \ No newline at end of file