From 7ba965c86065dd82ad183deece1cb6681602d423 Mon Sep 17 00:00:00 2001 From: kstdl Date: Mon, 11 Dec 2017 17:42:55 +0300 Subject: [PATCH] minor logic changes. added db_api and cli_wallet methods --- libraries/app/database_api.cpp | 53 ++++++++++++++- .../app/include/graphene/app/database_api.hpp | 24 ++++++- libraries/chain/CMakeLists.txt | 1 - libraries/chain/asset_object.cpp | 3 +- libraries/chain/db_management.cpp | 32 ++++++---- libraries/chain/hardfork.d/SWEEPS.hf | 2 +- .../include/graphene/chain/asset_object.hpp | 16 ++++- .../graphene/chain/protocol/asset_ops.hpp | 2 +- .../graphene/chain/protocol/lottery_ops.hpp | 10 ++- libraries/chain/lottery_evaluator.cpp | 3 +- libraries/db/include/graphene/db/index.hpp | 2 - .../account_history_plugin.cpp | 9 ++- .../wallet/include/graphene/wallet/wallet.hpp | 25 ++++++-- libraries/wallet/wallet.cpp | 64 +++++++++++++------ tests/tests/lottery_tests.cpp | 38 +++++++++-- 15 files changed, 220 insertions(+), 64 deletions(-) diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 3f95a8c1..05679f9f 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -113,6 +113,14 @@ class database_api_impl : public std::enable_shared_from_this vector get_unmatched_bets_for_bettor(betting_market_id_type, account_id_type) const; vector get_all_unmatched_bets_for_bettor(account_id_type) const; + + // Lottery Assets + vector get_lotteries( asset_id_type stop = asset_id_type(), + unsigned limit = 100, + asset_id_type start = asset_id_type() )const; + asset get_lottery_balance( asset_id_type lottery_id )const; + + // Markets / feeds vector get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const; vector get_call_orders(asset_id_type a, uint32_t limit)const; @@ -174,7 +182,6 @@ class database_api_impl : public std::enable_shared_from_this if( !is_subscribed_to_item(i) ) { - idump((i)); _subscribe_filter.insert( vec.data(), vec.size() );//(vecconst char*)&i, sizeof(i) ); } } @@ -630,7 +637,6 @@ std::map database_api::get_full_accounts( const vector database_api_impl::get_full_accounts( const vector& names_or_ids, bool subscribe) { - idump((names_or_ids)); std::map results; for (const std::string& account_name_or_id : names_or_ids) @@ -1014,6 +1020,49 @@ vector> database_api_impl::lookup_asset_symbols(const vec return result; } +//////////////////// +// Lottery Assets // +//////////////////// + + +vector database_api::get_lotteries( asset_id_type stop, + unsigned limit, + asset_id_type start )const +{ + return my->get_lotteries( stop, limit, start ); +} +vector database_api_impl::get_lotteries(asset_id_type stop, + unsigned limit, + asset_id_type start )const +{ + vector result; + const auto& assets = _db.get_index_type().indices().get(); + + const auto range = assets.equal_range( boost::make_tuple( true ) ); + for( const auto& a : boost::make_iterator_range( range.first, range.second ) ) + { + if( start == asset_id_type() || (a.get_id().instance.value <= start.instance.value) ) + result.push_back( a ); + if( a.get_id().instance.value < stop.instance.value || result.size() >= limit ) + break; + } + + return result; +} + +asset database_api::get_lottery_balance( asset_id_type lottery_id )const +{ + return my->get_lottery_balance( lottery_id ); +} + +asset database_api_impl::get_lottery_balance( asset_id_type lottery_id )const +{ + auto lottery_asset = lottery_id( _db ); + FC_ASSERT( lottery_asset.is_lottery() ); + return _db.get_balance( lottery_id ); +} + + ////////////////////////////////////////////////////////////////////// // Peerplays // ////////////////////////////////////////////////////////////////////// diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 3fac4b5f..689f9532 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -348,7 +348,23 @@ class database_api * This function has semantics identical to @ref get_objects */ vector> lookup_asset_symbols(const vector& symbols_or_ids)const; - + + //////////////////// + // Lottery Assets // + //////////////////// + /** + * @brief Get a list of lottery assets + * @return The lottery assets between start and stop ids + */ + vector get_lotteries( asset_id_type stop = asset_id_type(), + unsigned limit = 100, + asset_id_type start = asset_id_type() )const; + /** + * @brief Get balance of lottery assets + */ + asset get_lottery_balance( asset_id_type lottery_id ) const; + + ///////////////////// // Peerplays // ///////////////////// @@ -733,7 +749,11 @@ FC_API(graphene::app::database_api, (list_betting_markets) (get_unmatched_bets_for_bettor) (get_all_unmatched_bets_for_bettor) - + + // Lottery assets + (get_lotteries) + (get_lottery_balance) + // Markets / feeds (get_order_book) (get_limit_orders) diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 20fb2ea1..a8d9e5db 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -90,7 +90,6 @@ add_library( graphene_chain account_object.cpp asset_object.cpp - lottery.cpp fba_object.cpp proposal_object.cpp vesting_balance_object.cpp diff --git a/libraries/chain/asset_object.cpp b/libraries/chain/asset_object.cpp index 7266d6bb..7545f6d9 100644 --- a/libraries/chain/asset_object.cpp +++ b/libraries/chain/asset_object.cpp @@ -262,11 +262,10 @@ void asset_object::distribute_sweeps_holders_part( database& db ) void asset_object::end_lottery( database& db ) { - idump(( "end_lottery" )); transaction_evaluation_state eval(&db); FC_ASSERT( is_lottery() ); - FC_ASSERT( lottery_options->is_active && ( lottery_options->end_date <= db.head_block_time() ) ); + FC_ASSERT( lottery_options->is_active && ( lottery_options->end_date <= db.head_block_time() || lottery_options->ending_on_soldout ) ); auto participants = distribute_winners_part( db ); if( participants.size() > 0) { diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp index 800f475d..e0681455 100644 --- a/libraries/chain/db_management.cpp +++ b/libraries/chain/db_management.cpp @@ -142,8 +142,6 @@ void database::open( if( last_block.valid() ) { _fork_db.start_block( *last_block ); - idump((last_block->id())(last_block->block_num())); - idump((head_block_id())(head_block_num())); if( last_block->id() != head_block_id() ) { FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state", @@ -210,22 +208,28 @@ void database::force_slow_replays() void database::check_ending_lotteries() { - const auto& lotteries_idx = get_index_type().indices().get(); - for( auto checking_asset: lotteries_idx ) - { - if( !checking_asset.is_lottery() ) break; - if( !checking_asset.lottery_options->is_active ) break; - if( checking_asset.lottery_options->end_date >= head_block_time() ) break; - checking_asset.end_lottery(*this); - } + try { + const auto& lotteries_idx = get_index_type().indices().get(); + for( auto checking_asset: lotteries_idx ) + { + FC_ASSERT( checking_asset.is_lottery() ); + FC_ASSERT( checking_asset.lottery_options->is_active ); + FC_ASSERT( checking_asset.lottery_options->end_date < head_block_time() ); + checking_asset.end_lottery(*this); + } + } catch( ... ) {} } void database::check_lottery_end_by_participants( asset_id_type asset_id ) { - asset_object asset_to_check = asset_id( *this ); - if( !asset_to_check.is_lottery() ) return; - if( !asset_to_check.lottery_options->ending_on_soldout ) return; - asset_to_check.end_lottery( *this ); + try { + asset_object asset_to_check = asset_id( *this ); + auto asset_dyn_props = asset_to_check.dynamic_data( *this ); + FC_ASSERT( asset_dyn_props.current_supply == asset_to_check.options.max_supply ); + FC_ASSERT( asset_to_check.is_lottery() ); + FC_ASSERT( asset_to_check.lottery_options->ending_on_soldout ); + asset_to_check.end_lottery( *this ); + } catch( ... ) {} } } } diff --git a/libraries/chain/hardfork.d/SWEEPS.hf b/libraries/chain/hardfork.d/SWEEPS.hf index 7dd1485c..587e007e 100644 --- a/libraries/chain/hardfork.d/SWEEPS.hf +++ b/libraries/chain/hardfork.d/SWEEPS.hf @@ -1,3 +1,3 @@ #ifndef HARDFORK_SWEEPS_TIME -#define HARDFORK_SWEEPS_TIME (fc::time_point_sec( 1510320000 )) +#define HARDFORK_SWEEPS_TIME (fc::time_point_sec( 1515000764 )) #endif diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 3e504d0a..21129ae4 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -147,7 +147,7 @@ namespace graphene { namespace chain { optional dividend_data_id; asset_id_type get_id()const { return id; } - + void validate()const { // UIAs may not be prediction markets, have force settlement, or global settlements @@ -255,7 +255,7 @@ namespace graphene { namespace chain { bool operator()(const asset_object& lhs, const asset_object& rhs) const { if ( !lhs.is_lottery() ) return false; - if ( !lhs.lottery_options->is_active && !rhs.is_lottery()) return true; // not active lotteries first + if ( !lhs.lottery_options->is_active && !rhs.is_lottery()) return true; // not active lotteries first, just assets then if ( !lhs.lottery_options->is_active ) return false; return lhs.get_lottery_expiration() > rhs.get_lottery_expiration(); } @@ -265,6 +265,7 @@ namespace graphene { namespace chain { struct by_type; struct by_issuer; struct active_lotteries; + struct by_lottery; typedef multi_index_container< asset_object, indexed_by< @@ -275,6 +276,17 @@ namespace graphene { namespace chain { identity< asset_object >, lottery_asset_comparer >, + ordered_unique< tag, + composite_key< + asset_object, + const_mem_fun, + member + >, + composite_key_compare< + std::greater< bool >, + std::greater< object_id_type > + > + >, ordered_unique< tag, composite_key< asset_object, const_mem_fun, diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index cca20d5e..60e1390b 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -95,7 +95,7 @@ namespace graphene { namespace chain { flat_set whitelist_markets; /** defines the assets that this asset may not be traded against in the market, must not overlap whitelist */ flat_set blacklist_markets; - + /** * data that describes the meaning/purpose of this asset, fee will be charged proportional to * size of description. diff --git a/libraries/chain/include/graphene/chain/protocol/lottery_ops.hpp b/libraries/chain/include/graphene/chain/protocol/lottery_ops.hpp index 1d238a55..32d70a37 100644 --- a/libraries/chain/include/graphene/chain/protocol/lottery_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/lottery_ops.hpp @@ -52,6 +52,9 @@ namespace graphene { namespace chain { share_type calculate_fee( const fee_parameters_type& k )const; }; + /** + * @ingroup operations + */ struct lottery_reward_operation : public base_operation { struct fee_parameters_type { @@ -77,6 +80,9 @@ namespace graphene { namespace chain { share_type calculate_fee( const fee_parameters_type& k )const { return k.fee; }; }; + /** + * @ingroup operations + */ struct lottery_end_operation : public base_operation { struct fee_parameters_type { @@ -86,9 +92,7 @@ namespace graphene { namespace chain { asset fee; // from what lottery is ticket asset_id_type lottery; - -// std::vector participants; - + map > participants; extensions_type extensions; diff --git a/libraries/chain/lottery_evaluator.cpp b/libraries/chain/lottery_evaluator.cpp index b64bdde1..67f16557 100644 --- a/libraries/chain/lottery_evaluator.cpp +++ b/libraries/chain/lottery_evaluator.cpp @@ -43,7 +43,8 @@ void_result ticket_purchase_evaluator::do_evaluate( const ticket_purchase_operat asset_dynamic_data = &lottery->dynamic_asset_data_id(db()); FC_ASSERT( asset_dynamic_data->current_supply < lottery->options.max_supply ); - + FC_ASSERT( (asset_dynamic_data->current_supply.value + op.tickets_to_buy) <= lottery->options.max_supply ); + auto lottery_options = *lottery->lottery_options; FC_ASSERT( lottery_options.is_active ); FC_ASSERT( lottery_options.ticket_price.asset_id == op.amount.asset_id ); diff --git a/libraries/db/include/graphene/db/index.hpp b/libraries/db/include/graphene/db/index.hpp index e84e4043..aebdb8b9 100644 --- a/libraries/db/include/graphene/db/index.hpp +++ b/libraries/db/include/graphene/db/index.hpp @@ -108,8 +108,6 @@ namespace graphene { namespace db { const object& get( object_id_type id )const { auto maybe_found = find( id ); - if (maybe_found == nullptr) - idump(("fail")); FC_ASSERT( maybe_found != nullptr, "Unable to find Object", ("id",id) ); return *maybe_found; } diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index 5cd00235..67cd362b 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -117,7 +117,14 @@ void account_history_plugin_impl::update_account_histories( const signed_block& impacted.insert( op.result.get() ); else graphene::app::operation_get_impacted_accounts( op.op, impacted ); - + if( op.op.which() == operation::tag< lottery_end_operation >::value ) + { + auto lop = op.op.get< lottery_end_operation >(); + auto asset_object = lop.lottery( db ); + impacted.insert( asset_object.issuer ); + for( auto benefactor : asset_object.lottery_options->benefactors ) + impacted.insert( benefactor.id ); + } for( auto& a : other ) for( auto& item : a.account_auths ) impacted.insert( item.first ); diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index 79ad8172..1cdb895b 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -348,7 +348,13 @@ class wallet_api * @returns the list of asset objects, ordered by symbol */ vector list_assets(const string& lowerbound, uint32_t limit)const; - + + + vector get_lotteries( asset_id_type stop = asset_id_type(), + unsigned limit = 100, + asset_id_type start = asset_id_type() )const; + + asset get_lottery_balance( asset_id_type lottery_id ) const; /** Returns the most recent operations on the named account. * * This returns a list of operation history objects, which describe activity on the account. @@ -1009,12 +1015,13 @@ class wallet_api fc::optional bitasset_opts, bool broadcast = false); - signed_transaction create_lottery(string issuer, - string symbol, - uint8_t precision, - asset_options common, - fc::optional bitasset_opts, - bool broadcast = false); + signed_transaction create_lottery( string issuer, + string symbol, + asset_options common, + lottery_asset_options lottery_opts, + bool broadcast = false); + + signed_transaction buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy ); /** Issue new shares of an asset. * @@ -1956,6 +1963,8 @@ FC_API( graphene::wallet::wallet_api, (issue_asset) (get_asset) (get_bitasset_data) + (get_lotteries) + (get_lottery_balance) (fund_asset_fee_pool) (reserve_asset) (global_settle_asset) @@ -2064,4 +2073,6 @@ FC_API( graphene::wallet::wallet_api, (get_binned_order_book) (get_matched_bets_for_bettor) (get_all_matched_bets_for_bettor) + + (buy_ticket) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index fbc3817a..c913bbaa 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -1447,9 +1447,8 @@ public: signed_transaction create_lottery(string issuer, string symbol, - uint8_t precision, asset_options common, - fc::optional bitasset_opts, + lottery_asset_options lottery_opts, bool broadcast = false) { try { account_object issuer_account = get_account( issuer ); @@ -1458,17 +1457,10 @@ public: asset_create_operation create_op; create_op.issuer = issuer_account.id; create_op.symbol = symbol; - create_op.precision = precision; + create_op.precision = 0; create_op.common_options = common; - create_op.bitasset_opts = bitasset_opts; - lottery_asset_options lottery_options; - lottery_options.benefactors.push_back( benefactor( issuer_account.id, 0.5 ) ); - lottery_options.end_date = _remote_db->get_dynamic_global_properties().time + fc::minutes(120); - lottery_options.ticket_price = asset(100); - lottery_options.winning_tickets.push_back(0.5); - - create_op.extension = lottery_options; + create_op.extension = lottery_opts; signed_transaction tx; tx.operations.push_back( create_op ); @@ -1476,8 +1468,28 @@ public: tx.validate(); return sign_transaction( tx, broadcast ); - } FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(precision)(common)(bitasset_opts)(broadcast) ) } + } FC_CAPTURE_AND_RETHROW( (issuer)(symbol)(common)(broadcast) ) } + + signed_transaction buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy ) + { try { + auto asset_obj = get_asset( lottery ); + FC_ASSERT( asset_obj.is_lottery() ); + ticket_purchase_operation top; + top.lottery = lottery; + top.buyer = buyer; + top.tickets_to_buy = tickets_to_buy; + top.amount = asset( asset_obj.lottery_options->ticket_price.amount * tickets_to_buy, asset_obj.lottery_options->ticket_price.asset_id ); + + signed_transaction tx; + tx.operations.push_back( top ); + set_operation_fees( tx, _remote_db->get_global_properties().parameters.current_fees); + tx.validate(); + + return sign_transaction( tx, true ); + } FC_CAPTURE_AND_RETHROW( (lottery)(tickets_to_buy) ) } + + signed_transaction update_asset(string symbol, optional new_issuer, asset_options new_options, @@ -3494,6 +3506,18 @@ vector wallet_api::list_assets(const string& lowerbound, uint32_t return my->_remote_db->list_assets( lowerbound, limit ); } +vector wallet_api::get_lotteries( asset_id_type stop, + unsigned limit, + asset_id_type start )const +{ + return my->_remote_db->get_lotteries( stop, limit, start ); +} + +asset wallet_api::get_lottery_balance( asset_id_type lottery_id )const +{ + return my->_remote_db->get_lottery_balance( lottery_id ); +} + vector wallet_api::get_account_history(string name, int limit)const { vector result; @@ -3917,17 +3941,21 @@ signed_transaction wallet_api::create_asset(string issuer, } signed_transaction wallet_api::create_lottery(string issuer, - string symbol, - uint8_t precision, - asset_options common, - fc::optional bitasset_opts, - bool broadcast) + string symbol, + asset_options common, + lottery_asset_options lottery_opts, + bool broadcast) { -return my->create_lottery(issuer, symbol, precision, common, bitasset_opts, broadcast); + return my->create_lottery(issuer, symbol, common, lottery_opts, broadcast); } +signed_transaction wallet_api::buy_ticket( asset_id_type lottery, account_id_type buyer, uint64_t tickets_to_buy ) +{ + return my->buy_ticket(lottery, buyer, tickets_to_buy); +} + signed_transaction wallet_api::update_asset(string symbol, optional new_issuer, asset_options new_options, diff --git a/tests/tests/lottery_tests.cpp b/tests/tests/lottery_tests.cpp index 6d1b8c42..19350772 100644 --- a/tests/tests/lottery_tests.cpp +++ b/tests/tests/lottery_tests.cpp @@ -43,7 +43,6 @@ BOOST_AUTO_TEST_CASE( create_lottery_asset_test ) { try { generate_block(); - idump((db.head_block_time())); asset_id_type test_asset_id = db.get_index().get_next_id(); asset_create_operation creator; creator.issuer = account_id_type(); @@ -65,13 +64,12 @@ BOOST_AUTO_TEST_CASE( create_lottery_asset_test ) lottery_options.ticket_price = asset(100); lottery_options.winning_tickets = { 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 5 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT, 10 * GRAPHENE_1_PERCENT }; lottery_options.is_active = test_asset_id.instance.value % 2; + lottery_options.ending_on_soldout = true; creator.extension = lottery_options; trx.operations.push_back(std::move(creator)); PUSH_TX( db, trx, ~0 ); - idump((lottery_options.end_date)); - idump((db.head_block_time())); generate_block(); auto test_asset = test_asset_id(db); @@ -334,7 +332,7 @@ BOOST_AUTO_TEST_CASE( claim_sweeps_vesting_balance_test ) sweeps_vesting_claim_operation claim; claim.account = benefactor; claim.amount_to_claim = available_for_claim; - trx.clear(); + trx.clear(); graphene::chain::test::set_expiration(db, trx); trx.operations.push_back(claim); PUSH_TX( db, trx, ~0 ); @@ -373,13 +371,10 @@ BOOST_AUTO_TEST_CASE( more_winners_then_participants_test ) generate_block(); test_asset = test_asset_id(db); auto holders = test_asset.get_holders(db); - idump(( db.get_balance(test_asset.get_id()) )); auto participants = test_asset.distribute_winners_part( db ); test_asset.distribute_benefactors_part( db ); test_asset.distribute_sweeps_holders_part( db ); generate_block(); - idump(( db.get_balance(test_asset.get_id()) )); - idump(( participants )); for( auto p: participants ) { idump(( get_operation_history(p.first) )); } @@ -442,6 +437,35 @@ BOOST_AUTO_TEST_CASE( ending_by_date_test ) } } +BOOST_AUTO_TEST_CASE( ending_by_participants_count_test ) +{ + try { + asset_id_type test_asset_id = db.get_index().get_next_id(); + INVOKE( create_lottery_asset_test ); + auto test_asset = test_asset_id(db); + FC_ASSERT( test_asset.lottery_options->is_active ); + account_id_type buyer(3); + transfer(account_id_type(), buyer, asset(10000000)); + ticket_purchase_operation tpo; + tpo.fee = asset(); + tpo.buyer = buyer; + tpo.lottery = test_asset.id; + tpo.tickets_to_buy = 200; + tpo.amount = asset(200 * 100); + trx.operations.push_back(tpo); + graphene::chain::test::set_expiration(db, trx); + PUSH_TX( db, trx, ~0 ); + trx.operations.clear(); + generate_block(); + test_asset = test_asset_id(db); + FC_ASSERT( !test_asset.lottery_options->is_active ); + } catch( fc::exception& e ) { + edump((e.to_detail_string())); + throw; + } +} + + BOOST_AUTO_TEST_CASE( try_to_end_empty_lottery_test ) { try {