diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 2c7a17fd..e8b4b0fe 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -144,7 +144,12 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& database& d = db(); const asset_object& a = o.amount_to_reserve.asset_id(d); - FC_ASSERT( !a.is_market_issued() ); + GRAPHENE_ASSERT( + !a.is_market_issued(), + asset_reserve_invalid_on_mia, + "Cannot reserve ${sym} because it is a market-issued asset", + ("sym", a.symbol) + ); from_account = &o.payer(d); diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index 05bde928..dc993912 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -90,6 +90,7 @@ namespace graphene { namespace chain { GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( call_order_update ); GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( unfilled_margin_call, call_order_update, 1, "Updating call order would trigger a margin call that cannot be fully filled" ) + //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_create ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_update ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( account_whitelist ); @@ -100,7 +101,11 @@ namespace graphene { namespace chain { //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_bitasset ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_update_feed_producers ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_issue ); - //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve ); + + GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_reserve ); + + GRAPHENE_DECLARE_OP_EVALUATE_EXCEPTION( invalid_on_mia, asset_reserve, 1, "invalid on mia" ) + //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_fund_fee_pool ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_settle ); //GRAPHENE_DECLARE_OP_BASE_EXCEPTIONS( asset_global_settle ); diff --git a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp index 5cdc6661..d64f4474 100644 --- a/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp +++ b/libraries/chain/include/graphene/chain/protocol/asset_ops.hpp @@ -366,7 +366,7 @@ namespace graphene { namespace chain { * @brief used to take an asset out of circulation, returning to the issuer * @ingroup operations * - * @note You cannot burn market-issued assets. + * @note You cannot use this operation on market-issued assets. */ struct asset_reserve_operation : public base_operation { diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index dad31ead..08d12e1a 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -1217,6 +1217,91 @@ BOOST_AUTO_TEST_CASE( witness_pay_test ) } FC_LOG_AND_RETHROW() } +/** + * Reserve asset test should make sure that all assets except bitassets + * can be burned, and all supplies add up. + */ +BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( reserve_asset_test, 1 ) +BOOST_AUTO_TEST_CASE( reserve_asset_test ) +{ + try + { + ACTORS((alice)(bob)(sam)(judge)); + const auto& basset = create_bitasset("BITUSD"); + const auto& uasset = create_user_issued_asset("TEST"); + const auto& passet = create_prediction_market("PMARK", judge_id); + const auto& casset = asset_id_type()(db); + + auto reserve_asset = [&]( account_id_type payer, asset amount_to_reserve ) + { + asset_reserve_operation op; + op.payer = payer; + op.amount_to_reserve = amount_to_reserve; + transaction tx; + tx.operations.push_back( op ); + tx.set_expiration( db.head_block_time() + fc::minutes(5) ); + db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + } ; + + auto _issue_uia = [&]( const account_object& recipient, asset amount ) + { + asset_issue_operation op; + op.issuer = amount.asset_id(db).issuer; + op.asset_to_issue = amount; + op.issue_to_account = recipient.id; + transaction tx; + tx.operations.push_back( op ); + tx.set_expiration( db.head_block_time() + fc::minutes(5) ); + db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures ); + } ; + + int64_t init_balance = 10000; + int64_t reserve_amount = 3000; + share_type initial_reserve; + + BOOST_TEST_MESSAGE( "Test reserve operation on core asset" ); + transfer( committee_account, alice_id, casset.amount( init_balance ) ); + + initial_reserve = casset.reserved( db ); + reserve_asset( alice_id, casset.amount( reserve_amount ) ); + BOOST_CHECK_EQUAL( get_balance( alice, casset ), init_balance - reserve_amount ); + BOOST_CHECK_EQUAL( (casset.reserved( db ) - initial_reserve).value, reserve_amount ); + verify_asset_supplies(db); + + BOOST_TEST_MESSAGE( "Test reserve operation on market issued asset" ); + transfer( committee_account, alice_id, casset.amount( init_balance*100 ) ); + update_feed_producers( basset, {sam.id} ); + price_feed current_feed; + current_feed.settlement_price = basset.amount( 2 ) / casset.amount(100); + publish_feed( basset, sam, current_feed ); + borrow( alice_id, basset.amount( init_balance ), casset.amount( 100*init_balance ) ); + BOOST_CHECK_EQUAL( get_balance( alice, basset ), init_balance ); + + GRAPHENE_REQUIRE_THROW( reserve_asset( alice_id, basset.amount( reserve_amount ) ), asset_reserve_invalid_on_mia ); + + BOOST_TEST_MESSAGE( "Test reserve operation on prediction market asset" ); + transfer( committee_account, alice_id, casset.amount( init_balance ) ); + borrow( alice_id, passet.amount( init_balance ), casset.amount( init_balance ) ); + GRAPHENE_REQUIRE_THROW( reserve_asset( alice_id, passet.amount( reserve_amount ) ), asset_reserve_invalid_on_mia ); + + BOOST_TEST_MESSAGE( "Test reserve operation on user issued asset" ); + _issue_uia( alice, uasset.amount( init_balance ) ); + BOOST_CHECK_EQUAL( get_balance( alice, uasset ), init_balance ); + verify_asset_supplies(db); + + BOOST_TEST_MESSAGE( "Reserving asset" ); + initial_reserve = uasset.reserved( db ); + reserve_asset( alice_id, uasset.amount( reserve_amount ) ); + BOOST_CHECK_EQUAL( get_balance( alice, uasset ), init_balance - reserve_amount ); + BOOST_CHECK_EQUAL( (uasset.reserved( db ) - initial_reserve).value, reserve_amount ); + verify_asset_supplies(db); + } + catch (fc::exception& e) + { + edump((e.to_detail_string())); + throw; + } +} /** * This test demonstrates how using the call_order_update_operation to