518 583 868 890 922 931 935 1270 hf fixes
This commit is contained in:
parent
93c4793d0a
commit
14a3e7d9e9
21 changed files with 1066 additions and 193 deletions
|
|
@ -481,7 +481,7 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o)
|
|||
database& d = db();
|
||||
|
||||
// If we are now disabling force settlements, cancel all open force settlement orders
|
||||
if( o.new_options.flags & disable_force_settle && asset_to_update->can_force_settle() )
|
||||
if( (o.new_options.flags & disable_force_settle) && asset_to_update->can_force_settle() )
|
||||
{
|
||||
const auto& idx = d.get_index_type<force_settlement_index>().indices().get<by_expiration>();
|
||||
// Funky iteration code because we're removing objects as we go. We have to re-initialize itr every loop instead
|
||||
|
|
@ -515,57 +515,366 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o)
|
|||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bitasset_operation& o)
|
||||
/****************
|
||||
* Loop through assets, looking for ones that are backed by the asset being changed. When found,
|
||||
* perform checks to verify validity
|
||||
*
|
||||
* @param d the database
|
||||
* @param op the bitasset update operation being performed
|
||||
* @param new_backing_asset
|
||||
* @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,
|
||||
const asset_object& new_backing_asset, bool after_hf_922_931)
|
||||
{
|
||||
// no need to do these checks if the new backing asset is CORE
|
||||
if ( new_backing_asset.get_id() == asset_id_type() )
|
||||
return;
|
||||
|
||||
// loop through all assets that have this asset as a backing asset
|
||||
const auto& idx = d.get_index_type<asset_index>().indices().get<by_type>();
|
||||
|
||||
for( auto itr = idx.lower_bound(true); itr != idx.end(); ++itr )
|
||||
{
|
||||
const auto& child = *itr;
|
||||
if ( child.bitasset_data(d).options.short_backing_asset == op.asset_to_update )
|
||||
{
|
||||
if ( after_hf_922_931 )
|
||||
{
|
||||
FC_ASSERT( child.get_id() != op.new_options.short_backing_asset,
|
||||
"A BitAsset would be invalidated by changing this backing asset ('A' backed by 'B' backed by 'A')." );
|
||||
|
||||
FC_ASSERT( child.issuer != GRAPHENE_COMMITTEE_ACCOUNT,
|
||||
"A blockchain-controlled market asset would be invalidated by changing this backing asset." );
|
||||
|
||||
FC_ASSERT( !new_backing_asset.is_market_issued(),
|
||||
"A non-blockchain controlled BitAsset would be invalidated by changing this backing asset.");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if( child.get_id() == op.new_options.short_backing_asset )
|
||||
{
|
||||
wlog( "Before hf-922-931, modified an asset to be backed by another, but would cause a continuous "
|
||||
"loop. A cannot be backed by B which is backed by A." );
|
||||
return;
|
||||
}
|
||||
|
||||
if( child.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
|
||||
{
|
||||
wlog( "before hf-922-931, modified an asset to be backed by a non-CORE, but this asset "
|
||||
"is a backing asset for a committee-issued asset. This occurred at block ${b}",
|
||||
("b", d.head_block_num()));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( new_backing_asset.is_market_issued() ) { // a.k.a. !UIA
|
||||
wlog( "before hf-922-931, modified an asset to be backed by an MPA, but this asset "
|
||||
"is a backing asset for another MPA, which would cause MPA backed by MPA backed by MPA. "
|
||||
"This occurred at block ${b}",
|
||||
("b", d.head_block_num()));
|
||||
return;
|
||||
}
|
||||
} // if child.issuer
|
||||
} // if hf 922/931
|
||||
} // if this child is backed by the asset being adjusted
|
||||
} // for each asset
|
||||
} // check_children_of_bitasset
|
||||
|
||||
void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bitasset_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
|
||||
const asset_object& a = o.asset_to_update(d);
|
||||
const asset_object& asset_obj = op.asset_to_update(d);
|
||||
|
||||
FC_ASSERT(a.is_market_issued(), "Cannot update BitAsset-specific settings on a non-BitAsset.");
|
||||
FC_ASSERT( asset_obj.is_market_issued(), "Cannot update BitAsset-specific settings on a non-BitAsset." );
|
||||
|
||||
const asset_bitasset_data_object& b = a.bitasset_data(d);
|
||||
FC_ASSERT( !b.has_settlement(), "Cannot update a bitasset after a settlement has executed" );
|
||||
if( o.new_options.short_backing_asset != b.options.short_backing_asset )
|
||||
FC_ASSERT( op.issuer == asset_obj.issuer, "Only asset issuer can update bitasset_data of the asset." );
|
||||
|
||||
const asset_bitasset_data_object& current_bitasset_data = asset_obj.bitasset_data(d);
|
||||
|
||||
FC_ASSERT( !current_bitasset_data.has_settlement(), "Cannot update a bitasset after a global settlement has executed" );
|
||||
|
||||
bool after_hf_core_922_931 = ( d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_922_931_TIME );
|
||||
|
||||
// Are we changing the backing asset?
|
||||
if( op.new_options.short_backing_asset != current_bitasset_data.options.short_backing_asset )
|
||||
{
|
||||
FC_ASSERT(a.dynamic_asset_data_id(d).current_supply == 0);
|
||||
FC_ASSERT(d.find_object(o.new_options.short_backing_asset));
|
||||
FC_ASSERT( asset_obj.dynamic_asset_data_id(d).current_supply == 0,
|
||||
"Cannot update a bitasset if there is already a current supply." );
|
||||
|
||||
if( a.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
|
||||
const asset_object& new_backing_asset = op.new_options.short_backing_asset(d); // check if the asset exists
|
||||
|
||||
if( after_hf_core_922_931 ) // TODO remove this check after hard fork if things in `else` did not occur
|
||||
{
|
||||
const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d);
|
||||
if( backing.is_market_issued() )
|
||||
FC_ASSERT( op.new_options.short_backing_asset != asset_obj.get_id(),
|
||||
"Cannot update an asset to be backed by itself." );
|
||||
|
||||
if( current_bitasset_data.is_prediction_market )
|
||||
{
|
||||
const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d);
|
||||
FC_ASSERT( backing_backing.get_id() == asset_id_type(),
|
||||
"May not create a blockchain-controlled market asset which is not backed by CORE.");
|
||||
} else
|
||||
FC_ASSERT( backing.get_id() == asset_id_type(),
|
||||
"May not create a blockchain-controlled market asset which is not backed by CORE.");
|
||||
FC_ASSERT( asset_obj.precision == new_backing_asset.precision,
|
||||
"The precision of the asset and backing asset must be equal." );
|
||||
}
|
||||
|
||||
if( asset_obj.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
|
||||
{
|
||||
if( new_backing_asset.is_market_issued() )
|
||||
{
|
||||
FC_ASSERT( new_backing_asset.bitasset_data(d).options.short_backing_asset == asset_id_type(),
|
||||
"May not modify a blockchain-controlled market asset to be backed by an asset which is not "
|
||||
"backed by CORE." );
|
||||
|
||||
check_children_of_bitasset( d, op, new_backing_asset, after_hf_core_922_931 );
|
||||
}
|
||||
else
|
||||
{
|
||||
FC_ASSERT( new_backing_asset.get_id() == asset_id_type(),
|
||||
"May not modify a blockchain-controlled market asset to be backed by an asset which is not "
|
||||
"market issued asset nor CORE." );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a committee issued asset
|
||||
|
||||
// If we're changing to a backing_asset that is not CORE, we need to look at any
|
||||
// asset ( "CHILD" ) that has this one as a backing asset. If CHILD is committee-owned,
|
||||
// the change is not allowed. If CHILD is user-owned, then this asset's backing
|
||||
// asset must be either CORE or a UIA.
|
||||
if ( new_backing_asset.get_id() != asset_id_type() ) // not backed by CORE
|
||||
{
|
||||
check_children_of_bitasset( d, op, new_backing_asset, after_hf_core_922_931 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if the new backing asset is itself backed by something. It must be CORE or a UIA
|
||||
if ( new_backing_asset.is_market_issued() )
|
||||
{
|
||||
const asset_object& backing_backing_asset = new_backing_asset.bitasset_data(d).asset_id(d);
|
||||
FC_ASSERT( !backing_backing_asset.is_market_issued(), "A BitAsset cannot be backed by a BitAsset that itself "
|
||||
"is backed by a BitAsset.");
|
||||
}
|
||||
}
|
||||
else // prior to HF 922 / 931
|
||||
{
|
||||
// code to check if issues occurred before hard fork. TODO cleanup after hard fork
|
||||
if( op.new_options.short_backing_asset == asset_obj.get_id() )
|
||||
{
|
||||
wlog( "before hf-922-931, op.new_options.short_backing_asset == asset_obj.get_id() at block ${b}",
|
||||
("b",d.head_block_num()) );
|
||||
}
|
||||
if( current_bitasset_data.is_prediction_market && asset_obj.precision != new_backing_asset.precision )
|
||||
{
|
||||
wlog( "before hf-922-931, for a PM, asset_obj.precision != new_backing_asset.precision at block ${b}",
|
||||
("b",d.head_block_num()) );
|
||||
}
|
||||
|
||||
if( asset_obj.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
|
||||
{
|
||||
// code to check if issues occurred before hard fork. TODO cleanup after hard fork
|
||||
if( new_backing_asset.is_market_issued() )
|
||||
{
|
||||
if( new_backing_asset.bitasset_data(d).options.short_backing_asset != asset_id_type() )
|
||||
wlog( "before hf-922-931, modified a blockchain-controlled market asset to be backed by an asset "
|
||||
"which is not backed by CORE at block ${b}",
|
||||
("b",d.head_block_num()) );
|
||||
|
||||
check_children_of_bitasset( d, op, new_backing_asset, after_hf_core_922_931 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( new_backing_asset.get_id() != asset_id_type() )
|
||||
wlog( "before hf-922-931, modified a blockchain-controlled market asset to be backed by an asset "
|
||||
"which is not market issued asset nor CORE at block ${b}",
|
||||
("b",d.head_block_num()) );
|
||||
}
|
||||
|
||||
//prior to HF 922_931, these checks were mistakenly using the old backing_asset
|
||||
const asset_object& old_backing_asset = current_bitasset_data.options.short_backing_asset(d);
|
||||
|
||||
if( old_backing_asset.is_market_issued() )
|
||||
{
|
||||
FC_ASSERT( old_backing_asset.bitasset_data(d).options.short_backing_asset == asset_id_type(),
|
||||
"May not modify a blockchain-controlled market asset to be backed by an asset which is not "
|
||||
"backed by CORE." );
|
||||
}
|
||||
else
|
||||
{
|
||||
FC_ASSERT( old_backing_asset.get_id() == asset_id_type(),
|
||||
"May not modify a blockchain-controlled market asset to be backed by an asset which is not "
|
||||
"market issued asset nor CORE." );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a committee issued asset
|
||||
|
||||
// If we're changing to a backing_asset that is not CORE, we need to look at any
|
||||
// asset ( "CHILD" ) that has this one as a backing asset. If CHILD is committee-owned,
|
||||
// the change is not allowed. If CHILD is user-owned, then this asset's backing
|
||||
// asset must be either CORE or a UIA.
|
||||
if ( new_backing_asset.get_id() != asset_id_type() ) // not backed by CORE
|
||||
{
|
||||
check_children_of_bitasset( d, op, new_backing_asset, after_hf_core_922_931 );
|
||||
}
|
||||
}
|
||||
// if the new backing asset is backed by something which is not CORE and not a UIA, this is not allowed
|
||||
// Check if the new backing asset is itself backed by something. It must be CORE or a UIA
|
||||
if ( new_backing_asset.is_market_issued() )
|
||||
{
|
||||
const asset_object& backing_backing_asset = new_backing_asset.bitasset_data(d).asset_id(d);
|
||||
if ( backing_backing_asset.get_id() != asset_id_type() )
|
||||
{
|
||||
if ( backing_backing_asset.is_market_issued() )
|
||||
{
|
||||
wlog( "before hf-922-931, a BitAsset cannot be backed by a BitAsset that itself "
|
||||
"is backed by a BitAsset. This occurred at block ${b}",
|
||||
("b", d.head_block_num() ) );
|
||||
}
|
||||
} // not core
|
||||
} // if market issued
|
||||
}
|
||||
}
|
||||
|
||||
bitasset_to_update = &b;
|
||||
FC_ASSERT( o.issuer == a.issuer, "", ("o.issuer", o.issuer)("a.issuer", a.issuer) );
|
||||
const auto& chain_parameters = d.get_global_properties().parameters;
|
||||
if( after_hf_core_922_931 ) // TODO remove this check after hard fork if things in `else` did not occur
|
||||
{
|
||||
FC_ASSERT( op.new_options.feed_lifetime_sec > chain_parameters.block_interval,
|
||||
"Feed lifetime must exceed block interval." );
|
||||
FC_ASSERT( op.new_options.force_settlement_delay_sec > chain_parameters.block_interval,
|
||||
"Force settlement delay must exceed block interval." );
|
||||
}
|
||||
else // code to check if issues occurred before hard fork. TODO cleanup after hard fork
|
||||
{
|
||||
if( op.new_options.feed_lifetime_sec <= chain_parameters.block_interval )
|
||||
wlog( "before hf-922-931, op.new_options.feed_lifetime_sec <= chain_parameters.block_interval at block ${b}",
|
||||
("b",d.head_block_num()) );
|
||||
if( op.new_options.force_settlement_delay_sec <= chain_parameters.block_interval )
|
||||
wlog( "before hf-922-931, op.new_options.force_settlement_delay_sec <= chain_parameters.block_interval at block ${b}",
|
||||
("b",d.head_block_num()) );
|
||||
}
|
||||
|
||||
bitasset_to_update = ¤t_bitasset_data;
|
||||
asset_to_update = &asset_obj;
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result asset_update_bitasset_evaluator::do_apply(const asset_update_bitasset_operation& o)
|
||||
{ try {
|
||||
bool should_update_feeds = false;
|
||||
/*******
|
||||
* @brief Apply requested changes to bitasset options
|
||||
*
|
||||
* This applies the requested changes to the bitasset object. It also cleans up the
|
||||
* releated feeds
|
||||
*
|
||||
* @param op the requested operation
|
||||
* @param db the database
|
||||
* @param bdo the actual database object
|
||||
* @param asset_to_update the asset_object related to this bitasset_data_object
|
||||
* @returns true if the feed price is changed, and after hf core-868-890
|
||||
*/
|
||||
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;
|
||||
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
|
||||
if( o.new_options.minimum_feeds != bitasset_to_update->options.minimum_feeds )
|
||||
bool should_update_feeds = false;
|
||||
if( op.new_options.minimum_feeds != bdo.options.minimum_feeds )
|
||||
should_update_feeds = true;
|
||||
|
||||
db().modify(*bitasset_to_update, [&](asset_bitasset_data_object& b) {
|
||||
b.options = o.new_options;
|
||||
// after hardfork core-868-890, we also should call update_median_feeds if the feed_lifetime_sec changed
|
||||
if( after_hf_core_868_890
|
||||
&& op.new_options.feed_lifetime_sec != bdo.options.feed_lifetime_sec )
|
||||
{
|
||||
should_update_feeds = true;
|
||||
}
|
||||
|
||||
if( should_update_feeds )
|
||||
b.update_median_feeds(db().head_block_time());
|
||||
});
|
||||
// feeds must be reset if the backing asset is changed after hardfork core-868-890
|
||||
bool backing_asset_changed = false;
|
||||
bool is_witness_or_committee_fed = false;
|
||||
if( after_hf_core_868_890
|
||||
&& op.new_options.short_backing_asset != bdo.options.short_backing_asset )
|
||||
{
|
||||
backing_asset_changed = true;
|
||||
should_update_feeds = true;
|
||||
if ( asset_to_update.options.flags & (witness_fed_asset | committee_fed_asset) )
|
||||
is_witness_or_committee_fed = true;
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
bdo.options = op.new_options;
|
||||
|
||||
// are we modifying the underlying? If so, reset the feeds
|
||||
if (backing_asset_changed)
|
||||
{
|
||||
if ( is_witness_or_committee_fed )
|
||||
{
|
||||
bdo.feeds.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// for non-witness-feeding and non-committee-feeding assets, modify all feeds
|
||||
// published by producers to nothing, since we can't simply remove them. For more information:
|
||||
// https://github.com/bitshares/bitshares-core/pull/832#issuecomment-384112633
|
||||
for(auto& current_feed : bdo.feeds)
|
||||
{
|
||||
current_feed.second.second.settlement_price = price();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( should_update_feeds )
|
||||
{
|
||||
const auto old_feed = bdo.current_feed;
|
||||
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
|
||||
// 2. if wlog did not actually get called
|
||||
|
||||
// We need to call check_call_orders if the price feed changes after hardfork core-935
|
||||
if( next_maint_time > HARDFORK_CORE_935_TIME )
|
||||
return ( !( old_feed == bdo.current_feed ) );
|
||||
|
||||
// We need to call check_call_orders if the settlement price changes after hardfork core-868-890
|
||||
if( after_hf_core_868_890 )
|
||||
{
|
||||
if( old_feed.settlement_price != bdo.current_feed.settlement_price )
|
||||
return true;
|
||||
else
|
||||
{
|
||||
if( !( old_feed == bdo.current_feed ) )
|
||||
wlog( "Settlement price did not change but current_feed changed at block ${b}", ("b",db.head_block_num()) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void_result asset_update_bitasset_evaluator::do_apply(const asset_update_bitasset_operation& op)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto& db_conn = db();
|
||||
const auto& asset_being_updated = (*asset_to_update);
|
||||
bool to_check_call_orders = false;
|
||||
|
||||
db_conn.modify( *bitasset_to_update,
|
||||
[&op, &asset_being_updated, &to_check_call_orders, &db_conn]( asset_bitasset_data_object& bdo )
|
||||
{
|
||||
to_check_call_orders = update_bitasset_object_options( op, db_conn, bdo, asset_being_updated );
|
||||
});
|
||||
|
||||
if( to_check_call_orders )
|
||||
db_conn.check_call_orders( asset_being_updated );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) )
|
||||
}
|
||||
|
||||
void_result asset_update_dividend_evaluator::do_evaluate(const asset_update_dividend_operation& o)
|
||||
{ try {
|
||||
|
|
@ -652,6 +961,7 @@ 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 {
|
||||
const auto next_maint_time = db().get_dynamic_global_properties().next_maintenance_time;
|
||||
db().modify(*bitasset_to_update, [&](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
|
||||
|
|
@ -668,7 +978,7 @@ 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(db().head_block_time(), next_maint_time);
|
||||
});
|
||||
db().check_call_orders( o.asset_to_update(db()) );
|
||||
|
||||
|
|
@ -835,27 +1145,48 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
|
|||
{ try {
|
||||
|
||||
database& d = db();
|
||||
const auto head_time = d.head_block_time();
|
||||
const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;
|
||||
|
||||
const asset_object& base = o.asset_id(d);
|
||||
const asset_bitasset_data_object& bad = base.bitasset_data(d);
|
||||
|
||||
auto old_feed = bad.current_feed;
|
||||
// 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());
|
||||
d.modify( bad , [&o,head_time,next_maint_time](asset_bitasset_data_object& a) {
|
||||
a.feeds[o.publisher] = make_pair( head_time, o.feed );
|
||||
a.update_median_feeds( head_time, next_maint_time );
|
||||
});
|
||||
|
||||
if( !(old_feed == bad.current_feed) )
|
||||
{
|
||||
if( bad.has_settlement() ) // implies head_block_time > HARDFORK_CORE_216_TIME
|
||||
// Check whether need to revive the asset and proceed if need
|
||||
if( bad.has_settlement() // has globally settled, implies head_block_time > HARDFORK_CORE_216_TIME
|
||||
&& !bad.current_feed.settlement_price.is_null() ) // has a valid feed
|
||||
{
|
||||
bool should_revive = false;
|
||||
const auto& mia_dyn = base.dynamic_asset_data_id(d);
|
||||
if( !bad.current_feed.settlement_price.is_null()
|
||||
&& ( mia_dyn.current_supply == 0
|
||||
|| ~price::call_price(asset(mia_dyn.current_supply, o.asset_id),
|
||||
asset(bad.settlement_fund, bad.options.short_backing_asset),
|
||||
bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price ) )
|
||||
if( mia_dyn.current_supply == 0 ) // if current supply is zero, revive the asset
|
||||
should_revive = true;
|
||||
else // if current supply is not zero, when collateral ratio of settlement fund is greater than MCR, revive the asset
|
||||
{
|
||||
if( next_maint_time <= HARDFORK_CORE_1270_TIME )
|
||||
{
|
||||
// before core-1270 hard fork, calculate call_price and compare to median feed
|
||||
if( ~price::call_price( asset(mia_dyn.current_supply, o.asset_id),
|
||||
asset(bad.settlement_fund, bad.options.short_backing_asset),
|
||||
bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price )
|
||||
should_revive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// after core-1270 hard fork, calculate collateralization and compare to maintenance_collateralization
|
||||
if( price( asset( bad.settlement_fund, bad.options.short_backing_asset ),
|
||||
asset( mia_dyn.current_supply, o.asset_id ) ) > bad.current_maintenance_collateralization )
|
||||
should_revive = true;
|
||||
}
|
||||
}
|
||||
if( should_revive )
|
||||
d.revive_bitasset(base);
|
||||
}
|
||||
db().check_call_orders(base);
|
||||
|
|
|
|||
|
|
@ -44,10 +44,13 @@ share_type asset_bitasset_data_object::max_force_settlement_volume(share_type cu
|
|||
return volume.to_uint64();
|
||||
}
|
||||
|
||||
void 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 )
|
||||
{
|
||||
bool after_core_hardfork_1270 = ( next_maintenance_time > HARDFORK_CORE_1270_TIME ); // call price caching issue
|
||||
current_feed_publication_time = current_time;
|
||||
vector<std::reference_wrapper<const price_feed>> current_feeds;
|
||||
// find feeds that were alive at current_time
|
||||
for( const pair<account_id_type, pair<time_point_sec,price_feed>>& f : feeds )
|
||||
{
|
||||
if( (current_time - f.second.first).to_seconds() < options.feed_lifetime_sec &&
|
||||
|
|
@ -65,13 +68,18 @@ void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time
|
|||
feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance
|
||||
current_feed_publication_time = current_time;
|
||||
current_feed = price_feed();
|
||||
if( after_core_hardfork_1270 )
|
||||
current_maintenance_collateralization = price();
|
||||
return;
|
||||
}
|
||||
if( current_feeds.size() == 1 )
|
||||
{
|
||||
if( current_feed.core_exchange_rate != current_feeds.front().get().core_exchange_rate )
|
||||
feed_cer_updated = true;
|
||||
current_feed = std::move(current_feeds.front());
|
||||
current_feed = current_feeds.front();
|
||||
// Note: perhaps can defer updating current_maintenance_collateralization for better performance
|
||||
if( after_core_hardfork_1270 )
|
||||
current_maintenance_collateralization = current_feed.maintenance_collateralization();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +100,9 @@ void asset_bitasset_data_object::update_median_feeds(time_point_sec current_time
|
|||
if( current_feed.core_exchange_rate != median_feed.core_exchange_rate )
|
||||
feed_cer_updated = true;
|
||||
current_feed = median_feed;
|
||||
// Note: perhaps can defer updating current_maintenance_collateralization for better performance
|
||||
if( after_core_hardfork_1270 )
|
||||
current_maintenance_collateralization = current_feed.maintenance_collateralization();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1939,7 +1939,9 @@ void database::perform_son_tasks()
|
|||
}
|
||||
}
|
||||
|
||||
void update_and_match_call_orders( database& db )
|
||||
/// Reset call_price of all call orders according to their remaining collateral and debt.
|
||||
/// Do not update orders of prediction markets because we're sure they're up to date.
|
||||
void update_call_orders_hf_343( database& db )
|
||||
{
|
||||
// Update call_price
|
||||
wlog( "Updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) );
|
||||
|
|
@ -1960,7 +1962,30 @@ void update_and_match_call_orders( database& db )
|
|||
abd->current_feed.maintenance_collateral_ratio );
|
||||
});
|
||||
}
|
||||
wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) );
|
||||
}
|
||||
|
||||
/// Reset call_price of all call orders to (1,1) since it won't be used in the future.
|
||||
/// Update PMs as well.
|
||||
void update_call_orders_hf_1270( database& db )
|
||||
{
|
||||
// Update call_price
|
||||
wlog( "Updating all call orders for hardfork core-1270 at block ${n}", ("n",db.head_block_num()) );
|
||||
for( const auto& call_obj : db.get_index_type<call_order_index>().indices().get<by_id>() )
|
||||
{
|
||||
db.modify( call_obj, []( call_order_object& call ) {
|
||||
call.call_price.base.amount = 1;
|
||||
call.call_price.quote.amount = 1;
|
||||
});
|
||||
}
|
||||
wlog( "Done updating all call orders for hardfork core-1270 at block ${n}", ("n",db.head_block_num()) );
|
||||
}
|
||||
|
||||
/// Match call orders for all bitAssets, including PMs.
|
||||
void match_call_orders( database& db )
|
||||
{
|
||||
// Match call orders
|
||||
wlog( "Matching call orders at block ${n}", ("n",db.head_block_num()) );
|
||||
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() )
|
||||
|
|
@ -1970,7 +1995,7 @@ void update_and_match_call_orders( database& db )
|
|||
// 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 at block ${n}", ("n",db.head_block_num()) );
|
||||
wlog( "Done matching call orders at block ${n}", ("n",db.head_block_num()) );
|
||||
}
|
||||
|
||||
void database::process_bids( const asset_bitasset_data_object& bad )
|
||||
|
|
@ -2024,6 +2049,216 @@ void database::process_bids( const asset_bitasset_data_object& bad )
|
|||
_cancel_bids_and_revive_mpa( to_revive, bad );
|
||||
}
|
||||
|
||||
void update_median_feeds(database& db)
|
||||
{
|
||||
time_point_sec head_time = db.head_block_time();
|
||||
time_point_sec next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;
|
||||
|
||||
const auto update_bitasset = [head_time, next_maint_time]( asset_bitasset_data_object &o )
|
||||
{
|
||||
o.update_median_feeds( head_time, next_maint_time );
|
||||
};
|
||||
|
||||
for( const auto& d : db.get_index_type<asset_bitasset_data_index>().indices() )
|
||||
{
|
||||
db.modify( d, update_bitasset );
|
||||
}
|
||||
}
|
||||
|
||||
/******
|
||||
* @brief one-time data process for hard fork core-868-890
|
||||
*
|
||||
* Prior to hardfork 868, switching a bitasset's shorting asset would not reset its
|
||||
* feeds. This method will run at the hardfork time, and erase (or nullify) feeds
|
||||
* that have incorrect backing assets.
|
||||
* https://github.com/bitshares/bitshares-core/issues/868
|
||||
*
|
||||
* Prior to hardfork 890, changing a bitasset's feed expiration time would not
|
||||
* trigger a median feed update. This method will run at the hardfork time, and
|
||||
* correct all median feed data.
|
||||
* https://github.com/bitshares/bitshares-core/issues/890
|
||||
*
|
||||
* @param db the database
|
||||
* @param skip_check_call_orders true if check_call_orders() should not be called
|
||||
*/
|
||||
// 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_868_890( database& db, bool skip_check_call_orders )
|
||||
{
|
||||
const auto next_maint_time = db.get_dynamic_global_properties().next_maintenance_time;
|
||||
const auto head_time = db.head_block_time();
|
||||
const auto head_num = db.head_block_num();
|
||||
wlog( "Processing hard fork core-868-890 at block ${n}", ("n",head_num) );
|
||||
// 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;
|
||||
// Incorrect witness & committee feeds can simply be removed.
|
||||
// For non-witness-fed and non-committee-fed assets, set incorrect
|
||||
// feeds to price(), since we can't simply remove them. For more information:
|
||||
// https://github.com/bitshares/bitshares-core/pull/832#issuecomment-384112633
|
||||
bool is_witness_or_committee_fed = false;
|
||||
if ( current_asset.options.flags & ( witness_fed_asset | committee_fed_asset ) )
|
||||
is_witness_or_committee_fed = true;
|
||||
|
||||
// for each feed
|
||||
const asset_bitasset_data_object& bitasset_data = current_asset.bitasset_data(db);
|
||||
// NOTE: We'll only need old_feed if HF343 hasn't rolled out yet
|
||||
auto old_feed = bitasset_data.current_feed;
|
||||
bool feeds_changed = false; // did any feed change
|
||||
auto itr = bitasset_data.feeds.begin();
|
||||
while( itr != bitasset_data.feeds.end() )
|
||||
{
|
||||
// If the feed is invalid
|
||||
if ( itr->second.second.settlement_price.quote.asset_id != bitasset_data.options.short_backing_asset
|
||||
&& ( is_witness_or_committee_fed || itr->second.second.settlement_price != price() ) )
|
||||
{
|
||||
feeds_changed = true;
|
||||
db.modify( bitasset_data, [&itr, is_witness_or_committee_fed]( asset_bitasset_data_object& obj )
|
||||
{
|
||||
if( is_witness_or_committee_fed )
|
||||
{
|
||||
// erase the invalid feed
|
||||
itr = obj.feeds.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// nullify the invalid feed
|
||||
obj.feeds[itr->first].second.settlement_price = price();
|
||||
++itr;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Feed is valid. Skip it.
|
||||
++itr;
|
||||
}
|
||||
} // end loop of each feed
|
||||
|
||||
// if any feed was modified, print a warning message
|
||||
if( feeds_changed )
|
||||
{
|
||||
wlog( "Found invalid feed for asset ${asset_sym} (${asset_id}) during hardfork core-868-890",
|
||||
("asset_sym", current_asset.symbol)("asset_id", current_asset.id) );
|
||||
}
|
||||
|
||||
// always update the median feed due to https://github.com/bitshares/bitshares-core/issues/890
|
||||
db.modify( bitasset_data, [head_time,next_maint_time]( asset_bitasset_data_object &obj ) {
|
||||
obj.update_median_feeds( head_time, next_maint_time );
|
||||
});
|
||||
|
||||
bool median_changed = ( old_feed.settlement_price != bitasset_data.current_feed.settlement_price );
|
||||
bool median_feed_changed = ( !( old_feed == bitasset_data.current_feed ) );
|
||||
if( median_feed_changed )
|
||||
{
|
||||
wlog( "Median feed for asset ${asset_sym} (${asset_id}) changed during hardfork core-868-890",
|
||||
("asset_sym", current_asset.symbol)("asset_id", current_asset.id) );
|
||||
}
|
||||
// Note: due to bitshares-core issue #935, the check below (using median_changed) is incorrect.
|
||||
// However, `skip_check_call_orders` will likely be true in both testnet and mainnet,
|
||||
// so effectively the incorrect code won't make a difference.
|
||||
// Additionally, we have code to update all call orders again during hardfork core-935
|
||||
// TODO cleanup after hard fork
|
||||
if( !skip_check_call_orders && median_changed ) // check_call_orders should be called
|
||||
{
|
||||
db.check_call_orders( current_asset );
|
||||
}
|
||||
else if( !skip_check_call_orders && median_feed_changed )
|
||||
{
|
||||
wlog( "Incorrectly skipped check_call_orders for asset ${asset_sym} (${asset_id}) during hardfork core-868-890",
|
||||
("asset_sym", current_asset.symbol)("asset_id", current_asset.id) );
|
||||
}
|
||||
} // for each market issued asset
|
||||
wlog( "Done processing hard fork core-868-890 at block ${n}", ("n",head_num) );
|
||||
}
|
||||
|
||||
/******
|
||||
* @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 = ¤t_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::process_bitassets()
|
||||
{
|
||||
time_point_sec head_time = head_block_time();
|
||||
uint32_t head_epoch_seconds = head_time.sec_since_epoch();
|
||||
bool after_hf_core_518 = ( head_time >= HARDFORK_CORE_518_TIME ); // clear expired feeds
|
||||
|
||||
const auto update_bitasset = [this,head_time,head_epoch_seconds,after_hf_core_518]( asset_bitasset_data_object &o )
|
||||
{
|
||||
o.force_settled_volume = 0; // Reset all BitAsset force settlement volumes to zero
|
||||
|
||||
// clear expired feeds
|
||||
if( after_hf_core_518 )
|
||||
{
|
||||
const auto &asset = get( o.asset_id );
|
||||
auto flags = asset.options.flags;
|
||||
if ( ( flags & ( witness_fed_asset | committee_fed_asset ) ) &&
|
||||
o.options.feed_lifetime_sec < head_epoch_seconds ) // if smartcoin && check overflow
|
||||
{
|
||||
fc::time_point_sec calculated = head_time - o.options.feed_lifetime_sec;
|
||||
for( auto itr = o.feeds.rbegin(); itr != o.feeds.rend(); ) // loop feeds
|
||||
{
|
||||
auto feed_time = itr->second.first;
|
||||
std::advance( itr, 1 );
|
||||
if( feed_time < calculated )
|
||||
o.feeds.erase( itr.base() ); // delete expired feed
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
|
||||
{
|
||||
modify( d, update_bitasset );
|
||||
if( d.has_settlement() )
|
||||
process_bids(d);
|
||||
}
|
||||
}
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{ try {
|
||||
const auto& gpo = get_global_properties();
|
||||
|
|
@ -2280,27 +2515,47 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) )
|
||||
deprecate_annual_members(*this);
|
||||
|
||||
// To reset call_price of all call orders, then match by new rule
|
||||
bool to_update_and_match_call_orders = false;
|
||||
// To reset call_price of all call orders, then match by new rule, for hard fork core-343
|
||||
bool to_update_and_match_call_orders_for_hf_343 = false;
|
||||
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_343_TIME) && (next_maintenance_time > HARDFORK_CORE_343_TIME) )
|
||||
to_update_and_match_call_orders = true;
|
||||
to_update_and_match_call_orders_for_hf_343 = 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_for_hf_343 );
|
||||
|
||||
// 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_for_hf_343 )
|
||||
process_hf_935( *this );
|
||||
|
||||
// To reset call_price of all call orders, then match by new rule, for hard fork core-1270
|
||||
bool to_update_and_match_call_orders_for_hf_1270 = false;
|
||||
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_1270_TIME) && (next_maintenance_time > HARDFORK_CORE_1270_TIME) )
|
||||
to_update_and_match_call_orders_for_hf_1270 = true;
|
||||
|
||||
modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {
|
||||
d.next_maintenance_time = next_maintenance_time;
|
||||
d.accounts_registered_this_interval = 0;
|
||||
});
|
||||
|
||||
// We need to do it after updated next_maintenance_time, to apply new rules here
|
||||
if( to_update_and_match_call_orders )
|
||||
update_and_match_call_orders(*this);
|
||||
|
||||
// Reset all BitAsset force settlement volumes to zero
|
||||
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
|
||||
// We need to do it after updated next_maintenance_time, to apply new rules here, for hard fork core-343
|
||||
if( to_update_and_match_call_orders_for_hf_343 )
|
||||
{
|
||||
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
|
||||
if( d.has_settlement() )
|
||||
process_bids(d);
|
||||
update_call_orders_hf_343(*this);
|
||||
match_call_orders(*this);
|
||||
}
|
||||
|
||||
// We need to do it after updated next_maintenance_time, to apply new rules here, for hard fork core-1270.
|
||||
if( to_update_and_match_call_orders_for_hf_1270 )
|
||||
{
|
||||
update_call_orders_hf_1270(*this);
|
||||
update_median_feeds(*this);
|
||||
match_call_orders(*this);
|
||||
}
|
||||
|
||||
process_bitassets();
|
||||
|
||||
// Ideally we have to do this after every block but that leads to longer block applicaiton/replay times.
|
||||
// So keep it here as it is not critical. valid_to check ensures
|
||||
// these custom account auths and account roles are not usable.
|
||||
|
|
|
|||
|
|
@ -43,12 +43,6 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
void database::globally_settle_asset( const asset_object& mia, const price& settlement_price )
|
||||
{ try {
|
||||
/*
|
||||
elog( "BLACK SWAN!" );
|
||||
debug_dump();
|
||||
edump( (mia.symbol)(settlement_price) );
|
||||
*/
|
||||
|
||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||
FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" );
|
||||
|
||||
|
|
@ -58,8 +52,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
const asset_dynamic_data_object& mia_dyn = mia.dynamic_asset_data_id(*this);
|
||||
auto original_mia_supply = mia_dyn.current_supply;
|
||||
|
||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
||||
const auto& call_price_index = call_index.indices().get<by_price>();
|
||||
const auto& call_price_index = get_index_type<call_order_index>().indices().get<by_price>();
|
||||
|
||||
auto maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding
|
||||
|
|
@ -90,9 +83,8 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
FC_ASSERT( fill_call_order( order, pays, order.get_debt(), settlement_price, true ) ); // call order is maker
|
||||
}
|
||||
|
||||
modify( bitasset, [&]( asset_bitasset_data_object& obj ){
|
||||
assert( collateral_gathered.asset_id == settlement_price.quote.asset_id );
|
||||
obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered; //settlement_price;
|
||||
modify( bitasset, [&mia,original_mia_supply,&collateral_gathered]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_price = mia.amount(original_mia_supply) / collateral_gathered;
|
||||
obj.settlement_fund = collateral_gathered.amount;
|
||||
});
|
||||
|
||||
|
|
@ -100,7 +92,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
/// that is a lie, the supply didn't change. We need to capture the current supply before
|
||||
/// filling all call orders and then restore it afterward. Then in the force settlement
|
||||
/// evaluator reduce the supply
|
||||
modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
|
||||
modify( mia_dyn, [original_mia_supply]( asset_dynamic_data_object& obj ){
|
||||
obj.current_supply = original_mia_supply;
|
||||
});
|
||||
|
||||
|
|
@ -174,14 +166,20 @@ void database::execute_bid( const collateral_bid_object& bid, share_type debt_co
|
|||
call.borrower = bid.bidder;
|
||||
call.collateral = bid.inv_swan_price.base.amount + collateral_from_fund;
|
||||
call.debt = debt_covered;
|
||||
call.call_price = price::call_price(asset(debt_covered, bid.inv_swan_price.quote.asset_id),
|
||||
asset(call.collateral, bid.inv_swan_price.base.asset_id),
|
||||
current_feed.maintenance_collateral_ratio);
|
||||
// don't calculate call_price after core-1270 hard fork
|
||||
if( get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_1270_TIME )
|
||||
// bid.inv_swan_price is in collateral / debt
|
||||
call.call_price = price( asset( 1, bid.inv_swan_price.base.asset_id ),
|
||||
asset( 1, bid.inv_swan_price.quote.asset_id ) );
|
||||
else
|
||||
call.call_price = price::call_price( asset(debt_covered, bid.inv_swan_price.quote.asset_id),
|
||||
asset(call.collateral, bid.inv_swan_price.base.asset_id),
|
||||
current_feed.maintenance_collateral_ratio );
|
||||
|
||||
});
|
||||
|
||||
if( bid.inv_swan_price.base.asset_id == asset_id_type() )
|
||||
modify(bid.bidder(*this).statistics(*this), [&](account_statistics_object& stats) {
|
||||
modify(get_account_stats_by_owner(bid.bidder), [&](account_statistics_object& stats) {
|
||||
stats.total_core_in_orders += call_obj.collateral;
|
||||
});
|
||||
|
||||
|
|
@ -429,6 +427,8 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo
|
|||
// 4. sell_asset has a valid price feed
|
||||
// 5. the call order's collateral ratio is below or equals to MCR
|
||||
// 6. the limit order provided a good price
|
||||
auto maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue
|
||||
bool to_check_call_orders = false;
|
||||
const asset_object& sell_asset = sell_asset_id( *this );
|
||||
//const asset_object& recv_asset = recv_asset_id( *this );
|
||||
|
|
@ -442,7 +442,10 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo
|
|||
&& !sell_abd->has_settlement()
|
||||
&& !sell_abd->current_feed.settlement_price.is_null() )
|
||||
{
|
||||
call_match_price = ~sell_abd->current_feed.max_short_squeeze_price();
|
||||
if( before_core_hardfork_1270 )
|
||||
call_match_price = ~sell_abd->current_feed.max_short_squeeze_price_before_hf_1270();
|
||||
else
|
||||
call_match_price = ~sell_abd->current_feed.max_short_squeeze_price();
|
||||
if( ~new_order_object.sell_price <= call_match_price ) // new limit order price is good enough to match a call
|
||||
to_check_call_orders = true;
|
||||
}
|
||||
|
|
@ -460,7 +463,33 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo
|
|||
finished = ( match( new_order_object, *old_limit_itr, old_limit_itr->sell_price ) != 2 );
|
||||
}
|
||||
|
||||
if( !finished )
|
||||
if( !finished && !before_core_hardfork_1270 ) // TODO refactor or cleanup duplicate code after core-1270 hard fork
|
||||
{
|
||||
// check if there are margin calls
|
||||
const auto& call_collateral_idx = get_index_type<call_order_index>().indices().get<by_collateral>();
|
||||
auto call_min = price::min( recv_asset_id, sell_asset_id );
|
||||
while( !finished )
|
||||
{
|
||||
// hard fork core-343 and core-625 took place at same time,
|
||||
// always check call order with least collateral ratio
|
||||
auto call_itr = call_collateral_idx.lower_bound( call_min );
|
||||
if( call_itr == call_collateral_idx.end()
|
||||
|| call_itr->debt_type() != sell_asset_id
|
||||
// feed protected https://github.com/cryptonomex/graphene/issues/436
|
||||
|| call_itr->collateralization() > sell_abd->current_maintenance_collateralization )
|
||||
break;
|
||||
// hard fork core-338 and core-625 took place at same time, not checking HARDFORK_CORE_338_TIME here.
|
||||
int match_result = match( new_order_object, *call_itr, call_match_price,
|
||||
sell_abd->current_feed.settlement_price,
|
||||
sell_abd->current_feed.maintenance_collateral_ratio,
|
||||
sell_abd->current_maintenance_collateralization );
|
||||
// match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching.
|
||||
// since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2.
|
||||
if( match_result == 1 || match_result == 3 )
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
else if( !finished ) // and before core-1270 hard fork
|
||||
{
|
||||
// check if there are margin calls
|
||||
const auto& call_price_idx = get_index_type<call_order_index>().indices().get<by_price>();
|
||||
|
|
@ -477,7 +506,8 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo
|
|||
// assume hard fork core-338 and core-625 will take place at same time, not checking HARDFORK_CORE_338_TIME here.
|
||||
int match_result = match( new_order_object, *call_itr, call_match_price,
|
||||
sell_abd->current_feed.settlement_price,
|
||||
sell_abd->current_feed.maintenance_collateral_ratio );
|
||||
sell_abd->current_feed.maintenance_collateral_ratio,
|
||||
optional<price>() );
|
||||
// match returns 1 or 3 when the new order was fully filled. In this case, we stop matching; otherwise keep matching.
|
||||
// since match can return 0 due to BSIP38 (hard fork core-834), we no longer only check if the result is 2.
|
||||
if( match_result == 1 || match_result == 3 )
|
||||
|
|
@ -583,7 +613,8 @@ int database::match( const limit_order_object& usd, const limit_order_object& co
|
|||
}
|
||||
|
||||
int database::match( const limit_order_object& bid, const call_order_object& ask, const price& match_price,
|
||||
const price& feed_price, const uint16_t maintenance_collateral_ratio )
|
||||
const price& feed_price, const uint16_t maintenance_collateral_ratio,
|
||||
const optional<price>& maintenance_collateralization )
|
||||
{
|
||||
FC_ASSERT( bid.sell_asset_id() == ask.debt_type() );
|
||||
FC_ASSERT( bid.receive_asset_id() == ask.collateral_type() );
|
||||
|
|
@ -607,7 +638,10 @@ int database::match( const limit_order_object& bid, const call_order_object& ask
|
|||
// TODO if we're sure `before_core_hardfork_834` is always false, remove the check
|
||||
asset usd_to_buy = ( before_core_hardfork_834 ?
|
||||
ask.get_debt() :
|
||||
asset( ask.get_max_debt_to_cover( match_price, feed_price, maintenance_collateral_ratio ),
|
||||
asset( ask.get_max_debt_to_cover( match_price,
|
||||
feed_price,
|
||||
maintenance_collateral_ratio,
|
||||
maintenance_collateralization ),
|
||||
ask.debt_type() ) );
|
||||
|
||||
asset call_pays, call_receives, order_pays, order_receives;
|
||||
|
|
@ -817,9 +851,9 @@ bool database::fill_call_order( const call_order_object& order, const asset& pay
|
|||
const price& fill_price, const bool is_maker )
|
||||
{ try {
|
||||
//idump((pays)(receives)(order));
|
||||
FC_ASSERT( order.get_debt().asset_id == receives.asset_id );
|
||||
FC_ASSERT( order.get_collateral().asset_id == pays.asset_id );
|
||||
FC_ASSERT( order.get_collateral() >= pays );
|
||||
FC_ASSERT( order.debt_type() == receives.asset_id );
|
||||
FC_ASSERT( order.collateral_type() == pays.asset_id );
|
||||
FC_ASSERT( order.collateral >= pays.amount );
|
||||
|
||||
const asset_object& mia = receives.asset_id(*this);
|
||||
FC_ASSERT( mia.is_market_issued() );
|
||||
|
|
@ -833,36 +867,39 @@ bool database::fill_call_order( const call_order_object& order, const asset& pay
|
|||
collateral_freed = o.get_collateral();
|
||||
o.collateral = 0;
|
||||
}
|
||||
else if( get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_343_TIME )
|
||||
o.call_price = price::call_price( o.get_debt(), o.get_collateral(),
|
||||
mia.bitasset_data(*this).current_feed.maintenance_collateral_ratio );
|
||||
else
|
||||
{
|
||||
auto maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
// update call_price after core-343 hard fork,
|
||||
// but don't update call_price after core-1270 hard fork
|
||||
if( maint_time <= HARDFORK_CORE_1270_TIME && maint_time > HARDFORK_CORE_343_TIME )
|
||||
{
|
||||
o.call_price = price::call_price( o.get_debt(), o.get_collateral(),
|
||||
mia.bitasset_data(*this).current_feed.maintenance_collateral_ratio );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const asset_dynamic_data_object& mia_ddo = mia.dynamic_asset_data_id(*this);
|
||||
|
||||
modify( mia_ddo, [&]( asset_dynamic_data_object& ao ){
|
||||
modify( mia_ddo, [&receives]( asset_dynamic_data_object& ao ){
|
||||
//idump((receives));
|
||||
ao.current_supply -= receives.amount;
|
||||
});
|
||||
|
||||
const account_object& borrower = order.borrower(*this);
|
||||
if( collateral_freed.valid() || pays.asset_id == asset_id_type() )
|
||||
// Adjust balance
|
||||
if( collateral_freed.valid() )
|
||||
adjust_balance( order.borrower, *collateral_freed );
|
||||
// Update account statistics. We know that order.collateral_type() == pays.asset_id
|
||||
if( pays.asset_id == asset_id_type() )
|
||||
{
|
||||
const account_statistics_object& borrower_statistics = borrower.statistics(*this);
|
||||
if( collateral_freed.valid() )
|
||||
adjust_balance(borrower.get_id(), *collateral_freed);
|
||||
|
||||
modify( borrower_statistics, [&]( account_statistics_object& b ){
|
||||
if( collateral_freed.valid() && collateral_freed->amount > 0 && collateral_freed->asset_id == asset_id_type())
|
||||
b.total_core_in_orders -= collateral_freed->amount;
|
||||
if( pays.asset_id == asset_id_type() )
|
||||
b.total_core_in_orders -= pays.amount;
|
||||
|
||||
assert( b.total_core_in_orders >= 0 );
|
||||
});
|
||||
modify( get_account_stats_by_owner(order.borrower), [&collateral_freed,&pays]( account_statistics_object& b ){
|
||||
b.total_core_in_orders -= pays.amount;
|
||||
if( collateral_freed.valid() )
|
||||
b.total_core_in_orders -= collateral_freed->amount;
|
||||
});
|
||||
}
|
||||
|
||||
assert( pays.asset_id != receives.asset_id );
|
||||
push_applied_operation( fill_order_operation( order.id, order.borrower, pays, receives,
|
||||
asset(0, pays.asset_id), fill_price, is_maker ) );
|
||||
|
||||
|
|
@ -908,6 +945,8 @@ bool database::fill_settle_order( const force_settlement_object& settle, const a
|
|||
* @param mia - the market issued asset that should be called.
|
||||
* @param enable_black_swan - when adjusting collateral, triggering a black swan is invalid and will throw
|
||||
* if enable_black_swan is not set to true.
|
||||
* @param for_new_limit_order - true if this function is called when matching call orders with a new limit order
|
||||
* @param bitasset_ptr - an optional pointer to the bitasset_data object of the asset
|
||||
*
|
||||
* @return true if a margin call was executed.
|
||||
*/
|
||||
|
|
@ -929,18 +968,17 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
if( bitasset.is_prediction_market ) return false;
|
||||
if( bitasset.current_feed.settlement_price.is_null() ) return false;
|
||||
|
||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
||||
const auto& call_price_index = call_index.indices().get<by_price>();
|
||||
|
||||
const limit_order_index& limit_index = get_index_type<limit_order_index>();
|
||||
const auto& limit_price_index = limit_index.indices().get<by_price>();
|
||||
|
||||
bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue
|
||||
|
||||
// looking for limit orders selling the most USD for the least CORE
|
||||
auto max_price = price::max( mia.id, bitasset.options.short_backing_asset );
|
||||
// stop when limit orders are selling too little USD for too much CORE
|
||||
auto min_price = bitasset.current_feed.max_short_squeeze_price();
|
||||
auto min_price = ( before_core_hardfork_1270 ? bitasset.current_feed.max_short_squeeze_price_before_hf_1270()
|
||||
: bitasset.current_feed.max_short_squeeze_price() );
|
||||
|
||||
assert( max_price.base.asset_id == min_price.base.asset_id );
|
||||
// NOTE limit_price_index is sorted from greatest to least
|
||||
auto limit_itr = limit_price_index.lower_bound( max_price );
|
||||
auto limit_end = limit_price_index.upper_bound( min_price );
|
||||
|
|
@ -948,10 +986,28 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
if( limit_itr == limit_end )
|
||||
return false;
|
||||
|
||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
||||
const auto& call_price_index = call_index.indices().get<by_price>();
|
||||
const auto& call_collateral_index = call_index.indices().get<by_collateral>();
|
||||
|
||||
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_itr = call_price_index.lower_bound( call_min );
|
||||
auto call_end = call_price_index.upper_bound( call_max );
|
||||
|
||||
auto call_price_itr = call_price_index.begin();
|
||||
auto call_price_end = call_price_itr;
|
||||
auto call_collateral_itr = call_collateral_index.begin();
|
||||
auto call_collateral_end = call_collateral_itr;
|
||||
|
||||
if( before_core_hardfork_1270 )
|
||||
{
|
||||
call_price_itr = call_price_index.lower_bound( call_min );
|
||||
call_price_end = call_price_index.upper_bound( call_max );
|
||||
}
|
||||
else
|
||||
{
|
||||
call_collateral_itr = call_collateral_index.lower_bound( call_min );
|
||||
call_collateral_end = call_collateral_index.upper_bound( call_max );
|
||||
}
|
||||
|
||||
bool filled_limit = false;
|
||||
bool margin_called = false;
|
||||
|
|
@ -969,34 +1025,32 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
bool before_core_hardfork_606 = ( maint_time <= HARDFORK_CORE_606_TIME ); // feed always trigger call
|
||||
bool before_core_hardfork_834 = ( maint_time <= HARDFORK_CORE_834_TIME ); // target collateral ratio option
|
||||
|
||||
while( !check_for_blackswan( mia, enable_black_swan, &bitasset ) && call_itr != call_end )
|
||||
while( !check_for_blackswan( mia, enable_black_swan, &bitasset ) // TODO perhaps improve performance by passing in iterators
|
||||
&& limit_itr != limit_end
|
||||
&& ( ( !before_core_hardfork_1270 && call_collateral_itr != call_collateral_end )
|
||||
|| ( before_core_hardfork_1270 && call_price_itr != call_price_end ) ) )
|
||||
{
|
||||
bool filled_call = false;
|
||||
price match_price;
|
||||
asset usd_for_sale;
|
||||
if( limit_itr != limit_end )
|
||||
{
|
||||
assert( limit_itr != limit_price_index.end() );
|
||||
match_price = limit_itr->sell_price;
|
||||
usd_for_sale = limit_itr->amount_for_sale();
|
||||
}
|
||||
else return margin_called;
|
||||
|
||||
match_price.validate();
|
||||
|
||||
const call_order_object& call_order = ( before_core_hardfork_1270 ? *call_price_itr : *call_collateral_itr );
|
||||
// Feed protected (don't call if CR>MCR) https://github.com/cryptonomex/graphene/issues/436
|
||||
if( after_hardfork_436 && ( bitasset.current_feed.settlement_price > ~call_itr->call_price ) )
|
||||
if( ( !before_core_hardfork_1270 && bitasset.current_maintenance_collateralization < call_order.collateralization() )
|
||||
|| ( before_core_hardfork_1270
|
||||
&& after_hardfork_436 && bitasset.current_feed.settlement_price > ~call_order.call_price ) )
|
||||
return margin_called;
|
||||
|
||||
const limit_order_object& limit_order = *limit_itr;
|
||||
price match_price = limit_order.sell_price;
|
||||
// There was a check `match_price.validate();` here, which is removed now because it always passes
|
||||
|
||||
// Old rule: margin calls can only buy high https://github.com/bitshares/bitshares-core/issues/606
|
||||
if( before_core_hardfork_606 && match_price > ~call_itr->call_price )
|
||||
if( before_core_hardfork_606 && match_price > ~call_order.call_price )
|
||||
return margin_called;
|
||||
|
||||
margin_called = true;
|
||||
|
||||
auto usd_to_buy = call_itr->get_debt();
|
||||
auto usd_to_buy = call_order.get_debt();
|
||||
|
||||
if( usd_to_buy * match_price > call_itr->get_collateral() )
|
||||
if( usd_to_buy * match_price > call_order.get_collateral() )
|
||||
{
|
||||
elog( "black swan detected on asset ${symbol} (${id}) at block ${b}",
|
||||
("id",mia.id)("symbol",mia.symbol)("b",head_num) );
|
||||
|
|
@ -1006,11 +1060,21 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
return true;
|
||||
}
|
||||
|
||||
if( !before_core_hardfork_834 )
|
||||
usd_to_buy.amount = call_itr->get_max_debt_to_cover( match_price,
|
||||
if( !before_core_hardfork_1270 )
|
||||
{
|
||||
usd_to_buy.amount = call_order.get_max_debt_to_cover( match_price,
|
||||
bitasset.current_feed.settlement_price,
|
||||
bitasset.current_feed.maintenance_collateral_ratio );
|
||||
bitasset.current_feed.maintenance_collateral_ratio,
|
||||
bitasset.current_maintenance_collateralization );
|
||||
}
|
||||
else if( !before_core_hardfork_834 )
|
||||
{
|
||||
usd_to_buy.amount = call_order.get_max_debt_to_cover( match_price,
|
||||
bitasset.current_feed.settlement_price,
|
||||
bitasset.current_feed.maintenance_collateral_ratio );
|
||||
}
|
||||
|
||||
asset usd_for_sale = limit_order.amount_for_sale();
|
||||
asset call_pays, call_receives, order_pays, order_receives;
|
||||
if( usd_to_buy > usd_for_sale )
|
||||
{ // fill order
|
||||
|
|
@ -1071,17 +1135,18 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
call_pays = order_receives;
|
||||
order_pays = call_receives;
|
||||
|
||||
auto old_call_itr = call_itr;
|
||||
if( filled_call && before_core_hardfork_343 )
|
||||
++call_itr;
|
||||
++call_price_itr;
|
||||
// when for_new_limit_order is true, the call order is maker, otherwise the call order is taker
|
||||
fill_call_order(*old_call_itr, call_pays, call_receives, match_price, for_new_limit_order );
|
||||
if( !before_core_hardfork_343 )
|
||||
call_itr = call_price_index.lower_bound( call_min );
|
||||
fill_call_order(call_order, call_pays, call_receives, match_price, for_new_limit_order );
|
||||
if( !before_core_hardfork_1270 )
|
||||
call_collateral_itr = call_collateral_index.lower_bound( call_min );
|
||||
else if( !before_core_hardfork_343 )
|
||||
call_price_itr = call_price_index.lower_bound( call_min );
|
||||
|
||||
auto next_limit_itr = std::next( limit_itr );
|
||||
// when for_new_limit_order is true, the limit order is taker, otherwise the limit order is maker
|
||||
bool really_filled = fill_limit_order( *limit_itr, order_pays, order_receives, true, match_price, !for_new_limit_order );
|
||||
bool really_filled = fill_limit_order( limit_order, order_pays, order_receives, true, match_price, !for_new_limit_order );
|
||||
if( really_filled || ( filled_limit && before_core_hardfork_453 ) )
|
||||
limit_itr = next_limit_itr;
|
||||
|
||||
|
|
|
|||
|
|
@ -250,22 +250,40 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s
|
|||
auto settle_price = bitasset.current_feed.settlement_price;
|
||||
if( settle_price.is_null() ) return false; // no feed
|
||||
|
||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
||||
const auto& call_price_index = call_index.indices().get<by_price>();
|
||||
const call_order_object* call_ptr = nullptr; // place holder for the call order with least collateral ratio
|
||||
|
||||
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id );
|
||||
auto call_itr = call_price_index.lower_bound( call_min );
|
||||
auto call_end = call_price_index.upper_bound( call_max );
|
||||
|
||||
if( call_itr == call_end ) return false; // no call orders
|
||||
|
||||
price highest = settle_price;
|
||||
asset_id_type debt_asset_id = mia.id;
|
||||
auto call_min = price::min( bitasset.options.short_backing_asset, debt_asset_id );
|
||||
|
||||
auto maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
if( maint_time > HARDFORK_CORE_338_TIME )
|
||||
bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue
|
||||
|
||||
if( before_core_hardfork_1270 ) // before core-1270 hard fork, check with call_price
|
||||
{
|
||||
const auto& call_price_index = get_index_type<call_order_index>().indices().get<by_price>();
|
||||
auto call_itr = call_price_index.lower_bound( call_min );
|
||||
if( call_itr == call_price_index.end() ) // no call order
|
||||
return false;
|
||||
call_ptr = &(*call_itr);
|
||||
}
|
||||
else // after core-1270 hard fork, check with collateralization
|
||||
{
|
||||
const auto& call_collateral_index = get_index_type<call_order_index>().indices().get<by_collateral>();
|
||||
auto call_itr = call_collateral_index.lower_bound( call_min );
|
||||
if( call_itr == call_collateral_index.end() ) // no call order
|
||||
return false;
|
||||
call_ptr = &(*call_itr);
|
||||
}
|
||||
if( call_ptr->debt_type() != debt_asset_id ) // no call order
|
||||
return false;
|
||||
|
||||
price highest = settle_price;
|
||||
if( maint_time > HARDFORK_CORE_1270_TIME )
|
||||
// due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here
|
||||
highest = bitasset.current_feed.max_short_squeeze_price();
|
||||
else if( maint_time > HARDFORK_CORE_338_TIME )
|
||||
// due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here
|
||||
highest = bitasset.current_feed.max_short_squeeze_price_before_hf_1270();
|
||||
|
||||
const limit_order_index& limit_index = get_index_type<limit_order_index>();
|
||||
const auto& limit_price_index = limit_index.indices().get<by_price>();
|
||||
|
|
@ -285,10 +303,10 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s
|
|||
highest = std::max( limit_itr->sell_price, highest );
|
||||
}
|
||||
|
||||
auto least_collateral = call_itr->collateralization();
|
||||
auto least_collateral = call_ptr->collateralization();
|
||||
if( ~least_collateral >= highest )
|
||||
{
|
||||
wdump( (*call_itr) );
|
||||
wdump( (*call_ptr) );
|
||||
elog( "Black Swan detected on asset ${symbol} (${id}) at block ${b}: \n"
|
||||
" Least collateralized call: ${lc} ${~lc}\n"
|
||||
// " Highest Bid: ${hb} ${~hb}\n"
|
||||
|
|
@ -515,6 +533,7 @@ void database::clear_expired_orders()
|
|||
void database::update_expired_feeds()
|
||||
{
|
||||
const auto head_time = head_block_time();
|
||||
const auto next_maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME );
|
||||
|
||||
const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_feed_expiration>();
|
||||
|
|
@ -529,9 +548,9 @@ void database::update_expired_feeds()
|
|||
if( after_hardfork_615 || b.feed_is_expired_before_hardfork_615( head_time ) )
|
||||
{
|
||||
auto old_median_feed = b.current_feed;
|
||||
modify( b, [head_time,&update_cer]( asset_bitasset_data_object& abdo )
|
||||
modify( b, [head_time,next_maint_time,&update_cer]( asset_bitasset_data_object& abdo )
|
||||
{
|
||||
abdo.update_median_feeds( head_time );
|
||||
abdo.update_median_feeds( head_time, next_maint_time );
|
||||
if( abdo.need_to_update_cer() )
|
||||
{
|
||||
update_cer = true;
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/CORE_1270.hf
Normal file
4
libraries/chain/hardfork.d/CORE_1270.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #1270 Call price is inconsistent when MCR changed
|
||||
#ifndef HARDFORK_CORE_1270_TIME
|
||||
#define HARDFORK_CORE_1270_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_518.hf
Normal file
4
libraries/chain/hardfork.d/CORE_518.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #518 Clean up bitasset_data during maintenance
|
||||
#ifndef HARDFORK_CORE_518_TIME
|
||||
#define HARDFORK_CORE_518_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_583.hf
Normal file
4
libraries/chain/hardfork.d/CORE_583.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #583 Always allow updating a call order to higher collateral ratio
|
||||
#ifndef HARDFORK_CORE_583_TIME
|
||||
#define HARDFORK_CORE_583_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
5
libraries/chain/hardfork.d/CORE_868_890.hf
Normal file
5
libraries/chain/hardfork.d/CORE_868_890.hf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// bitshares-core issue #868 Clear price feed data after updated a bitAsset's backing asset ID
|
||||
// bitshares-core issue #890 Update median feeds after feed_lifetime_sec changed
|
||||
#ifndef HARDFORK_CORE_868_890_TIME
|
||||
#define HARDFORK_CORE_868_890_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
5
libraries/chain/hardfork.d/CORE_922_931.hf
Normal file
5
libraries/chain/hardfork.d/CORE_922_931.hf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// bitshares-core issue #922 Missing checks when updating an asset's bitasset_data
|
||||
// bitshares-core issue #931 Changing backing asset ID runs some checks against the old value instead of the new
|
||||
#ifndef HARDFORK_CORE_922_931_TIME
|
||||
#define HARDFORK_CORE_922_931_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_935.hf
Normal file
4
libraries/chain/hardfork.d/CORE_935.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #935 Call check_call_orders not only when settlement_price changed
|
||||
#ifndef HARDFORK_CORE_935_TIME
|
||||
#define HARDFORK_CORE_935_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
|
|
@ -103,6 +103,7 @@ namespace graphene { namespace chain {
|
|||
void_result do_apply( const asset_update_bitasset_operation& o );
|
||||
|
||||
const asset_bitasset_data_object* bitasset_to_update = nullptr;
|
||||
const asset_object* asset_to_update = nullptr;
|
||||
};
|
||||
|
||||
class asset_update_dividend_evaluator : public evaluator<asset_update_dividend_evaluator>
|
||||
|
|
|
|||
|
|
@ -209,6 +209,9 @@ namespace graphene { namespace chain {
|
|||
price_feed current_feed;
|
||||
/// This is the publication time of the oldest feed which was factored into current_feed.
|
||||
time_point_sec current_feed_publication_time;
|
||||
/// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory.
|
||||
/// This value is derived from @ref current_feed for better performance and should be kept consistent.
|
||||
price current_maintenance_collateralization;
|
||||
|
||||
/// True if this asset implements a @ref prediction_market
|
||||
bool is_prediction_market = false;
|
||||
|
|
@ -260,7 +263,19 @@ namespace graphene { namespace chain {
|
|||
{ return feed_expiration_time() >= current_time; }
|
||||
bool feed_is_expired(time_point_sec current_time)const
|
||||
{ return feed_expiration_time() <= current_time; }
|
||||
void update_median_feeds(time_point_sec current_time);
|
||||
|
||||
/******
|
||||
* @brief calculate the median feed
|
||||
*
|
||||
* This calculates the median feed from @ref feeds, feed_lifetime_sec
|
||||
* in @ref options, and the given parameters.
|
||||
* It may update the current_feed_publication_time, current_feed and
|
||||
* current_maintenance_collateralization member variables.
|
||||
*
|
||||
* @param current_time the current time to use in the calculations
|
||||
* @param next_maintenance_time the next chain maintenance time
|
||||
*/
|
||||
void update_median_feeds(time_point_sec current_time, time_point_sec next_maintenance_time);
|
||||
};
|
||||
|
||||
// key extractor for short backing asset
|
||||
|
|
@ -521,6 +536,7 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::
|
|||
(feeds)
|
||||
(current_feed)
|
||||
(current_feed_publication_time)
|
||||
(current_maintenance_collateralization)
|
||||
(options)
|
||||
(force_settled_volume)
|
||||
(is_prediction_market)
|
||||
|
|
|
|||
|
|
@ -437,7 +437,8 @@ namespace graphene { namespace chain {
|
|||
///@{
|
||||
int match( const limit_order_object& taker, const limit_order_object& maker, const price& trade_price );
|
||||
int match( const limit_order_object& taker, const call_order_object& maker, const price& trade_price,
|
||||
const price& feed_price, const uint16_t maintenance_collateral_ratio );
|
||||
const price& feed_price, const uint16_t maintenance_collateral_ratio,
|
||||
const optional<price>& maintenance_collateralization );
|
||||
/// @return the amount of asset settled
|
||||
asset match(const call_order_object& call,
|
||||
const force_settlement_object& settle,
|
||||
|
|
@ -584,6 +585,7 @@ namespace graphene { namespace chain {
|
|||
void update_son_wallet( const vector<son_info>& new_active_sons );
|
||||
void update_worker_votes();
|
||||
void process_bids( const asset_bitasset_data_object& bad );
|
||||
void process_bitassets();
|
||||
|
||||
public:
|
||||
double calculate_vesting_factor(const account_object& stake_account);
|
||||
|
|
|
|||
|
|
@ -133,8 +133,20 @@ class call_order_object : public abstract_object<call_order_object>
|
|||
if( tmp.first > tmp.second ) std::swap( tmp.first, tmp.second );
|
||||
return tmp;
|
||||
}
|
||||
/// Calculate maximum quantity of debt to cover to satisfy @ref target_collateral_ratio.
|
||||
share_type get_max_debt_to_cover( price match_price, price feed_price, const uint16_t maintenance_collateral_ratio )const;
|
||||
/**
|
||||
* Calculate maximum quantity of debt to cover to satisfy @ref target_collateral_ratio.
|
||||
*
|
||||
* @param match_price the matching price if this call order is margin called
|
||||
* @param feed_price median settlement price of debt asset
|
||||
* @param maintenance_collateral_ratio median maintenance collateral ratio of debt asset
|
||||
* @param maintenance_collateralization maintenance collateralization of debt asset,
|
||||
* should only be valid after core-1270 hard fork
|
||||
* @return maximum amount of debt that can be called
|
||||
*/
|
||||
share_type get_max_debt_to_cover( price match_price,
|
||||
price feed_price,
|
||||
const uint16_t maintenance_collateral_ratio,
|
||||
const optional<price>& maintenance_collateralization = optional<price>() )const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -193,15 +193,6 @@ namespace graphene { namespace chain {
|
|||
/** Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM */
|
||||
uint16_t maximum_short_squeeze_ratio = GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO;
|
||||
|
||||
/**
|
||||
* When updating a call order the following condition must be maintained:
|
||||
*
|
||||
* debt * maintenance_price() < collateral
|
||||
* debt * settlement_price < debt * maintenance
|
||||
* debt * maintenance_price() < debt * max_short_squeeze_price()
|
||||
price maintenance_price()const;
|
||||
*/
|
||||
|
||||
/** When selling collateral to pay off debt, the least amount of debt to receive should be
|
||||
* min_usd = max_short_squeeze_price() * collateral
|
||||
*
|
||||
|
|
@ -209,6 +200,13 @@ namespace graphene { namespace chain {
|
|||
* must be confirmed by having the max_short_squeeze_price() move below the black swan price.
|
||||
*/
|
||||
price max_short_squeeze_price()const;
|
||||
/// Another implementation of max_short_squeeze_price() before the core-1270 hard fork
|
||||
price max_short_squeeze_price_before_hf_1270()const;
|
||||
|
||||
/// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory.
|
||||
/// Calculation: ~settlement_price * maintenance_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM
|
||||
price maintenance_collateralization()const;
|
||||
|
||||
///@}
|
||||
|
||||
friend bool operator == ( const price_feed& a, const price_feed& b )
|
||||
|
|
|
|||
|
|
@ -227,11 +227,15 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
}
|
||||
}
|
||||
|
||||
const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;
|
||||
bool before_core_hardfork_1270 = ( next_maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue
|
||||
|
||||
auto& call_idx = d.get_index_type<call_order_index>().indices().get<by_account>();
|
||||
auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) );
|
||||
const call_order_object* call_obj = nullptr;
|
||||
|
||||
optional<price> old_collateralization;
|
||||
|
||||
optional<uint16_t> new_target_cr = o.extensions.value.target_collateral_ratio;
|
||||
|
||||
if( itr == call_idx.end() )
|
||||
|
|
@ -239,19 +243,23 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
FC_ASSERT( o.delta_collateral.amount > 0 );
|
||||
FC_ASSERT( o.delta_debt.amount > 0 );
|
||||
|
||||
call_obj = &d.create<call_order_object>( [&](call_order_object& call ){
|
||||
call_obj = &d.create<call_order_object>( [&o,this,before_core_hardfork_1270]( call_order_object& call ){
|
||||
call.borrower = o.funding_account;
|
||||
call.collateral = o.delta_collateral.amount;
|
||||
call.debt = o.delta_debt.amount;
|
||||
call.call_price = price::call_price(o.delta_debt, o.delta_collateral,
|
||||
_bitasset_data->current_feed.maintenance_collateral_ratio);
|
||||
call.target_collateral_ratio = new_target_cr;
|
||||
if( before_core_hardfork_1270 ) // before core-1270 hard fork, calculate call_price here and cache it
|
||||
call.call_price = price::call_price( o.delta_debt, o.delta_collateral,
|
||||
_bitasset_data->current_feed.maintenance_collateral_ratio );
|
||||
else // after core-1270 hard fork, set call_price to 1
|
||||
call.call_price = price( asset( 1, o.delta_collateral.asset_id ), asset( 1, o.delta_debt.asset_id ) );
|
||||
call.target_collateral_ratio = o.extensions.value.target_collateral_ratio;
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
call_obj = &*itr;
|
||||
old_collateralization = call_obj->collateralization();
|
||||
|
||||
d.modify( *call_obj, [&]( call_order_object& call ){
|
||||
call.collateral += o.delta_collateral.amount;
|
||||
|
|
@ -283,10 +291,16 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
|
||||
// check to see if the order needs to be margin called now, but don't allow black swans and require there to be
|
||||
// limit orders available that could be used to fill the order.
|
||||
// Note: due to https://github.com/bitshares/bitshares-core/issues/649,
|
||||
// the first call order may be unable to be updated if the second one is undercollateralized.
|
||||
if( d.check_call_orders( *_debt_asset, false ) )
|
||||
{
|
||||
const auto call_obj = d.find(call_order_id);
|
||||
// if we filled at least one call order, we are OK if we totally filled.
|
||||
// before hard fork core-583: if we filled at least one call order, we are OK if we totally filled.
|
||||
// after hard fork core-583: we want to allow increasing collateral
|
||||
// Note: increasing collateral won't get the call order itself matched (instantly margin called)
|
||||
// if there is at least a call order get matched but didn't cause a black swan event,
|
||||
// current order must have got matched. in this case, it's OK if it's totally filled.
|
||||
GRAPHENE_ASSERT(
|
||||
!call_obj,
|
||||
call_order_update_unfilled_margin_call,
|
||||
|
|
@ -298,16 +312,35 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
{
|
||||
const auto call_obj = d.find(call_order_id);
|
||||
FC_ASSERT( call_obj, "no margin call was executed and yet the call object was deleted" );
|
||||
//edump( (~call_obj->call_price) ("<")( _bitasset_data->current_feed.settlement_price) );
|
||||
// We didn't fill any call orders. This may be because we
|
||||
// aren't in margin call territory, or it may be because there
|
||||
// were no matching orders. In the latter case, we throw.
|
||||
GRAPHENE_ASSERT(
|
||||
~call_obj->call_price < _bitasset_data->current_feed.settlement_price,
|
||||
call_order_update_unfilled_margin_call,
|
||||
"Updating call order would trigger a margin call that cannot be fully filled",
|
||||
("a", ~call_obj->call_price )("b", _bitasset_data->current_feed.settlement_price)
|
||||
);
|
||||
if( d.head_block_time() <= HARDFORK_CORE_583_TIME ) // TODO remove after hard fork core-583
|
||||
{
|
||||
// We didn't fill any call orders. This may be because we
|
||||
// aren't in margin call territory, or it may be because there
|
||||
// were no matching orders. In the latter case, we throw.
|
||||
GRAPHENE_ASSERT(
|
||||
~call_obj->call_price < _bitasset_data->current_feed.settlement_price,
|
||||
call_order_update_unfilled_margin_call,
|
||||
"Updating call order would trigger a margin call that cannot be fully filled",
|
||||
("a", ~call_obj->call_price )("b", _bitasset_data->current_feed.settlement_price)
|
||||
);
|
||||
}
|
||||
else // after hard fork, always allow call order to be updated if collateral ratio is increased
|
||||
{
|
||||
// We didn't fill any call orders. This may be because we
|
||||
// aren't in margin call territory, or it may be because there
|
||||
// were no matching orders. In the latter case,
|
||||
// if collateral ratio is not increased, we throw.
|
||||
// be here, we know no margin call was executed,
|
||||
// so call_obj's collateral ratio should be set only by op
|
||||
FC_ASSERT( ( old_collateralization.valid() && call_obj->collateralization() > *old_collateralization )
|
||||
|| ~call_obj->call_price < _bitasset_data->current_feed.settlement_price,
|
||||
"Can only update to higher collateral ratio if it would trigger a margin call that cannot be fully filled",
|
||||
("new_call_price", ~call_obj->call_price )
|
||||
("settlement_price", _bitasset_data->current_feed.settlement_price)
|
||||
("old_collateralization", old_collateralization)
|
||||
("new_collateralization", call_obj->collateralization() )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -367,6 +400,7 @@ void_result bid_collateral_evaluator::do_apply(const bid_collateral_operation& o
|
|||
bid.bidder = o.bidder;
|
||||
bid.inv_swan_price = o.additional_collateral / o.debt_covered;
|
||||
});
|
||||
// Note: CORE asset in collateral_bid_object is not counted in account_stats.total_core_in_orders
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
/*
|
||||
|
|
@ -51,7 +53,8 @@ max_debt_to_cover = max_amount_to_sell * match_price
|
|||
*/
|
||||
share_type call_order_object::get_max_debt_to_cover( price match_price,
|
||||
price feed_price,
|
||||
const uint16_t maintenance_collateral_ratio )const
|
||||
const uint16_t maintenance_collateral_ratio,
|
||||
const optional<price>& maintenance_collateralization )const
|
||||
{ try {
|
||||
// be defensive here, make sure feed_price is in collateral / debt format
|
||||
if( feed_price.base.asset_id != call_price.base.asset_id )
|
||||
|
|
@ -60,7 +63,23 @@ share_type call_order_object::get_max_debt_to_cover( price match_price,
|
|||
FC_ASSERT( feed_price.base.asset_id == call_price.base.asset_id
|
||||
&& feed_price.quote.asset_id == call_price.quote.asset_id );
|
||||
|
||||
if( call_price > feed_price ) // feed protected. be defensive here, although this should be guaranteed by caller
|
||||
bool after_core_hardfork_1270 = maintenance_collateralization.valid();
|
||||
|
||||
// be defensive here, make sure maintenance_collateralization is in collateral / debt format
|
||||
if( after_core_hardfork_1270 )
|
||||
{
|
||||
FC_ASSERT( maintenance_collateralization->base.asset_id == call_price.base.asset_id
|
||||
&& maintenance_collateralization->quote.asset_id == call_price.quote.asset_id );
|
||||
}
|
||||
|
||||
// According to the feed protection rule (https://github.com/cryptonomex/graphene/issues/436),
|
||||
// a call order should only be called when its collateral ratio is not higher than required maintenance collateral ratio.
|
||||
// Although this should be guaranteed by the caller of this function, we still check here to be defensive.
|
||||
// Theoretically this check can be skipped for better performance.
|
||||
//
|
||||
// Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
|
||||
if( ( !after_core_hardfork_1270 && call_price > feed_price )
|
||||
|| ( after_core_hardfork_1270 && collateralization() > *maintenance_collateralization ) )
|
||||
return 0;
|
||||
|
||||
if( !target_collateral_ratio.valid() ) // target cr is not set
|
||||
|
|
@ -68,6 +87,10 @@ share_type call_order_object::get_max_debt_to_cover( price match_price,
|
|||
|
||||
uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio ); // use mcr if target cr is too small
|
||||
|
||||
price target_collateralization = ( after_core_hardfork_1270 ?
|
||||
feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) :
|
||||
price() );
|
||||
|
||||
// be defensive here, make sure match_price is in collateral / debt format
|
||||
if( match_price.base.asset_id != call_price.base.asset_id )
|
||||
match_price = ~match_price;
|
||||
|
|
@ -108,9 +131,22 @@ share_type call_order_object::get_max_debt_to_cover( price match_price,
|
|||
return debt;
|
||||
FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
|
||||
|
||||
// check collateral ratio after filled, if it's OK, we return
|
||||
price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
|
||||
if( new_call_price > feed_price )
|
||||
// Check whether the collateral ratio after filled is high enough
|
||||
// Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
|
||||
std::function<bool()> result_is_good = after_core_hardfork_1270 ?
|
||||
std::function<bool()>( [this,&to_cover,&to_pay,target_collateralization]() -> bool
|
||||
{
|
||||
price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover );
|
||||
return ( new_collateralization > target_collateralization );
|
||||
}) :
|
||||
std::function<bool()>( [this,&to_cover,&to_pay,tcr,feed_price]() -> bool
|
||||
{
|
||||
price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
|
||||
return ( new_call_price > feed_price );
|
||||
});
|
||||
|
||||
// if the result is good, we return.
|
||||
if( result_is_good() )
|
||||
return to_cover.amount;
|
||||
|
||||
// be here, to_cover is too small due to rounding. deal with the fraction
|
||||
|
|
@ -204,8 +240,8 @@ share_type call_order_object::get_max_debt_to_cover( price match_price,
|
|||
return to_cover.amount;
|
||||
FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
|
||||
|
||||
new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
|
||||
if( new_call_price > feed_price ) // good
|
||||
// Check whether the result is good
|
||||
if( result_is_good() ) // good
|
||||
{
|
||||
if( to_pay.amount == max_to_pay.amount )
|
||||
return to_cover.amount;
|
||||
|
|
@ -253,8 +289,8 @@ share_type call_order_object::get_max_debt_to_cover( price match_price,
|
|||
// check
|
||||
FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
|
||||
|
||||
new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
|
||||
if( new_call_price > feed_price ) // good
|
||||
// Check whether the result is good
|
||||
if( result_is_good() ) // good
|
||||
return to_cover.amount;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,6 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio)
|
||||
{ try {
|
||||
//wdump((debt)(collateral)(collateral_ratio));
|
||||
boost::rational<int128_t> swan(debt.amount.value,collateral.amount.value);
|
||||
boost::rational<int128_t> ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
|
||||
auto cp = swan * ratio;
|
||||
|
|
@ -259,9 +258,11 @@ namespace graphene { namespace chain {
|
|||
FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );
|
||||
FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );
|
||||
FC_ASSERT( maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );
|
||||
max_short_squeeze_price(); // make sure that it doesn't overflow
|
||||
// Note: there was code here calling `max_short_squeeze_price();` before core-1270 hard fork,
|
||||
// in order to make sure that it doesn't overflow,
|
||||
// but the code doesn't actually check overflow, and it won't overflow, so the code is removed.
|
||||
|
||||
//FC_ASSERT( maintenance_collateral_ratio >= maximum_short_squeeze_ratio );
|
||||
// Note: not checking `maintenance_collateral_ratio >= maximum_short_squeeze_ratio` since launch
|
||||
} FC_CAPTURE_AND_RETHROW( (*this) ) }
|
||||
|
||||
bool price_feed::is_for( asset_id_type asset_id ) const
|
||||
|
|
@ -278,16 +279,34 @@ namespace graphene { namespace chain {
|
|||
FC_CAPTURE_AND_RETHROW( (*this) )
|
||||
}
|
||||
|
||||
price price_feed::max_short_squeeze_price()const
|
||||
// This function is kept here due to potential different behavior in edge cases.
|
||||
// TODO check after core-1270 hard fork to see if we can safely remove it
|
||||
price price_feed::max_short_squeeze_price_before_hf_1270()const
|
||||
{
|
||||
boost::rational<int128_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value ); //debt.amount.value,collateral.amount.value);
|
||||
// settlement price is in debt/collateral
|
||||
boost::rational<int128_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value );
|
||||
boost::rational<int128_t> ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
|
||||
auto cp = sp * ratio;
|
||||
|
||||
while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )
|
||||
cp = boost::rational<int128_t>( (cp.numerator() >> 1)+(cp.numerator()&1), (cp.denominator() >> 1)+(cp.denominator()&1) );
|
||||
cp = boost::rational<int128_t>( (cp.numerator() >> 1)+(cp.numerator()&1),
|
||||
(cp.denominator() >> 1)+(cp.denominator()&1) );
|
||||
|
||||
return (asset( cp.numerator().convert_to<int64_t>(), settlement_price.base.asset_id ) / asset( cp.denominator().convert_to<int64_t>(), settlement_price.quote.asset_id ));
|
||||
return ( asset( cp.numerator().convert_to<int64_t>(), settlement_price.base.asset_id )
|
||||
/ asset( cp.denominator().convert_to<int64_t>(), settlement_price.quote.asset_id ) );
|
||||
}
|
||||
|
||||
price price_feed::max_short_squeeze_price()const
|
||||
{
|
||||
// settlement price is in debt/collateral
|
||||
return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
|
||||
}
|
||||
|
||||
price price_feed::maintenance_collateralization()const
|
||||
{
|
||||
if( settlement_price.is_null() )
|
||||
return price();
|
||||
return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
|
||||
}
|
||||
|
||||
// compile-time table of powers of 10 using template metaprogramming
|
||||
|
|
|
|||
|
|
@ -974,6 +974,38 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obje
|
|||
verify_asset_supplies(db);
|
||||
}
|
||||
|
||||
/***
|
||||
* @brief helper method to add a price feed
|
||||
*
|
||||
* Adds a price feed for asset2, pushes the transaction, and generates the block
|
||||
*
|
||||
* @param fixture the database_fixture
|
||||
* @param publisher who is publishing the feed
|
||||
* @param asset1 the base asset
|
||||
* @param amount1 the amount of the base asset
|
||||
* @param asset2 the quote asset
|
||||
* @param amount2 the amount of the quote asset
|
||||
* @param core_id id of core (helps with core_exchange_rate)
|
||||
*/
|
||||
void database_fixture::publish_feed(const account_id_type& publisher,
|
||||
const asset_id_type& asset1, int64_t amount1,
|
||||
const asset_id_type& asset2, int64_t amount2,
|
||||
const asset_id_type& core_id)
|
||||
{
|
||||
const asset_object& a1 = asset1(db);
|
||||
const asset_object& a2 = asset2(db);
|
||||
const asset_object& core = core_id(db);
|
||||
asset_publish_feed_operation op;
|
||||
op.publisher = publisher;
|
||||
op.asset_id = asset2;
|
||||
op.feed.settlement_price = ~price(a1.amount(amount1),a2.amount(amount2));
|
||||
op.feed.core_exchange_rate = ~price(core.amount(amount1), a2.amount(amount2));
|
||||
trx.operations.push_back(std::move(op));
|
||||
PUSH_TX( db, trx, ~0);
|
||||
generate_block();
|
||||
trx.clear();
|
||||
}
|
||||
|
||||
void database_fixture::force_global_settle( const asset_object& what, const price& p )
|
||||
{ try {
|
||||
set_expiration( db, trx );
|
||||
|
|
|
|||
|
|
@ -228,6 +228,22 @@ struct database_fixture {
|
|||
void update_feed_producers(const asset_object& mia, flat_set<account_id_type> producers);
|
||||
void publish_feed(asset_id_type mia, account_id_type by, const price_feed& f)
|
||||
{ publish_feed(mia(db), by(db), f); }
|
||||
/***
|
||||
* @brief helper method to add a price feed
|
||||
*
|
||||
* Adds a price feed for asset2, pushes the transaction, and generates the block
|
||||
*
|
||||
* @param publisher who is publishing the feed
|
||||
* @param asset1 the base asset
|
||||
* @param amount1 the amount of the base asset
|
||||
* @param asset2 the quote asset
|
||||
* @param amount2 the amount of the quote asset
|
||||
* @param core_id id of core (helps with core_exchange_rate)
|
||||
*/
|
||||
void publish_feed(const account_id_type& publisher,
|
||||
const asset_id_type& asset1, int64_t amount1,
|
||||
const asset_id_type& asset2, int64_t amount2,
|
||||
const asset_id_type& core_id);
|
||||
void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f);
|
||||
const call_order_object* borrow(account_id_type who, asset what, asset collateral)
|
||||
{ return borrow(who(db), what, collateral); }
|
||||
|
|
|
|||
Loading…
Reference in a new issue