diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 532be11e..b7075ab4 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -740,7 +740,7 @@ void database_fixture::force_global_settle( const asset_object& what, const pric verify_asset_supplies(db); } FC_CAPTURE_AND_RETHROW( (what)(p) ) } -void database_fixture::force_settle( const account_object& who, asset what ) +operation_result database_fixture::force_settle( const account_object& who, asset what ) { try { trx.set_expiration(db.head_block_time() + fc::minutes(1)); trx.operations.clear(); @@ -750,9 +750,11 @@ void database_fixture::force_settle( const account_object& who, asset what ) trx.operations.push_back(sop); for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op); trx.validate(); - db.push_transaction(trx, ~0); + processed_transaction ptx = db.push_transaction(trx, ~0); + const operation_result& op_result = ptx.operation_results.front(); trx.operations.clear(); verify_asset_supplies(db); + return op_result; } FC_CAPTURE_AND_RETHROW( (who)(what) ) } const call_order_object* database_fixture::borrow(const account_object& who, asset what, asset collateral) diff --git a/tests/common/database_fixture.hpp b/tests/common/database_fixture.hpp index 14671b87..366d95a9 100644 --- a/tests/common/database_fixture.hpp +++ b/tests/common/database_fixture.hpp @@ -190,9 +190,9 @@ struct database_fixture { ); void force_global_settle(const asset_object& what, const price& p); - void force_settle(account_id_type who, asset what) - { force_settle(who(db), what); } - void force_settle(const account_object& who, asset what); + operation_result force_settle(account_id_type who, asset what) + { return force_settle(who(db), what); } + operation_result force_settle(const account_object& who, asset what); void update_feed_producers(asset_id_type mia, flat_set producers) { update_feed_producers(mia(db), producers); } void update_feed_producers(const asset_object& mia, flat_set producers); diff --git a/tests/tests/operation_tests2.cpp b/tests/tests/operation_tests2.cpp index b6edcbc0..2c0978ad 100644 --- a/tests/tests/operation_tests2.cpp +++ b/tests/tests/operation_tests2.cpp @@ -820,128 +820,191 @@ BOOST_AUTO_TEST_CASE( burn_worker_test ) BOOST_CHECK_EQUAL( get_balance(GRAPHENE_NULL_ACCOUNT, asset_id_type()), 2000 ); }FC_LOG_AND_RETHROW()} -BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_force_settlement_unavailable, 1 ) -BOOST_AUTO_TEST_CASE( unimp_force_settlement_unavailable ) +BOOST_AUTO_TEST_CASE( force_settle_test ) { - BOOST_FAIL( "TODO - Reimplement this" ); - /* - try { - auto private_key = init_account_priv_key; - auto private_key = generate_private_key("committee"); ->>>>>>> short_refactor - account_id_type nathan_id = create_account("nathan").get_id(); - account_id_type shorter1_id = create_account("shorter1").get_id(); - account_id_type shorter2_id = create_account("shorter2").get_id(); - account_id_type shorter3_id = create_account("shorter3").get_id(); - transfer(account_id_type()(db), nathan_id(db), asset(100000000)); - transfer(account_id_type()(db), shorter1_id(db), asset(100000000)); - transfer(account_id_type()(db), shorter2_id(db), asset(100000000)); - transfer(account_id_type()(db), shorter3_id(db), asset(100000000)); - asset_id_type bit_usd = create_bitasset("BITUSD", GRAPHENE_TEMP_ACCOUNT, 0, disable_force_settle).get_id(); - FC_ASSERT( bit_usd(db).is_market_issued() ); + try { - asset_update_bitasset_operation op; - op.asset_to_update = bit_usd; - op.issuer = bit_usd(db).issuer; - op.new_options = bit_usd(db).bitasset_data(db).options; - op.new_options.maximum_force_settlement_volume = 9000; - trx.clear(); - trx.operations.push_back(op); - PUSH_TX( db, trx, ~0 ); - trx.clear(); + ACTORS( (nathan)(shorter1)(shorter2)(shorter3)(shorter4)(shorter5) ); + + int64_t initial_balance = 100000000; + + transfer(account_id_type()(db), shorter1_id(db), asset(initial_balance)); + transfer(account_id_type()(db), shorter2_id(db), asset(initial_balance)); + transfer(account_id_type()(db), shorter3_id(db), asset(initial_balance)); + transfer(account_id_type()(db), shorter4_id(db), asset(initial_balance)); + transfer(account_id_type()(db), shorter5_id(db), asset(initial_balance)); + + asset_id_type bitusd_id = create_bitasset( + "BITUSD", + nathan_id, + 100, + disable_force_settle + ).id; + + asset_id_type core_id = asset_id_type(); + + auto update_bitasset_options = [&]( asset_id_type asset_id, + std::function< void(bitasset_options&) > update_function ) + { + const asset_object& _asset = asset_id(db); + asset_update_bitasset_operation op; + op.asset_to_update = asset_id; + op.issuer = _asset.issuer; + op.new_options = (*_asset.bitasset_data_id)(db).options; + update_function( op.new_options ); + signed_transaction tx; + tx.operations.push_back( op ); + tx.set_expiration( db.head_block_time() + fc::minutes(5) ); + PUSH_TX( db, tx, ~0 ); + } ; + + auto update_asset_options = [&]( asset_id_type asset_id, + std::function< void(asset_options&) > update_function ) + { + const asset_object& _asset = asset_id(db); + asset_update_operation op; + op.asset_to_update = asset_id; + op.issuer = _asset.issuer; + op.new_options = _asset.options; + update_function( op.new_options ); + signed_transaction tx; + tx.operations.push_back( op ); + tx.set_expiration( db.head_block_time() + fc::minutes(5) ); + PUSH_TX( db, tx, ~0 ); + } ; + + BOOST_TEST_MESSAGE( "Update maximum_force_settlement_volume = 9000" ); + + BOOST_CHECK( bitusd_id(db).is_market_issued() ); + update_bitasset_options( bitusd_id, [&]( bitasset_options& new_options ) + { new_options.maximum_force_settlement_volume = 9000; } ); + + BOOST_TEST_MESSAGE( "Publish price feed" ); + + update_feed_producers( bitusd_id, { nathan_id } ); + { + price_feed feed; + feed.settlement_price = price( asset( 1, bitusd_id ), asset( 1, core_id ) ); + publish_feed( bitusd_id, nathan_id, feed ); + } + + BOOST_TEST_MESSAGE( "First short batch" ); + + call_order_id_type call1_id = borrow( shorter1_id, asset(1000, bitusd_id), asset(2*1000, core_id) )->id; // 2.0000 + call_order_id_type call2_id = borrow( shorter2_id, asset(2000, bitusd_id), asset(2*1999, core_id) )->id; // 1.9990 + call_order_id_type call3_id = borrow( shorter3_id, asset(3000, bitusd_id), asset(2*2890, core_id) )->id; // 1.9267 + call_order_id_type call4_id = borrow( shorter4_id, asset(4000, bitusd_id), asset(2*3950, core_id) )->id; // 1.9750 + call_order_id_type call5_id = borrow( shorter5_id, asset(5000, bitusd_id), asset(2*4900, core_id) )->id; // 1.9600 + + transfer( shorter1_id, nathan_id, asset(1000, bitusd_id) ); + transfer( shorter2_id, nathan_id, asset(2000, bitusd_id) ); + transfer( shorter3_id, nathan_id, asset(3000, bitusd_id) ); + transfer( shorter4_id, nathan_id, asset(4000, bitusd_id) ); + transfer( shorter5_id, nathan_id, asset(5000, bitusd_id) ); + + BOOST_CHECK_EQUAL( get_balance(nathan_id, bitusd_id), 15000); + BOOST_CHECK_EQUAL( get_balance(nathan_id, core_id), 0); + BOOST_CHECK_EQUAL( get_balance(shorter1_id, core_id), initial_balance-2000 ); + BOOST_CHECK_EQUAL( get_balance(shorter2_id, core_id), initial_balance-3998 ); + BOOST_CHECK_EQUAL( get_balance(shorter3_id, core_id), initial_balance-5780 ); + BOOST_CHECK_EQUAL( get_balance(shorter4_id, core_id), initial_balance-7900 ); + BOOST_CHECK_EQUAL( get_balance(shorter5_id, core_id), initial_balance-9800 ); + + BOOST_TEST_MESSAGE( "Update force_settlement_delay_sec = 100, force_settlement_offset_percent = 1%" ); + + update_bitasset_options( bitusd_id, [&]( bitasset_options& new_options ) + { new_options.force_settlement_delay_sec = 100; + new_options.force_settlement_offset_percent = GRAPHENE_1_PERCENT; } ); + + // Force settlement is disabled; check that it fails + GRAPHENE_REQUIRE_THROW( force_settle( nathan_id, asset( 50, bitusd_id ) ), fc::exception ); + + update_asset_options( bitusd_id, [&]( asset_options& new_options ) + { new_options.flags &= ~disable_force_settle; } ); + + // Can't settle more BitUSD than you own + GRAPHENE_REQUIRE_THROW( force_settle( nathan_id, asset( 999999, bitusd_id ) ), fc::exception ); + + // settle3 should be least collateralized order according to index + BOOST_CHECK( db.get_index_type().indices().get().begin()->id == call3_id ); + BOOST_CHECK_EQUAL( call3_id(db).debt.value, 3000 ); + + BOOST_TEST_MESSAGE( "Verify partial settlement of call" ); + // Partially settle a call + force_settlement_id_type settle_id = force_settle( nathan_id, asset( 50, bitusd_id ) ).get< object_id_type >(); + + // Call does not take effect immediately + BOOST_CHECK_EQUAL( get_balance(nathan_id, bitusd_id), 14950); + BOOST_CHECK_EQUAL( settle_id(db).balance.amount.value, 50); + BOOST_CHECK_EQUAL( call3_id(db).debt.value, 3000 ); + BOOST_CHECK_EQUAL( call3_id(db).collateral.value, 5780 ); + BOOST_CHECK( settle_id(db).owner == nathan_id ); + + // Wait for settlement to take effect + generate_blocks(settle_id(db).settlement_date); + BOOST_CHECK(db.find(settle_id) == nullptr); + BOOST_CHECK_EQUAL( get_balance(nathan_id, bitusd_id), 14950); + BOOST_CHECK_EQUAL( get_balance(nathan_id, core_id), 49 ); // 1% force_settlement_offset_percent (rounded unfavorably) + BOOST_CHECK_EQUAL( call3_id(db).debt.value, 2950 ); + BOOST_CHECK_EQUAL( call3_id(db).collateral.value, 5731 ); // 5731 == 5780-49 + + BOOST_CHECK( db.get_index_type().indices().get().begin()->id == call3_id ); + + BOOST_TEST_MESSAGE( "Verify pending settlement is cancelled when asset's force_settle is disabled" ); + // Ensure pending settlement is cancelled when force settle is disabled + settle_id = force_settle( nathan_id, asset( 50, bitusd_id ) ).get< object_id_type >(); + + BOOST_CHECK( !db.get_index_type().indices().empty() ); + update_asset_options( bitusd_id, [&]( asset_options& new_options ) + { new_options.flags |= disable_force_settle; } ); + BOOST_CHECK( db.get_index_type().indices().empty() ); + update_asset_options( bitusd_id, [&]( asset_options& new_options ) + { new_options.flags &= ~disable_force_settle; } ); + + BOOST_TEST_MESSAGE( "Perform iterative settlement" ); + settle_id = force_settle( nathan_id, asset( 12500, bitusd_id ) ).get< object_id_type >(); + + // c3 2950 : 5731 1.9427 fully settled + // c5 5000 : 9800 1.9600 fully settled + // c4 4000 : 7900 1.9750 fully settled + // c2 2000 : 3998 1.9990 550 settled + // c1 1000 : 2000 2.0000 + + generate_blocks( settle_id(db).settlement_date ); + + int64_t call1_payout = 0; + int64_t call2_payout = 550*99/100; + int64_t call3_payout = 49 + 2950*99/100; + int64_t call4_payout = 4000*99/100; + int64_t call5_payout = 5000*99/100; + + BOOST_CHECK_EQUAL( get_balance(shorter1_id, core_id), initial_balance-2*1000 ); // full collat still tied up + BOOST_CHECK_EQUAL( get_balance(shorter2_id, core_id), initial_balance-2*1999 ); // full collat still tied up + BOOST_CHECK_EQUAL( get_balance(shorter3_id, core_id), initial_balance-call3_payout ); // initial balance minus transfer to Nathan (as BitUSD) + BOOST_CHECK_EQUAL( get_balance(shorter4_id, core_id), initial_balance-call4_payout ); // initial balance minus transfer to Nathan (as BitUSD) + BOOST_CHECK_EQUAL( get_balance(shorter5_id, core_id), initial_balance-call5_payout ); // initial balance minus transfer to Nathan (as BitUSD) + + BOOST_CHECK_EQUAL( get_balance(nathan_id, core_id), + call1_payout + call2_payout + call3_payout + call4_payout + call5_payout ); + + BOOST_CHECK( db.find(call3_id) == nullptr ); + BOOST_CHECK( db.find(call4_id) == nullptr ); + BOOST_CHECK( db.find(call5_id) == nullptr ); + + BOOST_REQUIRE( db.find(call1_id) != nullptr ); + BOOST_REQUIRE( db.find(call2_id) != nullptr ); + + BOOST_CHECK_EQUAL( call1_id(db).debt.value, 1000 ); + BOOST_CHECK_EQUAL( call1_id(db).collateral.value, 2000 ); + + BOOST_CHECK_EQUAL( call2_id(db).debt.value, 2000-550 ); + BOOST_CHECK_EQUAL( call2_id(db).collateral.value, 3998-call2_payout ); } - generate_block(); - - create_short(shorter1_id(db), asset(1000, bit_usd), asset(1000)); - create_sell_order(nathan_id(db), asset(1000), asset(1000, bit_usd)); - create_short(shorter2_id(db), asset(2000, bit_usd), asset(1999)); - create_sell_order(nathan_id(db), asset(1999), asset(2000, bit_usd)); - create_short(shorter3_id(db), asset(3000, bit_usd), asset(2990)); - create_sell_order(nathan_id(db), asset(2990), asset(3000, bit_usd)); - BOOST_CHECK_EQUAL(get_balance(nathan_id, bit_usd), 6000); - - transfer(nathan_id(db), account_id_type()(db), db.get_balance(nathan_id, asset_id_type())); - + catch(fc::exception& e) { - asset_update_bitasset_operation uop; - uop.issuer = bit_usd(db).issuer; - uop.asset_to_update = bit_usd; - uop.new_options = bit_usd(db).bitasset_data(db).options; - uop.new_options.force_settlement_delay_sec = 100; - uop.new_options.force_settlement_offset_percent = 100; - trx.operations.push_back(uop); - } { - asset_update_feed_producers_operation uop; - uop.asset_to_update = bit_usd; - uop.issuer = bit_usd(db).issuer; - uop.new_feed_producers = {nathan_id}; - trx.operations.push_back(uop); - } { - asset_publish_feed_operation pop; - pop.asset_id = bit_usd; - pop.publisher = nathan_id; - price_feed feed; - feed.settlement_price = price(asset(1),asset(1, bit_usd)); - feed.call_limit = price::min(0, bit_usd); - pop.feed = feed; - trx.operations.push_back(pop); + edump((e.to_detail_string())); + throw; } - trx.sign(key_id_type(),private_key); - PUSH_TX( db, trx ); - trx.clear(); - - asset_settle_operation sop; - sop.account = nathan_id; - sop.amount = asset(50, bit_usd); - trx.operations = {sop}; - //Force settlement is disabled; check that it fails - GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, ~0 ), fc::exception); - { - //Enable force settlement - asset_update_operation op; - op.issuer = bit_usd(db).issuer; - op.asset_to_update = bit_usd; - op.new_options = bit_usd(db).options; - op.new_options.flags &= ~disable_force_settle; - trx.operations = {op}; - trx.sign(key_id_type(), private_key); - PUSH_TX( db, trx ); - trx.operations = {sop}; - } - REQUIRE_THROW_WITH_VALUE(sop, amount, asset(999999, bit_usd)); - trx.operations.back() = sop; - trx.sign(key_id_type(),private_key); - - //Partially settle a call - force_settlement_id_type settle_id = PUSH_TX( db, trx ).operation_results.front().get(); - trx.clear(); - call_order_id_type call_id = db.get_index_type().indices().get().begin()->id; - BOOST_CHECK_EQUAL(settle_id(db).balance.amount.value, 50); - BOOST_CHECK_EQUAL(call_id(db).debt.value, 3000); - BOOST_CHECK(settle_id(db).owner == nathan_id); - - generate_blocks(settle_id(db).settlement_date); - BOOST_CHECK(db.find(settle_id) == nullptr); - BOOST_CHECK_EQUAL(get_balance(nathan_id, asset_id_type()), 49); - BOOST_CHECK_EQUAL(call_id(db).debt.value, 2950); - - { - //Disable force settlement - asset_update_operation op; - op.issuer = bit_usd(db).issuer; - op.asset_to_update = bit_usd; - op.new_options = bit_usd(db).options; - op.new_options.flags |= disable_force_settle; - trx.operations.push_back(op); - trx.set_reference_block(db.head_block_id()); trx.set_expiration( db.head_block_time() + fc::seconds( 3 * db.get_global_properties().parameters.block_interval ) ); - trx.sign(key_id_type(), private_key); - PUSH_TX( db, trx ); - //Check that force settlements were all canceled - BOOST_CHECK(db.get_index_type().indices().empty()); - BOOST_CHECK_EQUAL(get_balance(nathan_id, bit_usd), bit_usd(db).dynamic_data(db).current_supply.value); - } - } FC_LOG_AND_RETHROW() - */ } BOOST_AUTO_TEST_CASE( assert_op_test )