black swan test with force settle after
This commit is contained in:
parent
626b5cbb23
commit
c80c839675
5 changed files with 87 additions and 16 deletions
|
|
@ -370,6 +370,12 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::
|
||||||
|
|
||||||
d.adjust_balance(op.account, settled_amount);
|
d.adjust_balance(op.account, settled_amount);
|
||||||
|
|
||||||
|
const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d);
|
||||||
|
|
||||||
|
d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
|
||||||
|
obj.current_supply -= op.amount.amount;
|
||||||
|
});
|
||||||
|
|
||||||
return settled_amount;
|
return settled_amount;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,11 @@ namespace graphene { namespace chain {
|
||||||
*/
|
*/
|
||||||
void database::globally_settle_asset( const asset_object& mia, const price& settlement_price )
|
void database::globally_settle_asset( const asset_object& mia, const price& settlement_price )
|
||||||
{ try {
|
{ try {
|
||||||
|
/*
|
||||||
elog( "BLACK SWAN!" );
|
elog( "BLACK SWAN!" );
|
||||||
debug_dump();
|
debug_dump();
|
||||||
|
|
||||||
edump( (mia.symbol)(settlement_price) );
|
edump( (mia.symbol)(settlement_price) );
|
||||||
|
*/
|
||||||
|
|
||||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||||
FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" );
|
FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" );
|
||||||
|
|
@ -54,6 +55,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
||||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
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 = call_index.indices().get<by_price>();
|
||||||
|
|
||||||
|
|
||||||
// cancel all call orders and accumulate it into collateral_gathered
|
// cancel all call orders and accumulate it into collateral_gathered
|
||||||
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
||||||
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
||||||
|
|
@ -72,10 +74,13 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
||||||
obj.settlement_fund = collateral_gathered.amount;
|
obj.settlement_fund = collateral_gathered.amount;
|
||||||
});
|
});
|
||||||
|
|
||||||
/// TODO: after all margin positions are closed, the current supply will be reported as 0, but
|
/// After all margin positions are closed, the current supply will be reported as 0, but
|
||||||
/// that is a lie, the supply didn't change. We need to capture the current supply before
|
/// 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
|
/// filling all call orders and then restore it afterward. Then in the force settlement
|
||||||
/// evaluator reduce the supply
|
/// evaluator reduce the supply
|
||||||
|
modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
|
||||||
|
obj.current_supply = original_mia_supply;
|
||||||
|
});
|
||||||
|
|
||||||
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
|
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,11 @@ void database_fixture::verify_asset_supplies( )const
|
||||||
if( asset_obj.id != asset_id_type() )
|
if( asset_obj.id != asset_id_type() )
|
||||||
BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value);
|
BOOST_CHECK_EQUAL(total_balances[asset_obj.id].value, asset_obj.dynamic_asset_data_id(db).current_supply.value);
|
||||||
total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;
|
total_balances[asset_id_type()] += asset_obj.dynamic_asset_data_id(db).fee_pool;
|
||||||
|
if( asset_obj.is_market_issued() )
|
||||||
|
{
|
||||||
|
const auto& bad = asset_obj.bitasset_data(db);
|
||||||
|
total_balances[bad.options.short_backing_asset] += bad.settlement_fund;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for( const witness_object& witness_obj : db.get_index_type<simple_index<witness_object>>() )
|
for( const witness_object& witness_obj : db.get_index_type<simple_index<witness_object>>() )
|
||||||
{
|
{
|
||||||
|
|
@ -655,6 +660,20 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obj
|
||||||
trx.operations.clear();
|
trx.operations.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void database_fixture::force_settle( const account_object& who, asset what )
|
||||||
|
{ try {
|
||||||
|
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
||||||
|
trx.operations.clear();
|
||||||
|
asset_settle_operation sop;
|
||||||
|
sop.account = who.id;
|
||||||
|
sop.amount = what;
|
||||||
|
trx.operations.push_back(sop);
|
||||||
|
for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) );
|
||||||
|
trx.validate();
|
||||||
|
db.push_transaction(trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
} FC_CAPTURE_AND_RETHROW( (who)(what) ) }
|
||||||
|
|
||||||
void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price )
|
void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price )
|
||||||
{ try {
|
{ try {
|
||||||
asset call_price_collateral((collateral.amount.value * 3)/4, collateral.asset_id );
|
asset call_price_collateral((collateral.amount.value * 3)/4, collateral.asset_id );
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ struct database_fixture {
|
||||||
key_id_type key = key_id_type()
|
key_id_type key = key_id_type()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void force_settle( const account_object& who, asset what );
|
||||||
void update_feed_producers( const asset_object& mia, flat_set<account_id_type> producers );
|
void update_feed_producers( const asset_object& mia, flat_set<account_id_type> producers );
|
||||||
void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f );
|
void publish_feed( const asset_object& mia, const account_object& by, const price_feed& f );
|
||||||
void borrow( const account_object& who, asset what, asset collateral, price call_price = price());
|
void borrow( const account_object& who, asset what, asset collateral, price call_price = price());
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,60 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test sets up the minimum condition for a black swan to occur but does
|
||||||
|
* not test the full range of cases that may be possible during a black swan.
|
||||||
|
*/
|
||||||
|
BOOST_AUTO_TEST_CASE( black_swan )
|
||||||
|
{ try {
|
||||||
|
ACTORS((buyer)(seller)(borrower)(borrower2)(feedproducer));
|
||||||
|
|
||||||
|
const auto& bitusd = create_bitasset("BITUSD");
|
||||||
|
const auto& core = asset_id_type()(db);
|
||||||
|
|
||||||
|
int64_t init_balance(1000000);
|
||||||
|
|
||||||
|
transfer(genesis_account, buyer_id, asset(init_balance));
|
||||||
|
transfer(genesis_account, borrower_id, asset(init_balance));
|
||||||
|
transfer(genesis_account, borrower2_id, asset(init_balance));
|
||||||
|
update_feed_producers( bitusd, {feedproducer.id} );
|
||||||
|
|
||||||
|
price_feed current_feed;
|
||||||
|
current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);
|
||||||
|
auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750);
|
||||||
|
|
||||||
|
// starting out with price 1:1
|
||||||
|
publish_feed( bitusd, feedproducer, current_feed );
|
||||||
|
|
||||||
|
// start out with 2:1 collateral
|
||||||
|
borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price );
|
||||||
|
borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price );
|
||||||
|
|
||||||
|
BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 );
|
||||||
|
BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 );
|
||||||
|
BOOST_REQUIRE_EQUAL( get_balance( borrower , core ), init_balance - 2000 );
|
||||||
|
BOOST_REQUIRE_EQUAL( get_balance( borrower2, core ), init_balance - 4000 );
|
||||||
|
|
||||||
|
current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(200);
|
||||||
|
publish_feed( bitusd, feedproducer, current_feed );
|
||||||
|
|
||||||
|
/// this sell order is designed to trigger a black swan
|
||||||
|
auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(3000) );
|
||||||
|
|
||||||
|
FC_ASSERT( bitusd.bitasset_data(db).has_settlement() );
|
||||||
|
wdump(( bitusd.bitasset_data(db) ));
|
||||||
|
|
||||||
|
force_settle( borrower, bitusd.amount(100) );
|
||||||
|
|
||||||
|
BOOST_TEST_MESSAGE( "Verify that we cannot borrow after black swan" );
|
||||||
|
BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ), fc::exception );
|
||||||
|
} catch( const fc::exception& e) {
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( create_account_test )
|
BOOST_AUTO_TEST_CASE( create_account_test )
|
||||||
{
|
{
|
||||||
|
|
@ -1248,20 +1302,6 @@ BOOST_AUTO_TEST_CASE( unimp_bulk_discount_test )
|
||||||
//const account_object& shorter2 = create_account( "bob" );
|
//const account_object& shorter2 = create_account( "bob" );
|
||||||
BOOST_FAIL( "not implemented" );
|
BOOST_FAIL( "not implemented" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This test sets up the minimum condition for a black swan to occur but does
|
|
||||||
* not test the full range of cases that may be possible during a black swan.
|
|
||||||
*/
|
|
||||||
BOOST_AUTO_TEST_CASE( margin_call_black_swan )
|
|
||||||
{ try {
|
|
||||||
FC_ASSERT( "TODO - Reimplement with new short semantics" );
|
|
||||||
} catch( const fc::exception& e) {
|
|
||||||
edump((e.to_detail_string()));
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assume the referrer gets 99% of transaction fee
|
* Assume the referrer gets 99% of transaction fee
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue