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);
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue