From 22dbbf3e484d2528927a25d9cd4054a00d7487e4 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 1 Oct 2019 12:25:33 -0500 Subject: [PATCH 1/5] prevent black swan for prediction markets --- libraries/chain/db_market.cpp | 4 ++ libraries/chain/hardfork.d/CORE_460.hf | 4 ++ tests/tests/operation_tests.cpp | 64 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 libraries/chain/hardfork.d/CORE_460.hf diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index eef79aa977..1c511be664 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1003,6 +1003,10 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa if( !mia.is_market_issued() ) return false; const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) ); + + // previously, price feeds could cause black swan in prediction market + if ( maint_time >= HARDFORK_CORE_460_TIME && bitasset.is_prediction_market ) + return false; if( check_for_blackswan( mia, enable_black_swan, &bitasset ) ) return false; diff --git a/libraries/chain/hardfork.d/CORE_460.hf b/libraries/chain/hardfork.d/CORE_460.hf new file mode 100644 index 0000000000..8f0bd3f6fa --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_460.hf @@ -0,0 +1,4 @@ +// bitshares-core issue #460 Prediction Market price feed should not cause black swan +#ifndef HARDFORK_CORE_460_TIME +#define HARDFORK_CORE_460_TIME (fc::time_point_sec( 1609372800 ) ) // 2020-12-31T00:00:00 +#endif diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index f5deed6ddd..f731153f9c 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -840,6 +840,70 @@ BOOST_AUTO_TEST_CASE( prediction_market_resolves_to_0 ) } } +/*** + * Prediction markets should not suffer a black swan (Issue #460) + */ +BOOST_AUTO_TEST_CASE( prediction_market_black_swan ) +{ + try { + ACTORS((judge)(dan)(nathan)); + + // progress to recent hardfork + generate_blocks( HARDFORK_CORE_1270_TIME ); + set_expiration( db, trx ); + + const auto& pmark = create_prediction_market("PMARK", judge_id); + + int64_t init_balance(1000000); + transfer(committee_account, judge_id, asset(init_balance)); + transfer(committee_account, dan_id, asset(init_balance)); + + update_feed_producers( pmark, { judge_id }); + price_feed feed; + feed.settlement_price = asset( 1, pmark.id ) / asset( 1 ); + publish_feed( pmark, judge, feed ); + + borrow( dan, pmark.amount(1000), asset(1000) ); + + // feed a price that will cause a black swan + feed.settlement_price = asset( 1, pmark.id ) / asset( 1000 ); + publish_feed( pmark, judge, feed ); + + // verify a black swan happened + GRAPHENE_REQUIRE_THROW(borrow( dan, pmark.amount(1000), asset(1000) ), fc::exception); + + // interestingly, PMARKII cannot be created without dan force_settling + force_settle( dan, pmark.amount(1000) ); + + // progress past hardfork + generate_blocks( HARDFORK_CORE_460_TIME ); + set_expiration( db, trx ); + + // create another prediction market to test the hardfork + const auto& pmark2 = create_prediction_market("PMARKII", judge_id); + update_feed_producers( pmark2, { judge_id }); + price_feed feed2; + feed2.settlement_price = asset( 1, pmark2.id ) / asset( 1 ); + publish_feed( pmark2, judge, feed2 ); + + borrow( dan, pmark2.amount(1000), asset(1000) ); + + // feed a price that would have caused a black swan + feed2.settlement_price = asset( 1, pmark2.id ) / asset( 1000 ); + publish_feed( pmark2, judge, feed2 ); + + // verify a black swan did not happen + borrow( dan, pmark2.amount(1000), asset(1000) ); + + generate_block(~database::skip_transaction_dupe_check); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + generate_block(); + } catch( const fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_CASE( create_account_test ) { try { From 3f75dee6dd87e071fc3b1703ef21e4ea7dc81bb7 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 1 Oct 2019 14:03:26 -0500 Subject: [PATCH 2/5] Fix test --- tests/tests/operation_tests.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index f731153f9c..2fc1cacc73 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -871,9 +871,7 @@ BOOST_AUTO_TEST_CASE( prediction_market_black_swan ) // verify a black swan happened GRAPHENE_REQUIRE_THROW(borrow( dan, pmark.amount(1000), asset(1000) ), fc::exception); - - // interestingly, PMARKII cannot be created without dan force_settling - force_settle( dan, pmark.amount(1000) ); + trx.clear(); // progress past hardfork generate_blocks( HARDFORK_CORE_460_TIME ); From 9d6175cfbb080a04f6eaea99dbf966ca8febe2b5 Mon Sep 17 00:00:00 2001 From: John Jones Date: Tue, 1 Oct 2019 14:15:35 -0500 Subject: [PATCH 3/5] fix test (maintenance interval) --- tests/tests/operation_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index 2fc1cacc73..fc3f1f615c 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -874,7 +874,7 @@ BOOST_AUTO_TEST_CASE( prediction_market_black_swan ) trx.clear(); // progress past hardfork - generate_blocks( HARDFORK_CORE_460_TIME ); + generate_blocks( HARDFORK_CORE_460_TIME + db.get_global_properties().parameters.maintenance_interval ); set_expiration( db, trx ); // create another prediction market to test the hardfork From 0693d77ede168a02990c0110f3a9c89e5fc7122f Mon Sep 17 00:00:00 2001 From: John Jones Date: Fri, 4 Oct 2019 13:32:27 -0500 Subject: [PATCH 4/5] Add details to comment --- libraries/chain/db_market.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 1c511be664..70e0670266 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1004,7 +1004,11 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) ); - // previously, price feeds could cause black swan in prediction market + // price feeds can cause black swans in prediction markets + // The hardfork check may be able to be removed after the hardfork date + // if check_for_blackswan never triggered a black swan on a prediction market. + // NOTE: check_for_blackswan returning false does not always mean a black + // swan was triggered. if ( maint_time >= HARDFORK_CORE_460_TIME && bitasset.is_prediction_market ) return false; From b4e3a85ec5ee1d4994f4fbe2a87f12352ffa0b79 Mon Sep 17 00:00:00 2001 From: John Jones Date: Thu, 10 Oct 2019 13:52:09 -0500 Subject: [PATCH 5/5] fixed comment --- libraries/chain/db_market.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/db_market.cpp b/libraries/chain/db_market.cpp index 70e0670266..e19eec7b8f 100644 --- a/libraries/chain/db_market.cpp +++ b/libraries/chain/db_market.cpp @@ -1007,7 +1007,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa // price feeds can cause black swans in prediction markets // The hardfork check may be able to be removed after the hardfork date // if check_for_blackswan never triggered a black swan on a prediction market. - // NOTE: check_for_blackswan returning false does not always mean a black + // NOTE: check_for_blackswan returning true does not always mean a black // swan was triggered. if ( maint_time >= HARDFORK_CORE_460_TIME && bitasset.is_prediction_market ) return false;