diff --git a/libraries/chain/asset_evaluator.cpp b/libraries/chain/asset_evaluator.cpp index 9194a02d25..2ed6aaeb11 100644 --- a/libraries/chain/asset_evaluator.cpp +++ b/libraries/chain/asset_evaluator.cpp @@ -288,6 +288,12 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o) FC_ASSERT(!((o.new_options.flags ^ a.options.flags) & ~a.options.issuer_permissions), "Flag change is forbidden by issuer permissions"); + // TODO HARDFORKCHECK + // maximum supply can only be changed if the disable_modify_max_supply flag is 0 + FC_ASSERT( ( a.options.max_supply == o.new_options.max_supply + || ( a.options.flags & disable_modify_max_supply ) == 0 ), + "Modification of the maximum supply is forbidden" ); + asset_to_update = &a; FC_ASSERT( o.issuer == a.issuer, "Incorrect issuer for asset! (${o.issuer} != ${a.issuer})", diff --git a/libraries/chain/include/graphene/chain/asset_object.hpp b/libraries/chain/include/graphene/chain/asset_object.hpp index 5f73e79ddd..9bcbe4f66c 100644 --- a/libraries/chain/include/graphene/chain/asset_object.hpp +++ b/libraries/chain/include/graphene/chain/asset_object.hpp @@ -96,6 +96,8 @@ namespace graphene { namespace chain { bool is_transfer_restricted()const { return options.flags & transfer_restricted; } bool can_override()const { return options.flags & override_authority; } bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); } + /// @return true if this asset can modify the max supply + bool can_modify_max_supply() const { return !(options.flags & asset_issuer_permission_flags::disable_modify_max_supply); } /// Helper function to get an asset object with the given amount in this asset's type asset amount(share_type a)const { return asset(a, id); } diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index 4e96abf9f9..01a69b34c7 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -98,11 +98,12 @@ namespace graphene { namespace chain { global_settle = 0x20, /**< allow the bitasset issuer to force a global settling -- this may be set in permissions, but not flags */ disable_confidential = 0x40, /**< allow the asset to be used with confidential transactions */ witness_fed_asset = 0x80, /**< allow the asset to be fed by witnesses */ - committee_fed_asset = 0x100 /**< allow the asset to be fed by the committee */ + committee_fed_asset = 0x100,/**< allow the asset to be fed by the committee */ + disable_modify_max_supply = 0x200 /**< disable the modification of the maximum supply; can only be activated once */ }; const static uint32_t ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_force_settle|global_settle|disable_confidential - |witness_fed_asset|committee_fed_asset; - const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential; + |witness_fed_asset|committee_fed_asset|disable_modify_max_supply; + const static uint32_t UIA_ASSET_ISSUER_PERMISSION_MASK = charge_market_fee|white_list|override_authority|transfer_restricted|disable_confidential|disable_modify_max_supply; enum reserved_spaces { @@ -417,4 +418,5 @@ FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (disable_confidential) (witness_fed_asset) (committee_fed_asset) + (disable_modify_max_supply) ) diff --git a/tests/tests/operation_tests.cpp b/tests/tests/operation_tests.cpp index b2edab15ad..b35ed61992 100644 --- a/tests/tests/operation_tests.cpp +++ b/tests/tests/operation_tests.cpp @@ -2357,6 +2357,133 @@ BOOST_AUTO_TEST_CASE( vesting_balance_withdraw_test ) // TODO: Test with non-core asset and Bob account } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( disable_modify_max_supply_flag_test ) +{ + // latest hf + // generate_blocks( HARDFORK_CORE_942_TIME ); + + ACTOR( alice ); + account_id_type committee_acc; + transfer( committee_acc, alice_id, asset(100000000) ); + + signed_transaction trx; + string SYMBOL = "TEST"; + asset_object test_asset; + asset_id_type test_asset_id; + + edump( ("Creating an asset disable_modify_max_supply_flag == 0") ); + { + asset_options a_opt; + a_opt.max_supply = 1000; + a_opt.core_exchange_rate = price( asset( 4, asset_id_type(1) ), asset( 4, asset_id_type() ) ); + + asset_create_operation acop; + acop.issuer = alice_id; + acop.symbol = SYMBOL; + acop.common_options = a_opt; + + set_expiration(db, trx); + trx.operations.push_back( acop ); + PUSH_TX( db, trx, ~0 ); + + // getting asset_obj from db + const auto& idx = db.get_index_type(); + idx.inspect_all_objects( [&](const object& obj){ + const asset_object& ao = static_cast(obj); + if(ao.symbol == SYMBOL){ + test_asset = ao; + test_asset_id = ao.id; + } + } ); + + BOOST_CHECK( test_asset.can_modify_max_supply() ); + } + + edump( ( "Modifying the maximum supply. disable_modify_max_supply flag == 0" ) ); + { + asset_options a_opt; + a_opt.max_supply = 2000; + a_opt.core_exchange_rate = price( asset(4, asset_id_type(2) ), asset(4, asset_id_type(0) ) ); + + asset_update_operation auop; + auop.fee = asset(0); + auop.issuer = alice_id; + auop.asset_to_update = test_asset_id; + auop.new_options = a_opt; + + trx = signed_transaction(); + set_expiration(db, trx); + trx.operations.push_back( auop ); + PUSH_TX( db, trx, ~0 ); + + test_asset = db.get(test_asset_id); + BOOST_CHECK( test_asset.options.max_supply == 2000 ); + } + + edump( ( "Setting disable_modify_max_supply_flag. disable_modify_max_supply flag == 0x200" ) ); + { + asset_options a_opt; + a_opt.max_supply = 2000; + a_opt.core_exchange_rate = price( asset(4, asset_id_type(2) ), asset(4, asset_id_type(0) ) ); + a_opt.flags = asset_issuer_permission_flags::disable_modify_max_supply; + + asset_update_operation auop; + auop.fee = asset(0); + auop.issuer = alice_id; + auop.asset_to_update = test_asset_id; + auop.new_options = a_opt; + + trx = signed_transaction(); + set_expiration(db, trx); + trx.operations.push_back( auop ); + PUSH_TX( db, trx, ~0 ); + + test_asset = db.get(test_asset_id); + BOOST_CHECK( ( test_asset.options.flags)// & asset_issuer_permission_flags::disable_modify_max_supply ) + == asset_issuer_permission_flags::disable_modify_max_supply ); + BOOST_CHECK( !test_asset.can_modify_max_supply() ); + } + + edump( ( "Trying to disable the disable_modify_max_supply_flag.") ); + { + asset_options a_opt; + a_opt.max_supply = 2000; + a_opt.core_exchange_rate = price( asset(4, asset_id_type(2) ), asset(4, asset_id_type(0) ) ); + a_opt.flags = 0; + + asset_update_operation auop; + auop.fee = asset(0); + auop.issuer = alice_id; + auop.asset_to_update = test_asset.get_id(); + auop.new_options = a_opt; + + trx = signed_transaction(); + set_expiration(db, trx); + trx.operations.push_back( auop ); + + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception ); + } + + edump( ( "Trying to change the maximum supply." ) ); + { + asset_options a_opt; + a_opt.max_supply = 1000; + a_opt.core_exchange_rate = price( asset(4, asset_id_type(2) ), asset(4, asset_id_type(0) ) ); + a_opt.flags = asset_issuer_permission_flags::disable_modify_max_supply; + + asset_update_operation auop; + auop.fee = asset(0); + auop.issuer = alice_id; + auop.asset_to_update = test_asset.get_id(); + auop.new_options = a_opt; + + trx = signed_transaction(); + set_expiration(db, trx); + trx.operations.push_back( auop ); + + GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception ); + } +} // TODO: Write linear VBO tests BOOST_AUTO_TEST_SUITE_END()