black swan test with force settle after

This commit is contained in:
Daniel Larimer 2015-06-19 14:47:42 -04:00
parent 626b5cbb23
commit c80c839675
5 changed files with 87 additions and 16 deletions

View file

@ -370,6 +370,12 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::
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;
}
else

View file

@ -37,10 +37,11 @@ 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" );
@ -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 auto& call_price_index = call_index.indices().get<by_price>();
// 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_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;
});
/// 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
/// 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 ){
obj.current_supply = original_mia_supply;
});
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }

View file

@ -148,6 +148,11 @@ void database_fixture::verify_asset_supplies( )const
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);
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>>() )
{
@ -655,6 +660,20 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obj
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 )
{ try {
asset call_price_collateral((collateral.amount.value * 3)/4, collateral.asset_id );

View file

@ -147,6 +147,7 @@ struct database_fixture {
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 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());

View file

@ -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 )
{
@ -1248,20 +1302,6 @@ BOOST_AUTO_TEST_CASE( unimp_bulk_discount_test )
//const account_object& shorter2 = create_account( "bob" );
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
*/