Skip to content

Commit

Permalink
Defer MCR update to next maintenance interval #935
Browse files Browse the repository at this point in the history
  • Loading branch information
abitmore committed May 23, 2018
1 parent f87aaa4 commit 5e28975
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 98 deletions.
13 changes: 7 additions & 6 deletions libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ static bool update_bitasset_object_options(
const asset_update_bitasset_operation& op, database& db,
asset_bitasset_data_object& bdo, const asset_object& asset_to_update )
{
const fc::time_point_sec& next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;
fc::time_point_sec next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;
bool after_hf_core_868_890 = ( next_maint_time > HARDFORK_CORE_868_890_TIME );

// If the minimum number of feeds to calculate a median has changed, we need to recalculate the median
Expand Down Expand Up @@ -464,7 +464,7 @@ static bool update_bitasset_object_options(
if( should_update_feeds )
{
const auto old_feed = bdo.current_feed;
bdo.update_median_feeds( db.head_block_time() );
bdo.update_median_feeds( db.head_block_time(), next_maint_time );

// TODO review and refactor / cleanup after hard fork:
// 1. if hf_core_868_890 and core-935 occurred at same time
Expand Down Expand Up @@ -534,7 +534,8 @@ void_result asset_update_feed_producers_evaluator::do_evaluate(const asset_updat

void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_feed_producers_evaluator::operation_type& o)
{ try {
db().modify(*bitasset_to_update, [&](asset_bitasset_data_object& a) {
database& d = db();
d.modify(*bitasset_to_update, [&d, &o](asset_bitasset_data_object& a) {
//This is tricky because I have a set of publishers coming in, but a map of publisher to feed is stored.
//I need to update the map such that the keys match the new publishers, but not munge the old price feeds from
//publishers who are being kept.
Expand All @@ -550,9 +551,9 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f
for( auto itr = o.new_feed_producers.begin(); itr != o.new_feed_producers.end(); ++itr )
if( !a.feeds.count(*itr) )
a.feeds[*itr];
a.update_median_feeds(db().head_block_time());
a.update_median_feeds( d.head_block_time(), d.get_dynamic_global_properties().next_maintenance_time );
});
db().check_call_orders( o.asset_to_update(db()) );
d.check_call_orders( o.asset_to_update(d) );

return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
Expand Down Expand Up @@ -727,7 +728,7 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
// Store medians for this asset
d.modify(bad , [&o,&d](asset_bitasset_data_object& a) {
a.feeds[o.publisher] = make_pair(d.head_block_time(), o.feed);
a.update_median_feeds(d.head_block_time());
a.update_median_feeds( d.head_block_time(), d.get_dynamic_global_properties().next_maintenance_time );
});

if( !(old_feed == bad.current_feed) )
Expand Down
34 changes: 22 additions & 12 deletions libraries/chain/asset_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/hardfork.hpp>

#include <fc/uint128.hpp>

Expand All @@ -43,15 +44,8 @@ share_type asset_bitasset_data_object::max_force_settlement_volume(share_type cu
return volume.to_uint64();
}

/******
* @brief calculate the median feed
*
* This calculates the median feed. It sets the current_feed_publication_time
* and current_feed member variables
*
* @param current_time the time to use in the calculations
*/
void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point_sec current_time)
void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_point_sec current_time,
time_point_sec next_maintenance_time )
{
current_feed_publication_time = current_time;
vector<std::reference_wrapper<const price_feed>> current_feeds;
Expand All @@ -71,12 +65,14 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point
{
//... don't calculate a median, and set a null feed
current_feed_publication_time = current_time;
current_feed = price_feed();
price_feed null_feed;
update_current_feed( null_feed, next_maintenance_time );
return;
}
if( current_feeds.size() == 1 )
{
current_feed = std::move(current_feeds.front());
price_feed only_feed = std::move( current_feeds.front() );
update_current_feed( only_feed, next_maintenance_time );
return;
}

Expand All @@ -94,7 +90,21 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds(time_point
#undef CALCULATE_MEDIAN_VALUE
// *** End Median Calculations ***

current_feed = median_feed;
update_current_feed( median_feed, next_maintenance_time );
}

void graphene::chain::asset_bitasset_data_object::update_current_feed( price_feed& new_feed,
time_point_sec next_maintenance_time )
{
if( next_maintenance_time > HARDFORK_CORE_935_TIME )
{
if( current_feed.maintenance_collateral_ratio == new_feed.maintenance_collateral_ratio )
pending_maintenance_collateral_ratio.reset();
else
pending_maintenance_collateral_ratio = new_feed.maintenance_collateral_ratio;
new_feed.maintenance_collateral_ratio = current_feed.maintenance_collateral_ratio;
}
current_feed = std::move( new_feed );
}


Expand Down
140 changes: 68 additions & 72 deletions libraries/chain/db_maint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,38 +766,79 @@ void database::process_bids( const asset_bitasset_data_object& bad )
_cancel_bids_and_revive_mpa( to_revive, bad );
}

void update_and_match_call_orders( database& db )
/**
* @brief Update and match call orders
* @param db The database
* @param bitasset set to `nullptr` to update all call orders, otherwise only update call orders of this asset
*/
void update_and_match_call_orders( database& db, const asset_bitasset_data_object* bitasset = nullptr )
{
if( !bitasset )
wlog( "Updating all call orders for hardfork core-343/935 at block ${n}", ("n",db.head_block_num()) );

// Update call_price
wlog( "Updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) );
asset_id_type current_asset;
const asset_bitasset_data_object* abd = nullptr;
asset_id_type current_asset = bitasset ? bitasset->asset_id : asset_id_type();
const asset_bitasset_data_object* abd = bitasset;

// by_collateral index won't change after call_price updated, so it's safe to iterate
for( const auto& call_obj : db.get_index_type<call_order_index>().indices().get<by_collateral>() )
const auto& call_idx = db.get_index_type<call_order_index>().indices().get<by_collateral>();
auto citr = abd ? call_idx.lower_bound( price::min( abd->options.short_backing_asset, current_asset ) )
: call_idx.begin();
auto cend = abd ? call_idx.upper_bound( price::max( abd->options.short_backing_asset, current_asset ) )
: call_idx.end();
while( citr != cend )
{
if( current_asset != call_obj.debt_type() ) // debt type won't be asset_id_type(), abd will always get initialized
{
const call_order_object& call_obj = *citr;
++citr;
if( !bitasset && current_asset != call_obj.debt_type() ) // only do this when updating all call orders
{ // debt type won't be asset_id_type(), so abd will always get initialized
current_asset = call_obj.debt_type();
abd = &current_asset(db).bitasset_data(db);
}
if( !abd || abd->is_prediction_market ) // nothing to do with PM's; check !abd just to be safe
continue;
// note: to be consistent with `call_order_update_evaluator::do_apply()`, process prediction markets as well
db.modify( call_obj, [abd]( call_order_object& call ) {
call.call_price = price::call_price( call.get_debt(), call.get_collateral(),
abd->current_feed.maintenance_collateral_ratio );
});
}

// Match call orders
const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();
auto itr = asset_idx.lower_bound( true /** market issued */ );
while( itr != asset_idx.end() )
if( bitasset )
db.check_call_orders( current_asset(db), true, false ); // allow black swan, and call orders are taker
else
{
const asset_object& a = *itr;
++itr;
// be here, next_maintenance_time should have been updated already
db.check_call_orders( a, true, false ); // allow black swan, and call orders are taker
const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();
auto itr = asset_idx.lower_bound( true /** market issued */ );
while( itr != asset_idx.end() )
{
const asset_object& a = *itr;
++itr;
// be here, next_maintenance_time should have been updated already
db.check_call_orders( a, true, false ); // allow black swan, and call orders are taker
}
wlog( "Done updating all call orders for hardfork core-343/935 at block ${n}", ("n",db.head_block_num()) );
}
}

/**
* @brief Process pending_maintenance_collateral_ratio data for all MPA's
* @param db The database
*/
void process_pending_mcr( database& db )
{
const auto& bitasset_idx = db.get_index_type<asset_bitasset_data_index>().indices().get<by_pending_mcr>();
auto ba_itr = bitasset_idx.upper_bound( optional<uint16_t>() ); // has something pending
while( ba_itr != bitasset_idx.end() )
{
const asset_bitasset_data_object& abd = *ba_itr;
++ba_itr;
db.modify( abd, []( asset_bitasset_data_object& abdo )
{
abdo.current_feed.maintenance_collateral_ratio = *abdo.pending_maintenance_collateral_ratio;
abdo.pending_maintenance_collateral_ratio.reset();
});
update_and_match_call_orders( db, &abd );
}
wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) );
}

void database::process_bitassets()
Expand Down Expand Up @@ -853,11 +894,12 @@ void database::process_bitassets()
*
* @param db the database
* @param skip_check_call_orders true if check_call_orders() should not be called
* @param next_maintenance_time the next maintenance time
*/
// TODO: for better performance, this function can be removed if it actually updated nothing at hf time.
// * Also need to update related test cases
// * NOTE: the removal can't be applied to testnet
void process_hf_868_890( database& db, bool skip_check_call_orders )
void process_hf_868_890( database& db, bool skip_check_call_orders, time_point_sec next_maintenance_time )
{
auto head_time = db.head_block_time();
// for each market issued asset
Expand Down Expand Up @@ -916,8 +958,8 @@ void process_hf_868_890( database& db, bool skip_check_call_orders )
}

// always update the median feed due to https://github.com/bitshares/bitshares-core/issues/890
db.modify( bitasset_data, [&head_time]( asset_bitasset_data_object &obj ) {
obj.update_median_feeds( head_time );
db.modify( bitasset_data, [head_time,next_maintenance_time]( asset_bitasset_data_object &obj ) {
obj.update_median_feeds( head_time, next_maintenance_time );
});

bool median_changed = ( old_feed.settlement_price != bitasset_data.current_feed.settlement_price );
Expand All @@ -939,52 +981,6 @@ void process_hf_868_890( database& db, bool skip_check_call_orders )
} // for each market issued asset
}

/******
* @brief one-time data process for hard fork core-935
*
* Prior to hardfork 935, `check_call_orders` may be unintendedly skipped when
* median price feed has changed. This method will run at the hardfork time, and
* call `check_call_orders` for all markets.
* https://github.com/bitshares/bitshares-core/issues/935
*
* @param db the database
*/
// TODO: for better performance, this function can be removed if it actually updated nothing at hf time.
// * Also need to update related test cases
// * NOTE: perhaps the removal can't be applied to testnet
void process_hf_935( database& db )
{
bool changed_something = false;
const asset_bitasset_data_object* bitasset = nullptr;
bool settled_before_check_call;
bool settled_after_check_call;
// for each market issued asset
const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();
for( auto asset_itr = asset_idx.lower_bound(true); asset_itr != asset_idx.end(); ++asset_itr )
{
const auto& current_asset = *asset_itr;

if( !changed_something )
{
bitasset = &current_asset.bitasset_data( db );
settled_before_check_call = bitasset->has_settlement(); // whether already force settled
}

bool called_some = db.check_call_orders( current_asset );

if( !changed_something )
{
settled_after_check_call = bitasset->has_settlement(); // whether already force settled

if( settled_before_check_call != settled_after_check_call || called_some )
{
changed_something = true;
wlog( "process_hf_935 changed something" );
}
}
}
}

void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
{
const auto& gpo = get_global_properties();
Expand Down Expand Up @@ -1138,17 +1134,17 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g

// To reset call_price of all call orders, then match by new rule
bool to_update_and_match_call_orders = false;
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_343_TIME) && (next_maintenance_time > HARDFORK_CORE_343_TIME) )
if( ( dgpo.next_maintenance_time <= HARDFORK_CORE_343_TIME && next_maintenance_time > HARDFORK_CORE_343_TIME )
|| ( dgpo.next_maintenance_time <= HARDFORK_CORE_935_TIME && next_maintenance_time > HARDFORK_CORE_935_TIME ) )
to_update_and_match_call_orders = true;

// Process inconsistent price feeds
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_868_890_TIME) && (next_maintenance_time > HARDFORK_CORE_868_890_TIME) )
process_hf_868_890( *this, to_update_and_match_call_orders );
process_hf_868_890( *this, to_update_and_match_call_orders, next_maintenance_time );

// Explicitly call check_call_orders of all markets
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_935_TIME) && (next_maintenance_time > HARDFORK_CORE_935_TIME)
&& !to_update_and_match_call_orders )
process_hf_935( *this );
// Process pending_maitenance_collateral_ratio data for all MPA's
if( dgpo.next_maintenance_time > HARDFORK_CORE_935_TIME )
process_pending_mcr( *this );

modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {
d.next_maintenance_time = next_maintenance_time;
Expand Down
12 changes: 7 additions & 5 deletions libraries/chain/db_update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,8 @@ void database::clear_expired_orders()

void database::update_expired_feeds()
{
auto head_time = head_block_time();
auto next_maint_time = get_dynamic_global_properties().next_maintenance_time;
auto& asset_idx = get_index_type<asset_index>().indices().get<by_type>();
auto itr = asset_idx.lower_bound( true /** market issued */ );
while( itr != asset_idx.end() )
Expand All @@ -482,14 +484,14 @@ void database::update_expired_feeds()

const asset_bitasset_data_object& b = a.bitasset_data(*this);
bool feed_is_expired;
if( head_block_time() < HARDFORK_615_TIME )
feed_is_expired = b.feed_is_expired_before_hardfork_615( head_block_time() );
if( head_time < HARDFORK_615_TIME )
feed_is_expired = b.feed_is_expired_before_hardfork_615( head_time );
else
feed_is_expired = b.feed_is_expired( head_block_time() );
feed_is_expired = b.feed_is_expired( head_time );
if( feed_is_expired )
{
modify(b, [this](asset_bitasset_data_object& a) {
a.update_median_feeds(head_block_time());
modify(b, [head_time,next_maint_time](asset_bitasset_data_object& a) {
a.update_median_feeds( head_time, next_maint_time );
});
check_call_orders(b.current_feed.settlement_price.base.asset_id(*this));
}
Expand Down
Loading

0 comments on commit 5e28975

Please sign in to comment.