From a0ae945f8d17f4869bc5b5ba3d47c7e1a9da4560 Mon Sep 17 00:00:00 2001 From: Prabhjot Date: Fri, 9 Aug 2019 13:01:57 -0400 Subject: [PATCH] Added new lottery_asset_create_operation --- libraries/app/impacted.cpp | 1 + libraries/chain/asset_evaluator.cpp | 158 +++++++++++++++++- libraries/chain/db_init.cpp | 1 + libraries/chain/hardfork.d/SWEEPS.hf | 2 +- .../graphene/chain/asset_evaluator.hpp | 16 ++ .../include/graphene/chain/asset_object.hpp | 16 +- .../graphene/chain/protocol/asset_ops.hpp | 54 +++++- .../graphene/chain/protocol/operations.hpp | 1 + libraries/chain/protocol/asset_ops.cpp | 48 ++++-- libraries/wallet/wallet.cpp | 16 +- tests/tests/lottery_tests.cpp | 2 +- 11 files changed, 278 insertions(+), 37 deletions(-) diff --git a/libraries/app/impacted.cpp b/libraries/app/impacted.cpp index 745a54c5..08253417 100644 --- a/libraries/app/impacted.cpp +++ b/libraries/app/impacted.cpp @@ -282,6 +282,7 @@ struct get_impacted_account_visitor _impacted.insert( op.affiliate ); } void operator()( const affiliate_referral_payout_operation& op ) { } + void operator()( const lottery_asset_create_operation& op) { } void operator()( const ticket_purchase_operation& op ) { _impacted.insert( op.buyer ); diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index a84c1472..1c56499f 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -120,12 +120,6 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision ); } - if( op.extensions.which() == asset_extension::tag::value ) { - FC_ASSERT( op.common_options.max_supply >= 5 ); - auto lottery_options = op.extensions.get(); - lottery_options.validate(); - FC_ASSERT( lottery_options.end_date > d.head_block_time() || lottery_options.end_date == time_point_sec() ); - } return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -164,6 +158,155 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o auto next_asset_id = db().get_index_type().get_next_id(); + const asset_object& new_asset = + db().create( [&]( asset_object& a ) { + a.issuer = op.issuer; + a.symbol = op.symbol; + a.precision = op.precision; + a.options = op.common_options; + + if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 ) + a.options.core_exchange_rate.quote.asset_id = next_asset_id; + else + a.options.core_exchange_rate.base.asset_id = next_asset_id; + + a.dynamic_asset_data_id = dyn_asset.id; + + if( op.bitasset_opts.valid() ) + a.bitasset_data_id = bit_asset_id; + }); + assert( new_asset.id == next_asset_id ); + + return new_asset.id; +} FC_CAPTURE_AND_RETHROW( (op) ) } + +void_result lottery_asset_create_evaluator::do_evaluate( const lottery_asset_create_operation& op ) +{ try { + + database& d = db(); + + const auto& chain_parameters = d.get_global_properties().parameters; + FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); + FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); + + // Check that all authorities do exist + for( auto id : op.common_options.whitelist_authorities ) + d.get_object(id); + for( auto id : op.common_options.blacklist_authorities ) + d.get_object(id); + + auto& asset_indx = d.get_index_type().indices().get(); + auto asset_symbol_itr = asset_indx.find( op.symbol ); + FC_ASSERT( asset_symbol_itr == asset_indx.end() ); + + if( d.head_block_time() > HARDFORK_385_TIME ) + { + + if( d.head_block_time() <= HARDFORK_409_TIME ) + { + auto dotpos = op.symbol.find( '.' ); + if( dotpos != std::string::npos ) + { + auto prefix = op.symbol.substr( 0, dotpos ); + auto asset_symbol_itr = asset_indx.find( op.symbol ); + FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered", + ("s",op.symbol)("p",prefix) ); + FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}", + ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) ); + } + } + else + { + auto dotpos = op.symbol.rfind( '.' ); + if( dotpos != std::string::npos ) + + { + auto prefix = op.symbol.substr( 0, dotpos ); + auto asset_symbol_itr = asset_indx.find( prefix ); + FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered", + ("s",op.symbol)("p",prefix) ); + FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}", + ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) ); + } + } + + } + else + { + auto dotpos = op.symbol.find( '.' ); + if( dotpos != std::string::npos ) + wlog( "Asset ${s} has a name which requires hardfork 385", ("s",op.symbol) ); + } + + // core_fee_paid -= core_fee_paid.value/2; + + if( op.bitasset_opts ) + { + const asset_object& backing = op.bitasset_opts->short_backing_asset(d); + if( backing.is_market_issued() ) + { + const asset_bitasset_data_object& backing_bitasset_data = backing.bitasset_data(d); + const asset_object& backing_backing = backing_bitasset_data.options.short_backing_asset(d); + FC_ASSERT( !backing_backing.is_market_issued(), + "May not create a bitasset backed by a bitasset backed by a bitasset." ); + FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing_backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + } else + FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing.get_id() == asset_id_type(), + "May not create a blockchain-controlled market asset which is not backed by CORE."); + FC_ASSERT( op.bitasset_opts->feed_lifetime_sec > chain_parameters.block_interval && + op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval ); + } + if( op.is_prediction_market ) + { + FC_ASSERT( op.bitasset_opts ); + FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision ); + } + + if( op.extensions.which() == asset_extension::tag::value ) { + FC_ASSERT( op.common_options.max_supply >= 5 ); + auto lottery_options = op.extensions; + lottery_options.validate(); + FC_ASSERT( lottery_options.end_date > d.head_block_time() || lottery_options.end_date == time_point_sec() ); + } + return void_result(); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +// copied from bitshares. (https://github.com/bitshares/bitshares-core/issues/429) +void lottery_asset_create_evaluator::pay_fee() +{ + fee_is_odd = core_fee_paid.value & 1; + core_fee_paid -= core_fee_paid.value/2; + generic_evaluator::pay_fee(); +} + +object_id_type lottery_asset_create_evaluator::do_apply( const lottery_asset_create_operation& op ) +{ try { + // includes changes from bitshares. (https://github.com/bitshares/bitshares-core/issues/429) + bool hf_429 = fee_is_odd && db().head_block_time() > HARDFORK_CORE_429_TIME; + + const asset_dynamic_data_object& dyn_asset = + db().create( [&]( asset_dynamic_data_object& a ) { + a.current_supply = 0; + a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0); + }); + if( fee_is_odd && !hf_429 ) + { + const auto& core_dd = db().get( asset_id_type() ).dynamic_data( db() ); + db().modify( core_dd, [=]( asset_dynamic_data_object& dd ) { + dd.current_supply++; + }); + } + + asset_bitasset_data_id_type bit_asset_id; + if( op.bitasset_opts.valid() ) + bit_asset_id = db().create( [&]( asset_bitasset_data_object& a ) { + a.options = *op.bitasset_opts; + a.is_prediction_market = op.is_prediction_market; + }).id; + + auto next_asset_id = db().get_index_type().get_next_id(); + const asset_object& new_asset = db().create( [&]( asset_object& a ) { a.issuer = op.issuer; @@ -172,7 +315,8 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o a.options = op.common_options; if( op.extensions.which() == asset_extension::tag::value ) { a.precision = 0; - a.lottery_options = op.extensions.get(); //a.lottery_options->balance = asset( 0, a.lottery_options->ticket_price.asset_id ); + a.lottery_options = op.extensions; + //a.lottery_options->balance = asset( 0, a.lottery_options->ticket_price.asset_id ); a.lottery_options->owner = a.id; db().create([&](lottery_balance_object& lbo) { lbo.lottery_id = a.id; diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 25b866fc..99343682 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -238,6 +238,7 @@ void database::initialize_evaluators() register_evaluator(); register_evaluator(); register_evaluator(); + register_evaluator(); register_evaluator(); register_evaluator(); register_evaluator(); diff --git a/libraries/chain/hardfork.d/SWEEPS.hf b/libraries/chain/hardfork.d/SWEEPS.hf index 587e007e..aafecdf7 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( 1515000764 )) +#define HARDFORK_SWEEPS_TIME (fc::time_point_sec( 1565272800 )) #endif diff --git a/libraries/chain/include/graphene/chain/asset_evaluator.hpp b/libraries/chain/include/graphene/chain/asset_evaluator.hpp index 27fb1aa2..d65d37fc 100644 --- a/libraries/chain/include/graphene/chain/asset_evaluator.hpp +++ b/libraries/chain/include/graphene/chain/asset_evaluator.hpp @@ -44,6 +44,22 @@ namespace graphene { namespace chain { bool fee_is_odd; }; + class lottery_asset_create_evaluator : public evaluator + { + public: + typedef lottery_asset_create_operation operation_type; + + void_result do_evaluate( const lottery_asset_create_operation& o ); + object_id_type do_apply( const lottery_asset_create_operation& o ); + + /** override the default behavior defined by generic_evalautor which is to + * post the fee to fee_paying_account_stats.pending_fees + */ + virtual void pay_fee() override; + private: + bool fee_is_odd; + }; + class asset_issue_evaluator : public evaluator { public: diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 706cadcb..afd5215a 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -400,7 +400,7 @@ namespace graphene { namespace chain { class lottery_balance_object : public abstract_object { public: - static const uint8_t space_id = protocol_ids; + static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_lottery_balance_object_type; asset_id_type lottery_id; @@ -416,7 +416,7 @@ namespace graphene { namespace chain { /** * @ingroup object_index */ - using lottery_balance_index_type = multi_index_container< + typedef multi_index_container< lottery_balance_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, @@ -424,18 +424,18 @@ namespace graphene { namespace chain { member > > - >; + > lottery_balance_index_type; /** * @ingroup object_index */ - using lottery_balance_index = generic_index; + typedef generic_index lottery_balance_index; class sweeps_vesting_balance_object : public abstract_object { public: - static const uint8_t space_id = protocol_ids; + static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_sweeps_vesting_balance_object_type; @@ -452,7 +452,7 @@ namespace graphene { namespace chain { /** * @ingroup object_index */ - using sweeps_vesting_balance_index_type = multi_index_container< + typedef multi_index_container< sweeps_vesting_balance_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, @@ -460,12 +460,12 @@ namespace graphene { namespace chain { member > > - >; + > sweeps_vesting_balance_index_type; /** * @ingroup object_index */ - using sweeps_vesting_balance_index = generic_index; + typedef generic_index sweeps_vesting_balance_index; } } // graphene::chain diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index ce6ef3af..1d158d83 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -52,9 +52,6 @@ namespace graphene { namespace chain { void validate()const; }; - typedef static_variant< void_t, lottery_asset_options > asset_extension; - - /** * @brief The asset_options struct contains options available on all assets in the network * @@ -195,7 +192,6 @@ namespace graphene { namespace chain { uint64_t symbol3 = 500000 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t symbol4 = 300000 * GRAPHENE_BLOCKCHAIN_PRECISION; uint64_t long_symbol = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; - uint64_t lottery_asset = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; uint32_t price_per_kbyte = 10; /// only required for large memos. }; @@ -219,7 +215,42 @@ namespace graphene { namespace chain { /// For BitAssets, set this to true if the asset implements a @ref prediction_market; false otherwise bool is_prediction_market = false; // containing lottery_asset_options now - asset_extension extensions; + extensions_type extensions; + + account_id_type fee_payer()const { return issuer; } + void validate()const; + share_type calculate_fee( const fee_parameters_type& k )const; + }; + + ///Operation for creation of lottery + struct lottery_asset_create_operation : public base_operation + { + struct fee_parameters_type { + uint64_t lottery_asset = 5000 * GRAPHENE_BLOCKCHAIN_PRECISION; + uint32_t price_per_kbyte = 10; /// only required for large lottery names. + }; + + asset fee; + /// This account must sign and pay the fee for this operation. Later, this account may update the asset + account_id_type issuer; + /// The ticker symbol of this asset + string symbol; + /// Number of digits to the right of decimal point, must be less than or equal to 12 + uint8_t precision = 0; + + /// Options common to all assets. + /// + /// @note common_options.core_exchange_rate technically needs to store the asset ID of this new asset. Since this + /// ID is not known at the time this operation is created, create this price as though the new asset has instance + /// ID 1, and the chain will overwrite it with the new asset's ID. + asset_options common_options; + /// Options only available for BitAssets. MUST be non-null if and only if the @ref market_issued flag is set in + /// common_options.flags + optional bitasset_opts; + /// For BitAssets, set this to true if the asset implements a @ref prediction_market; false otherwise + bool is_prediction_market = false; + // containing lottery_asset_options now + lottery_asset_options extensions; account_id_type fee_payer()const { return issuer; } void validate()const; @@ -661,7 +692,8 @@ FC_REFLECT( graphene::chain::benefactor, (id)(share) ) FC_REFLECT( graphene::chain::lottery_asset_options, (benefactors)(owner)(winning_tickets)(ticket_price)(end_date)(ending_on_soldout)(is_active) ) -FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(lottery_asset)(price_per_kbyte) ) +FC_REFLECT( graphene::chain::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) +FC_REFLECT( graphene::chain::lottery_asset_create_operation::fee_parameters_type, (lottery_asset)(price_per_kbyte) ) FC_REFLECT( graphene::chain::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::asset_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::asset_settle_cancel_operation::fee_parameters_type, ) @@ -685,6 +717,16 @@ FC_REFLECT( graphene::chain::asset_create_operation, (is_prediction_market) (extensions) ) +FC_REFLECT( graphene::chain::lottery_asset_create_operation, + (fee) + (issuer) + (symbol) + (precision) + (common_options) + (bitasset_opts) + (is_prediction_market) + (extensions) + ) FC_REFLECT( graphene::chain::asset_update_operation, (fee) (issuer) diff --git a/libraries/chain/include/graphene/chain/protocol/operations.hpp b/libraries/chain/include/graphene/chain/protocol/operations.hpp index 2f70c4f7..bce0e201 100644 --- a/libraries/chain/include/graphene/chain/protocol/operations.hpp +++ b/libraries/chain/include/graphene/chain/protocol/operations.hpp @@ -131,6 +131,7 @@ namespace graphene { namespace chain { event_group_delete_operation, affiliate_payout_operation, // VIRTUAL affiliate_referral_payout_operation, // VIRTUAL + lottery_asset_create_operation, ticket_purchase_operation, lottery_reward_operation, lottery_end_operation, diff --git a/libraries/chain/protocol/asset_ops.cpp b/libraries/chain/protocol/asset_ops.cpp index 95b97bfd..e4942aa4 100644 --- a/libraries/chain/protocol/asset_ops.cpp +++ b/libraries/chain/protocol/asset_ops.cpp @@ -78,18 +78,13 @@ share_type asset_issue_operation::calculate_fee(const fee_parameters_type& k)con share_type asset_create_operation::calculate_fee(const asset_create_operation::fee_parameters_type& param)const { auto core_fee_required = param.long_symbol; - - if( extensions.which() == asset_extension::tag::value ) { - core_fee_required = param.lottery_asset; - } else { - switch(symbol.size()) { - case 3: core_fee_required = param.symbol3; - break; - case 4: core_fee_required = param.symbol4; - break; - default: - break; - } + switch(symbol.size()) { + case 3: core_fee_required = param.symbol3; + break; + case 4: core_fee_required = param.symbol4; + break; + default: + break; } // common_options contains several lists and a string. Charge fees for its size core_fee_required += calculate_data_fee( fc::raw::pack_size(*this), param.price_per_kbyte ); @@ -116,6 +111,35 @@ void asset_create_operation::validate()const FC_ASSERT(precision <= 12); } +share_type lottery_asset_create_operation::calculate_fee(const lottery_asset_create_operation::fee_parameters_type& param)const +{ + auto core_fee_required = param.lottery_asset; + + // common_options contains several lists and a string. Charge fees for its size + core_fee_required += calculate_data_fee( fc::raw::pack_size(*this), param.price_per_kbyte ); + + return core_fee_required; +} + +void lottery_asset_create_operation::validate()const +{ + FC_ASSERT( fee.amount >= 0 ); + FC_ASSERT( is_valid_symbol(symbol) ); + common_options.validate(); + if( common_options.issuer_permissions & (disable_force_settle|global_settle) ) + FC_ASSERT( bitasset_opts.valid() ); + if( is_prediction_market ) + { + FC_ASSERT( bitasset_opts.valid(), "Cannot have a User-Issued Asset implement a prediction market." ); + FC_ASSERT( common_options.issuer_permissions & global_settle ); + } + if( bitasset_opts ) bitasset_opts->validate(); + + asset dummy = asset(1) * common_options.core_exchange_rate; + FC_ASSERT(dummy.asset_id == asset_id_type(1)); + FC_ASSERT(precision <= 12); +} + void asset_update_operation::validate()const { FC_ASSERT( fee.amount >= 0 ); diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 719018be..59564852 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -135,6 +135,7 @@ public: std::string operator()(const account_create_operation& op)const; std::string operator()(const account_update_operation& op)const; std::string operator()(const asset_create_operation& op)const; + std::string operator()(const lottery_asset_create_operation& op)const; std::string operator()(const asset_dividend_distribution_operation& op)const; std::string operator()(const tournament_payout_operation& op)const; std::string operator()(const bet_place_operation& op)const; @@ -1454,7 +1455,7 @@ public: account_object issuer_account = get_account( issuer ); FC_ASSERT(!find_asset(symbol).valid(), "Asset with that symbol already exists!"); - asset_create_operation create_op; + lottery_asset_create_operation create_op; create_op.issuer = issuer_account.id; create_op.symbol = symbol; create_op.precision = 0; @@ -3344,7 +3345,18 @@ std::string operation_printer::operator()(const asset_create_operation& op) cons if( op.bitasset_opts.valid() ) out << "BitAsset "; else - out << "User-Issue Asset "; + out << "User-Issued Asset "; + out << "'" << op.symbol << "' with issuer " << wallet.get_account(op.issuer).name; + return fee(op.fee); +} + +std::string operation_printer::operator()(const lottery_asset_create_operation& op) const +{ + out << "Create "; + if( op.bitasset_opts.valid() ) + out << "BitAsset "; + else + out << "User-Issued Asset "; out << "'" << op.symbol << "' with issuer " << wallet.get_account(op.issuer).name; return fee(op.fee); } diff --git a/tests/tests/lottery_tests.cpp b/tests/tests/lottery_tests.cpp index 624502fd..b0f234e2 100644 --- a/tests/tests/lottery_tests.cpp +++ b/tests/tests/lottery_tests.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE( create_lottery_asset_test ) try { generate_block(); asset_id_type test_asset_id = db.get_index().get_next_id(); - asset_create_operation creator; + lottery_asset_create_operation creator; creator.issuer = account_id_type(); creator.fee = asset(); char symbol[5] = "LOT";