From 329e5acc9159404812265197a8d8b0357b2c682d Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sun, 19 Apr 2020 19:06:03 -0400 Subject: [PATCH 1/6] Initial implementation of BSIP87 --- libraries/chain/asset_evaluator.cpp | 9 +++++++ libraries/chain/db_market.cpp | 24 ++++++++++++++++++- libraries/chain/hardfork.d/CORE_BSIP87.hf | 4 ++++ .../chain/include/graphene/chain/database.hpp | 1 + libraries/protocol/asset_ops.cpp | 2 ++ .../include/graphene/protocol/asset_ops.hpp | 4 +++- 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 libraries/chain/hardfork.d/CORE_BSIP87.hf diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 5e6460a089..3f8963441a 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -101,6 +101,11 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o FC_ASSERT( op.bitasset_opts->feed_lifetime_sec > chain_parameters.block_interval && op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval ); } + + FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_BSIP87_TIME + || !op.common_options.extensions.value.force_settle_fee_percent.valid(), + "A BitAsset's FSFP cannot be set before Hardfork BSIP87" ); + if( op.is_prediction_market ) { FC_ASSERT( op.bitasset_opts ); @@ -299,6 +304,10 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) "Incorrect issuer for asset! (${o.issuer} != ${a.issuer})", ("o.issuer", o.issuer)("a.issuer", a.issuer) ); + FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_BSIP87_TIME + || !o.new_options.extensions.value.force_settle_fee_percent.valid(), + "A BitAsset's FSFP cannot be set before Hardfork BSIP87" ); + const auto& chain_parameters = d.get_global_properties().parameters; FC_ASSERT( o.new_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 6b50a323d1..ba7b90baa8 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -925,7 +925,13 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a if( head_block_time() >= HARDFORK_CORE_1780_TIME ) settle_owner_ptr = &settle.owner(*this); - auto issuer_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives ); + // Issuer lays claim to both market fees and force-settlement fees (BSIP87) when a settle + // order is filled. Note that when computing force-settle fees, we reduce the 'receives' + // amount by the previous market fee so that the combined effect of the two fees will be + // multiplicative rather than additive. This prevents the net fee exceeding 100%. + const asset_object & recv_asset_obj = get(receives.asset_id); + asset issuer_fees = pay_market_fees( settle_owner_ptr, recv_asset_obj, receives ); + issuer_fees += calculate_force_settle_fees( recv_asset_obj, receives - issuer_fees ); if( pays < settle.balance ) { @@ -1293,4 +1299,20 @@ asset database::pay_market_fees(const account_object* seller, const asset_object return market_fees; } +/*** + * @brief Calculate force-settlement fee as per BSIP87 + * @param recv_asset the collateral asset (passed in to avoid a lookup) + * @param receives the amount of collateral settler would expect to receive prior to this fee + * (fee is computed as a percentage of this amount) + * @return asset representing the fee amount + */ +asset database::calculate_force_settle_fees(const asset_object& recv_asset, const asset& receives) const +{ + if( !recv_asset.options.extensions.value.force_settle_fee_percent.valid() + || *recv_asset.options.extensions.value.force_settle_fee_percent == 0 ) + return recv_asset.amount(0); + auto value = detail::calculate_percent(receives.amount, *recv_asset.options.extensions.value.force_settle_fee_percent); + return recv_asset.amount(value); +} + } } diff --git a/libraries/chain/hardfork.d/CORE_BSIP87.hf b/libraries/chain/hardfork.d/CORE_BSIP87.hf new file mode 100644 index 0000000000..a4fbc39f34 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_BSIP87.hf @@ -0,0 +1,4 @@ +// bitshares-core BSIP 87: add force-settlement fee percentage: +#ifndef HARDFORK_CORE_BSIP87_TIME +#define HARDFORK_CORE_BSIP87_TIME (fc::time_point_sec( 1679955066 ) ) // Temporary date until actual hardfork date is set +#endif diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 7103ed753c..5787b55692 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -441,6 +441,7 @@ namespace graphene { namespace chain { asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount); asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives ); + asset calculate_force_settle_fees(const asset_object& recv_asset, const asset& receives) const; ///@} diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 202c79f4fc..3dc47cb654 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -240,6 +240,8 @@ void asset_options::validate()const } if( extensions.value.reward_percent.valid() ) FC_ASSERT( *extensions.value.reward_percent <= GRAPHENE_100_PERCENT ); + if( extensions.value.force_settle_fee_percent.valid() ) + FC_ASSERT( *extensions.value.force_settle_fee_percent <= GRAPHENE_100_PERCENT ); } void asset_claim_fees_operation::validate()const { diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 78c204ac06..d8c29cbc8d 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -32,6 +32,8 @@ namespace graphene { namespace protocol { { fc::optional reward_percent; fc::optional> whitelist_market_fee_sharing; + // MERGE NOTE: BSIP-74 and/or BSIP-81 additions here + fc::optional force_settle_fee_percent; // BSIP-87 }; typedef extension additional_asset_options_t; @@ -544,7 +546,7 @@ FC_REFLECT( graphene::protocol::bitasset_options, (extensions) ) -FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing) ) +FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing)(force_settle_fee_percent) ) FC_REFLECT( graphene::protocol::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) FC_REFLECT( graphene::protocol::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::asset_settle_operation::fee_parameters_type, (fee) ) From 993117691ba1a3ad87b08ab1e46735e13d9c3918 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Tue, 21 Apr 2020 15:37:33 -0400 Subject: [PATCH 2/6] Fix routing of fees to correct parties. --- libraries/chain/db_market.cpp | 81 ++++++++++++++----- .../chain/include/graphene/chain/database.hpp | 2 +- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index ba7b90baa8..73e0b0d120 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -911,28 +911,58 @@ bool database::fill_call_order( const call_order_object& order, const asset& pay return collateral_freed.valid(); } FC_CAPTURE_AND_RETHROW( (order)(pays)(receives) ) } +/*** + * @brief fullfill a settle order in the specified amounts + * + * @details Called from database::match(), this coordinates exchange of debt asset X held in the + * settle order for collateral asset Y held in a call order, and routes fees. Note that we + * don't touch the call order directly, as match() handles this via a separate call to + * fill_call_order(). We are told exactly how much X and Y to exchange, based on details of + * order matching determined higher up the call chain. Thus it is possible that the settle + * order is not completely satisfied at the conclusion of this function. + * + * @param settle the force_settlement object + * @param pays the quantity of market-issued debt asset X which the settler will yield in this + * round (may be less than the full amount indicated in settle object) + * @param receives the quantity of collateral asset Y which the settler will receive in + * exchange for X + * @param fill_price the price at which the settle order will execute (not used - passed through + * to virtual operation) + * @param is_maker TRUE if the settle order is the maker, FALSE if it is the taker (passed + * through to virtual operation) + * @returns TRUE if the settle order was completely filled, FALSE if only partially filled + */ bool database::fill_settle_order( const force_settlement_object& settle, const asset& pays, const asset& receives, const price& fill_price, const bool is_maker ) { try { bool filled = false; const account_object* settle_owner_ptr = nullptr; - // The owner of the settle order pays market fees to the issuer of the collateral asset after HF core-1780 + // The owner of the settle order pays market fees to the issuer of the collateral asset. + // After HF core-1780, these fees are shared to the referral program, which is flagged to + // pay_market_fees by setting settle_owner_ptr non-null. // // TODO Check whether the HF check can be removed after the HF. // Note: even if logically it can be removed, perhaps the removal will lead to a small performance // loss. Needs testing. if( head_block_time() >= HARDFORK_CORE_1780_TIME ) settle_owner_ptr = &settle.owner(*this); + // Compute and pay the market fees: + asset market_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives ); + + // Issuer of the settled smartcoin asset lays claim to a force-settlement fee (BSIP87), but + // note that fee is denominated in collateral asset, not the debt asset. Asset object of + // debt asset is passed to the pay function so it knows where to put the fee. Note that + // amount of collateral asset upon which fee is assessed is reduced by market_fees already + // paid to prevent the total fee exceeding total collateral. + asset force_settle_fees = pay_force_settle_fees( get(pays.asset_id), receives - market_fees ); - // Issuer lays claim to both market fees and force-settlement fees (BSIP87) when a settle - // order is filled. Note that when computing force-settle fees, we reduce the 'receives' - // amount by the previous market fee so that the combined effect of the two fees will be - // multiplicative rather than additive. This prevents the net fee exceeding 100%. - const asset_object & recv_asset_obj = get(receives.asset_id); - asset issuer_fees = pay_market_fees( settle_owner_ptr, recv_asset_obj, receives ); - issuer_fees += calculate_force_settle_fees( recv_asset_obj, receives - issuer_fees ); + auto total_collateral_denominated_fees = market_fees + force_settle_fees; + // TODO: Do we need a something-for-nothing check here? Or does rounding guarantee fees + // strictly less than receives? + + // If we don't consume entire settle order: if( pays < settle.balance ) { modify(settle, [&pays](force_settlement_object& s) { @@ -941,15 +971,17 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a } else { filled = true; } - adjust_balance(settle.owner, receives - issuer_fees); + // Give released collateral not already taken as fees to settle order owner: + adjust_balance(settle.owner, receives - total_collateral_denominated_fees); assert( pays.asset_id != receives.asset_id ); - push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives, issuer_fees, fill_price, is_maker ) ); + push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives, total_collateral_denominated_fees, fill_price, is_maker ) ); if (filled) remove(settle); return filled; + } FC_CAPTURE_AND_RETHROW( (settle)(pays)(receives) ) } /** @@ -1300,19 +1332,30 @@ asset database::pay_market_fees(const account_object* seller, const asset_object } /*** - * @brief Calculate force-settlement fee as per BSIP87 - * @param recv_asset the collateral asset (passed in to avoid a lookup) - * @param receives the amount of collateral settler would expect to receive prior to this fee + * @brief Calculate force-settlement fee and give it to issuer of the settled asset + * @param collecting_asset the smart asset object which should receive the the fee + * @param collat_receives the amount of collateral the settler would expect to receive absent this fee * (fee is computed as a percentage of this amount) - * @return asset representing the fee amount + * @return asset denoting the amount of fee collected */ -asset database::calculate_force_settle_fees(const asset_object& recv_asset, const asset& receives) const +asset database::pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives) { - if( !recv_asset.options.extensions.value.force_settle_fee_percent.valid() - || *recv_asset.options.extensions.value.force_settle_fee_percent == 0 ) + const asset_object& recv_asset = get(collat_receives.asset_id); + FC_ASSERT( collecting_asset.get_id() != collat_receives.asset_id ); + if( !collecting_asset.options.extensions.value.force_settle_fee_percent.valid() + || *collecting_asset.options.extensions.value.force_settle_fee_percent == 0 ) return recv_asset.amount(0); - auto value = detail::calculate_percent(receives.amount, *recv_asset.options.extensions.value.force_settle_fee_percent); - return recv_asset.amount(value); + auto value = detail::calculate_percent(collat_receives.amount, *collecting_asset.options.extensions.value.force_settle_fee_percent); + asset settle_fee = recv_asset.amount(value); + // Deposit fee in asset's dynamic data object: + if( value > 0) + { + const auto& asset_dyn_data = collecting_asset.dynamic_asset_data_id(*this); + modify( asset_dyn_data, [&settle_fee]( asset_dynamic_data_object& obj ){ + obj.accumulated_fees += settle_fee.amount; // TODO: Wrong place to put fee + }); + } + return settle_fee; } } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 5787b55692..81d0bd9287 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -441,7 +441,7 @@ namespace graphene { namespace chain { asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount); asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives ); - asset calculate_force_settle_fees(const asset_object& recv_asset, const asset& receives) const; + asset pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives); ///@} From 16023af6be919c6dfec9933b8061f0f83d294b9b Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sat, 25 Apr 2020 02:24:36 -0400 Subject: [PATCH 3/6] Put collateral-denominated fees into correct container --- libraries/chain/db_market.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 73e0b0d120..efc680eb30 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1340,20 +1340,18 @@ asset database::pay_market_fees(const account_object* seller, const asset_object */ asset database::pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives) { - const asset_object& recv_asset = get(collat_receives.asset_id); FC_ASSERT( collecting_asset.get_id() != collat_receives.asset_id ); + + const asset_object& collat_asset = get(collat_receives.asset_id); if( !collecting_asset.options.extensions.value.force_settle_fee_percent.valid() || *collecting_asset.options.extensions.value.force_settle_fee_percent == 0 ) - return recv_asset.amount(0); + return collat_asset.amount(0); auto value = detail::calculate_percent(collat_receives.amount, *collecting_asset.options.extensions.value.force_settle_fee_percent); - asset settle_fee = recv_asset.amount(value); + asset settle_fee = collat_asset.amount(value); + // Deposit fee in asset's dynamic data object: - if( value > 0) - { - const auto& asset_dyn_data = collecting_asset.dynamic_asset_data_id(*this); - modify( asset_dyn_data, [&settle_fee]( asset_dynamic_data_object& obj ){ - obj.accumulated_fees += settle_fee.amount; // TODO: Wrong place to put fee - }); + if( value > 0) { + collecting_asset.bitasset_data(*this).receive_collateral_fee(*this, settle_fee); } return settle_fee; } From 09a96dab8c728f34e5e362af28dd4379884699ba Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Sat, 25 Apr 2020 12:44:41 -0400 Subject: [PATCH 4/6] Use accumulate_fee method --- libraries/chain/db_market.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index efc680eb30..e43697471c 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1351,7 +1351,7 @@ asset database::pay_force_settle_fees(const asset_object& collecting_asset, cons // Deposit fee in asset's dynamic data object: if( value > 0) { - collecting_asset.bitasset_data(*this).receive_collateral_fee(*this, settle_fee); + collecting_asset.accumulate_fee(*this, settle_fee); } return settle_fee; } From cd4753b52aa991ae2844e468c6d789a0b6c6e969 Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Tue, 5 May 2020 11:26:48 -0400 Subject: [PATCH 5/6] BSIP87 hardfork checks in proposal evaluator. And other minor changes. --- libraries/chain/asset_evaluator.cpp | 42 +++++++++---------- libraries/chain/db_market.cpp | 12 +++--- libraries/chain/proposal_evaluator.cpp | 22 ++++++---- .../include/graphene/protocol/asset_ops.hpp | 3 +- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 6fbb8a3685..b345e730db 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -56,7 +56,16 @@ namespace detail { } } - void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op) + void check_asset_options_hf_bsip87(const fc::time_point_sec& block_time, const asset_options& options) + { + // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: + FC_ASSERT( !options.extensions.value.force_settle_fee_percent.valid() + || block_time >= HARDFORK_CORE_BSIP87_TIME, + "A BitAsset's FSFP cannot be set before Hardfork BSIP87" ); + } + + void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, + const asset_claim_fees_operation& op) { // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: FC_ASSERT( !op.extensions.value.claim_from_asset_id.valid() || @@ -70,13 +79,17 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o { try { const database& d = db(); + const time_point_sec now = d.head_block_time(); + + // Hardfork Checks: + detail::check_asset_options_hf_1774(now, op.common_options); + detail::check_asset_options_hf_bsip81(now, op.common_options); + detail::check_asset_options_hf_bsip87(now, op.common_options); // HF_REMOVABLE 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 ); - detail::check_asset_options_hf_1774(d.head_block_time(), op.common_options); - // Check that all authorities do exist for( auto id : op.common_options.whitelist_authorities ) d.get_object(id); @@ -87,8 +100,6 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o auto asset_symbol_itr = asset_indx.find( op.symbol ); FC_ASSERT( asset_symbol_itr == asset_indx.end() ); - // Define now from the current block time - const time_point_sec now = d.head_block_time(); // This must remain due to "BOND.CNY" being allowed before this HF if( now > HARDFORK_385_TIME ) { @@ -123,19 +134,12 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval ); } - FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_BSIP87_TIME - || !op.common_options.extensions.value.force_settle_fee_percent.valid(), - "A BitAsset's FSFP cannot be set before Hardfork BSIP87" ); - if( op.is_prediction_market ) { FC_ASSERT( op.bitasset_opts ); FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision ); } - // Check the taker fee percent - detail::check_asset_options_hf_bsip81(now, op.common_options); - return void_result(); } FC_CAPTURE_AND_RETHROW( (op) ) } @@ -299,6 +303,11 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) const database& d = db(); const time_point_sec now = d.head_block_time(); + // Hardfork Checks: + detail::check_asset_options_hf_1774(now, o.new_options); + detail::check_asset_options_hf_bsip81(now, o.new_options); + detail::check_asset_options_hf_bsip87(now, o.new_options); // HF_REMOVABLE + const asset_object& a = o.asset_to_update(d); auto a_copy = a; a_copy.options = o.new_options; @@ -311,8 +320,6 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) validate_new_issuer( d, a, *o.new_issuer ); } - detail::check_asset_options_hf_1774(d.head_block_time(), o.new_options); - if( a.dynamic_asset_data_id(d).current_supply != 0 ) { // new issuer_permissions must be subset of old issuer permissions @@ -329,10 +336,6 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) "Incorrect issuer for asset! (${o.issuer} != ${a.issuer})", ("o.issuer", o.issuer)("a.issuer", a.issuer) ); - FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_BSIP87_TIME - || !o.new_options.extensions.value.force_settle_fee_percent.valid(), - "A BitAsset's FSFP cannot be set before Hardfork BSIP87" ); - const auto& chain_parameters = d.get_global_properties().parameters; FC_ASSERT( o.new_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); @@ -342,9 +345,6 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) for( auto id : o.new_options.blacklist_authorities ) d.get_object(id); - // Check the taker fee percent - detail::check_asset_options_hf_bsip81(now, o.new_options); - return void_result(); } FC_CAPTURE_AND_RETHROW((o)) } diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 86ba4c7408..b7bbb60405 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1011,9 +1011,6 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a auto total_collateral_denominated_fees = market_fees + force_settle_fees; - // TODO: Do we need a something-for-nothing check here? Or does rounding guarantee fees - // strictly less than receives? - // If we don't consume entire settle order: if( pays < settle.balance ) { @@ -1027,7 +1024,8 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a adjust_balance(settle.owner, receives - total_collateral_denominated_fees); assert( pays.asset_id != receives.asset_id ); - push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives, total_collateral_denominated_fees, fill_price, is_maker ) ); + push_applied_operation( fill_order_operation( settle.id, settle.owner, pays, receives, + total_collateral_denominated_fees, fill_price, is_maker ) ); if (filled) remove(settle); @@ -1401,7 +1399,7 @@ asset database::pay_market_fees(const account_object* seller, const asset_object /*** * @brief Calculate force-settlement fee and give it to issuer of the settled asset - * @param collecting_asset the smart asset object which should receive the the fee + * @param collecting_asset the smart asset object which should receive the fee * @param collat_receives the amount of collateral the settler would expect to receive absent this fee * (fee is computed as a percentage of this amount) * @return asset denoting the amount of fee collected @@ -1414,7 +1412,9 @@ asset database::pay_force_settle_fees(const asset_object& collecting_asset, cons if( !collecting_asset.options.extensions.value.force_settle_fee_percent.valid() || *collecting_asset.options.extensions.value.force_settle_fee_percent == 0 ) return collat_asset.amount(0); - auto value = detail::calculate_percent(collat_receives.amount, *collecting_asset.options.extensions.value.force_settle_fee_percent); + + auto value = detail::calculate_percent(collat_receives.amount, + *collecting_asset.options.extensions.value.force_settle_fee_percent); asset settle_fee = collat_asset.amount(value); // Deposit fee in asset's dynamic data object: diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index d5a244787b..179eeea53a 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -29,9 +29,12 @@ namespace graphene { namespace chain { namespace detail { - void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options); + void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options); void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options); - void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op); + void check_asset_options_hf_bsip87(const fc::time_point_sec& block_time, + const asset_options& options); // HF_REMOVABLE + void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, + const asset_claim_fees_operation& op); // HF_REMOVABLE } struct proposal_operation_hardfork_visitor @@ -48,22 +51,25 @@ struct proposal_operation_hardfork_visitor void operator()(const T &v) const {} void operator()(const graphene::chain::asset_create_operation &v) const { - // hf_1774 - detail::check_asset_options_hf_1774(block_time, v.common_options); - // HARDFORK_BSIP_81 + detail::check_asset_options_hf_1774(block_time, v.common_options); detail::check_asset_options_hf_bsip81(block_time, v.common_options); + detail::check_asset_options_hf_bsip87(block_time, v.common_options); // HF_REMOVABLE + } + void operator()(const graphene::chain::asset_update_operation &v) const { - // hf_1774 - detail::check_asset_options_hf_1774(block_time, v.new_options); - // HARDFORK_BSIP_81 + detail::check_asset_options_hf_1774(block_time, v.new_options); detail::check_asset_options_hf_bsip81(block_time, v.new_options); + detail::check_asset_options_hf_bsip87(block_time, v.new_options); // HF_REMOVABLE + } void operator()(const graphene::chain::asset_claim_fees_operation &v) const { + detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE + } void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const { diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 34c6aea006..b42fa1ed60 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -563,7 +563,8 @@ FC_REFLECT( graphene::protocol::bitasset_options, (extensions) ) -FC_REFLECT( graphene::protocol::additional_asset_options, (reward_percent)(whitelist_market_fee_sharing)(taker_fee_percent)(force_settle_fee_percent) ) +FC_REFLECT( graphene::protocol::additional_asset_options, + (reward_percent)(whitelist_market_fee_sharing)(taker_fee_percent)(force_settle_fee_percent) ) FC_REFLECT( graphene::protocol::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) ) FC_REFLECT( graphene::protocol::asset_global_settle_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::protocol::asset_settle_operation::fee_parameters_type, (fee) ) From fbd832b4508518b728b05d1dcf7cbbd53785acba Mon Sep 17 00:00:00 2001 From: christophersanborn <23085117+christophersanborn@users.noreply.github.com> Date: Thu, 7 May 2020 18:12:46 -0400 Subject: [PATCH 6/6] Move force_settle_fee_percent from asset_options to bitasset_options --- libraries/chain/asset_evaluator.cpp | 18 +++++++++++------- libraries/chain/db_market.cpp | 13 +++++++------ libraries/chain/proposal_evaluator.cpp | 17 ++++++++++------- libraries/protocol/asset_ops.cpp | 6 ++++-- .../include/graphene/protocol/asset_ops.hpp | 9 ++++----- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 93982f9436..0781321eac 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -66,7 +66,7 @@ namespace detail { } } - void check_asset_options_hf_bsip87(const fc::time_point_sec& block_time, const asset_options& options) + void check_bitasset_options_hf_bsip87(const fc::time_point_sec& block_time, const bitasset_options& options) { // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes: FC_ASSERT( !options.extensions.value.force_settle_fee_percent.valid() @@ -94,7 +94,10 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o // Hardfork Checks: detail::check_asset_options_hf_1774(now, op.common_options); detail::check_asset_options_hf_bsip81(now, op.common_options); - detail::check_asset_options_hf_bsip87(now, op.common_options); // HF_REMOVABLE + if( op.bitasset_opts ) { + detail::check_bitasset_options_hf_bsip77( now, *op.bitasset_opts ); + detail::check_bitasset_options_hf_bsip87( now, *op.bitasset_opts ); // HF_REMOVABLE + } const auto& chain_parameters = d.get_global_properties().parameters; FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities ); @@ -128,7 +131,6 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o if( op.bitasset_opts ) { - detail::check_bitasset_options_hf_bsip77( now, *op.bitasset_opts ); const asset_object& backing = op.bitasset_opts->short_backing_asset(d); if( backing.is_market_issued() ) { @@ -317,7 +319,6 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) // Hardfork Checks: detail::check_asset_options_hf_1774(now, o.new_options); detail::check_asset_options_hf_bsip81(now, o.new_options); - detail::check_asset_options_hf_bsip87(now, o.new_options); // HF_REMOVABLE const asset_object& a = o.asset_to_update(d); auto a_copy = a; @@ -434,7 +435,7 @@ void_result asset_update_issuer_evaluator::do_apply(const asset_update_issuer_op * @param true if after hf 922/931 (if nothing triggers, this and the logic that depends on it * should be removed). */ -void check_children_of_bitasset(database& d, const asset_update_bitasset_operation& op, +void check_children_of_bitasset(const database& d, const asset_update_bitasset_operation& op, const asset_object& new_backing_asset) { // no need to do these checks if the new backing asset is CORE @@ -463,9 +464,12 @@ void check_children_of_bitasset(database& d, const asset_update_bitasset_operati void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bitasset_operation& op) { try { - database& d = db(); + const database& d = db(); + const time_point_sec now = d.head_block_time(); - detail::check_bitasset_options_hf_bsip77( d.head_block_time(), op.new_options ); + // Hardfork Checks: + detail::check_bitasset_options_hf_bsip77( now, op.new_options ); + detail::check_bitasset_options_hf_bsip87( now, op.new_options ); // HF_REMOVABLE const asset_object& asset_obj = op.asset_to_update(d); diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index b7bbb60405..c3a03fa9f3 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1408,14 +1408,15 @@ asset database::pay_force_settle_fees(const asset_object& collecting_asset, cons { FC_ASSERT( collecting_asset.get_id() != collat_receives.asset_id ); - const asset_object& collat_asset = get(collat_receives.asset_id); - if( !collecting_asset.options.extensions.value.force_settle_fee_percent.valid() - || *collecting_asset.options.extensions.value.force_settle_fee_percent == 0 ) - return collat_asset.amount(0); + const bitasset_options& collecting_bitasset_opts = collecting_asset.bitasset_data(*this).options; + + if( !collecting_bitasset_opts.extensions.value.force_settle_fee_percent.valid() + || *collecting_bitasset_opts.extensions.value.force_settle_fee_percent == 0 ) + return asset{ 0, collat_receives.asset_id }; auto value = detail::calculate_percent(collat_receives.amount, - *collecting_asset.options.extensions.value.force_settle_fee_percent); - asset settle_fee = collat_asset.amount(value); + *collecting_bitasset_opts.extensions.value.force_settle_fee_percent); + asset settle_fee = asset{ value, collat_receives.asset_id }; // Deposit fee in asset's dynamic data object: if( value > 0) { diff --git a/libraries/chain/proposal_evaluator.cpp b/libraries/chain/proposal_evaluator.cpp index 912a2ed2c0..d73e343759 100644 --- a/libraries/chain/proposal_evaluator.cpp +++ b/libraries/chain/proposal_evaluator.cpp @@ -32,8 +32,8 @@ namespace detail { void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options); void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time, const bitasset_options& options); void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options); - void check_asset_options_hf_bsip87(const fc::time_point_sec& block_time, - const asset_options& options); // HF_REMOVABLE + void check_bitasset_options_hf_bsip87(const fc::time_point_sec& block_time, + const bitasset_options& options); // HF_REMOVABLE void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op); // HF_REMOVABLE } @@ -54,10 +54,11 @@ struct proposal_operation_hardfork_visitor void operator()(const graphene::chain::asset_create_operation &v) const { detail::check_asset_options_hf_1774(block_time, v.common_options); - if( v.bitasset_opts.valid() ) - detail::check_bitasset_options_hf_bsip77( block_time, *v.bitasset_opts ); detail::check_asset_options_hf_bsip81(block_time, v.common_options); - detail::check_asset_options_hf_bsip87(block_time, v.common_options); // HF_REMOVABLE + if( v.bitasset_opts.valid() ) { + detail::check_bitasset_options_hf_bsip77( block_time, *v.bitasset_opts ); + detail::check_bitasset_options_hf_bsip87( block_time, *v.bitasset_opts ); // HF_REMOVABLE + } } @@ -65,12 +66,14 @@ struct proposal_operation_hardfork_visitor detail::check_asset_options_hf_1774(block_time, v.new_options); detail::check_asset_options_hf_bsip81(block_time, v.new_options); - detail::check_asset_options_hf_bsip87(block_time, v.new_options); // HF_REMOVABLE } + void operator()(const graphene::chain::asset_update_bitasset_operation &v) const { - // HARDFORK_BSIP_77 + detail::check_bitasset_options_hf_bsip77( block_time, v.new_options ); + detail::check_bitasset_options_hf_bsip87( block_time, v.new_options ); // HF_REMOVABLE + } void operator()(const graphene::chain::asset_claim_fees_operation &v) const { diff --git a/libraries/protocol/asset_ops.cpp b/libraries/protocol/asset_ops.cpp index 24318199ab..812df9b98c 100644 --- a/libraries/protocol/asset_ops.cpp +++ b/libraries/protocol/asset_ops.cpp @@ -216,6 +216,10 @@ void bitasset_options::validate() const FC_ASSERT( *extensions.value.initial_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO ); FC_ASSERT( *extensions.value.initial_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO ); } + + if( extensions.value.force_settle_fee_percent.valid() ) + FC_ASSERT( *extensions.value.force_settle_fee_percent <= GRAPHENE_100_PERCENT ); + } void asset_options::validate()const @@ -252,8 +256,6 @@ void asset_options::validate()const } if( extensions.value.reward_percent.valid() ) FC_ASSERT( *extensions.value.reward_percent <= GRAPHENE_100_PERCENT ); - if( extensions.value.force_settle_fee_percent.valid() ) - FC_ASSERT( *extensions.value.force_settle_fee_percent <= GRAPHENE_100_PERCENT ); } void asset_claim_fees_operation::validate()const { diff --git a/libraries/protocol/include/graphene/protocol/asset_ops.hpp b/libraries/protocol/include/graphene/protocol/asset_ops.hpp index 19f8fc6e6a..fd4c53297c 100644 --- a/libraries/protocol/include/graphene/protocol/asset_ops.hpp +++ b/libraries/protocol/include/graphene/protocol/asset_ops.hpp @@ -34,8 +34,6 @@ namespace graphene { namespace protocol { fc::optional> whitelist_market_fee_sharing; // After BSIP81 activation, taker_fee_percent is the taker fee fc::optional taker_fee_percent; - // MERGE NOTE: BSIP-74 additions here - fc::optional force_settle_fee_percent; // BSIP-87 }; typedef extension additional_asset_options_t; @@ -108,7 +106,8 @@ namespace graphene { namespace protocol { /// After BSIP77, when creating a new debt position or updating an existing position, /// the position will be checked against this parameter. /// Unused for prediction markets, although we allow it to be set for simpler implementation - fc::optional initial_collateral_ratio; + fc::optional initial_collateral_ratio; // BSIP-77 + fc::optional force_settle_fee_percent; // BSIP-87 }; /// Time before a price feed expires @@ -564,7 +563,7 @@ FC_REFLECT( graphene::protocol::asset_options, (extensions) ) -FC_REFLECT( graphene::protocol::bitasset_options::ext, (initial_collateral_ratio) ) +FC_REFLECT( graphene::protocol::bitasset_options::ext, (initial_collateral_ratio)(force_settle_fee_percent) ) FC_REFLECT( graphene::protocol::bitasset_options, (feed_lifetime_sec) @@ -577,7 +576,7 @@ FC_REFLECT( graphene::protocol::bitasset_options, ) FC_REFLECT( graphene::protocol::additional_asset_options, - (reward_percent)(whitelist_market_fee_sharing)(taker_fee_percent)(force_settle_fee_percent) ) + (reward_percent)(whitelist_market_fee_sharing)(taker_fee_percent) ) FC_REFLECT( graphene::protocol::asset_create_operation::fee_parameters_type, (symbol3)(symbol4)(long_symbol)(price_per_kbyte) )