Skip to content

Commit

Permalink
Merge pull request #2151 from christophersanborn/cjs-bsip87
Browse files Browse the repository at this point in the history
Implement BSIP87: Force-Settlement Fee
  • Loading branch information
abitmore authored May 7, 2020
2 parents 319fc3b + fbd832b commit fde2aa4
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 34 deletions.
45 changes: 29 additions & 16 deletions libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,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_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()
|| 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() ||
Expand All @@ -80,15 +89,20 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
{ try {

const database& d = db();
// Define now from the current block time
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);
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 );
FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );

detail::check_asset_options_hf_1774( now, op.common_options );

// Check that all authorities do exist
for( auto id : op.common_options.whitelist_authorities )
d.get_object(id);
Expand Down Expand Up @@ -117,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() )
{
Expand All @@ -133,15 +146,13 @@ 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 );
}

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) ) }

Expand Down Expand Up @@ -305,6 +316,10 @@ 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);

const asset_object& a = o.asset_to_update(d);
auto a_copy = a;
a_copy.options = o.new_options;
Expand All @@ -317,8 +332,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( now, o.new_options );

if( a.dynamic_asset_data_id(d).current_supply != 0 )
{
// new issuer_permissions must be subset of old issuer permissions
Expand All @@ -344,9 +357,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)) }

Expand Down Expand Up @@ -425,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
Expand Down Expand Up @@ -454,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);

Expand Down
72 changes: 68 additions & 4 deletions libraries/chain/db_market.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -963,22 +963,55 @@ 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, is_maker );

// 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 );

auto issuer_fees = pay_market_fees( settle_owner_ptr, get(receives.asset_id), receives, is_maker );
auto total_collateral_denominated_fees = market_fees + force_settle_fees;

// If we don't consume entire settle order:
if( pays < settle.balance )
{
modify(settle, [&pays](force_settlement_object& s) {
Expand All @@ -987,15 +1020,18 @@ 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) ) }

/**
Expand Down Expand Up @@ -1361,4 +1397,32 @@ asset database::pay_market_fees(const account_object* seller, const asset_object
return market_fees;
}

/***
* @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 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
*/
asset database::pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives)
{
FC_ASSERT( collecting_asset.get_id() != collat_receives.asset_id );

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_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) {
collecting_asset.accumulate_fee(*this, settle_fee);
}
return settle_fee;
}

} }
4 changes: 4 additions & 0 deletions libraries/chain/hardfork.d/CORE_BSIP87.hf
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions libraries/chain/include/graphene/chain/database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ namespace graphene { namespace chain {
asset calculate_market_fee(const asset_object& recv_asset, const asset& trade_amount, const bool& is_maker);
asset pay_market_fees(const account_object* seller, const asset_object& recv_asset, const asset& receives,
const bool& is_maker);
asset pay_force_settle_fees(const asset_object& collecting_asset, const asset& collat_receives);
///@}


Expand Down
31 changes: 19 additions & 12 deletions libraries/chain/proposal_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,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_bsip81(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_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec& block_time, const asset_claim_fees_operation& op);
void check_asset_options_hf_bsip81(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
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
Expand All @@ -49,30 +52,34 @@ 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_77
if( v.bitasset_opts.valid() )
detail::check_asset_options_hf_1774(block_time, v.common_options);
detail::check_asset_options_hf_bsip81(block_time, v.common_options);
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
}

// HARDFORK_BSIP_81
detail::check_asset_options_hf_bsip81(block_time, v.common_options);
}

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);

}

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 {

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 {
Expand Down
4 changes: 4 additions & 0 deletions libraries/protocol/asset_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions libraries/protocol/include/graphene/protocol/asset_ops.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,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<uint16_t> initial_collateral_ratio;
fc::optional<uint16_t> initial_collateral_ratio; // BSIP-77
fc::optional<uint16_t> force_settle_fee_percent; // BSIP-87
};

/// Time before a price feed expires
Expand Down Expand Up @@ -562,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)
Expand All @@ -579,6 +580,7 @@ FC_REFLECT( graphene::protocol::additional_asset_options,

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) )
FC_REFLECT( graphene::protocol::asset_settle_cancel_operation::fee_parameters_type, )
Expand Down

0 comments on commit fde2aa4

Please sign in to comment.