Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check max_supply before borrowing MPAs #1498

Merged
merged 9 commits into from
Jan 25, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions libraries/chain/db_maint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,30 @@ void database::process_bitassets()
}
}

/****
* @brief a one-time data process to correct max_supply
*/
void process_hf_1465( database& db )
{
const auto head_time = db.head_block_time();
const auto head_num = db.head_block_num();
wlog( "Processing hard fork core-1465 at block ${n}", ("n",head_num) );
// for each market issued asset
const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();
for( auto asset_itr = asset_idx.lower_bound(true); asset_itr != asset_idx.end(); ++asset_itr )
{
const auto& current_asset = *asset_itr;
graphene::chain::share_type current_supply = current_asset.dynamic_data(db).current_supply;
graphene::chain::share_type max_supply = current_asset.options.max_supply;
if (current_supply > max_supply && max_supply != GRAPHENE_MAX_SHARE_SUPPLY)
{
db.modify<asset_object>( current_asset, [current_supply](asset_object& obj) {
obj.options.max_supply = graphene::chain::share_type(std::min(current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY));
jmjatlanta marked this conversation as resolved.
Show resolved Hide resolved
});
}
}
}

/******
* @brief one-time data process for hard fork core-868-890
*
Expand Down Expand Up @@ -1225,6 +1249,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
&& !to_update_and_match_call_orders )
process_hf_935( *this );

// make sure current_supply is less than or equal to max_supply
if ( dgpo.next_maintenance_time <= HARDFORK_CORE_1465_TIME && next_maintenance_time > HARDFORK_CORE_1465_TIME )
process_hf_1465(*this);

modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {
d.next_maintenance_time = next_maintenance_time;
d.accounts_registered_this_interval = 0;
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/hardfork.d/CORE_1465.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// bitshares-core issue #1465 check max_supply before processing call_order_update
#ifndef HARDFORK_CORE_1465_TIME
#define HARDFORK_CORE_1465_TIME (fc::time_point_sec( 1600000000 )) // 2020-09-13 12:26:40
#endif
8 changes: 7 additions & 1 deletion libraries/chain/market_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
{ try {
database& d = db();

auto next_maintenance_time = d.get_dynamic_global_properties().next_maintenance_time;

// TODO: remove this check and the assertion after hf_834
if( d.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_834_TIME )
if( next_maintenance_time <= HARDFORK_CORE_834_TIME )
FC_ASSERT( !o.extensions.value.target_collateral_ratio.valid(),
"Can not set target_collateral_ratio in call_order_update_operation before hardfork 834." );

Expand All @@ -167,6 +169,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
("sym", _debt_asset->symbol) );

FC_ASSERT( next_maintenance_time <= HARDFORK_CORE_1465_TIME
|| _debt_asset->dynamic_data(d).current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply,
abitmore marked this conversation as resolved.
Show resolved Hide resolved
"Borrowing this quantity would exceed MAX_SUPPLY" );

_bitasset_data = &_debt_asset->bitasset_data(d);

/// if there is a settlement for this asset, then no further margin positions may be taken and
Expand Down
2 changes: 1 addition & 1 deletion tests/common/database_fixture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ const asset_object& database_fixture::create_bitasset(
creator.issuer = issuer;
creator.fee = asset();
creator.symbol = name;
creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY / 2;
pmconrad marked this conversation as resolved.
Show resolved Hide resolved
creator.precision = precision;
creator.common_options.market_fee_percent = market_fee_percent;
if( issuer == GRAPHENE_WITNESS_ACCOUNT )
Expand Down
105 changes: 104 additions & 1 deletion tests/tests/operation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,6 @@ BOOST_AUTO_TEST_CASE( call_order_update_validation_test )

op.extensions.value.target_collateral_ratio = 65535;
op.validate(); // still valid

}

// Tests that target_cr option can't be set before hard fork core-834
Expand Down Expand Up @@ -2004,6 +2003,110 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test )
}
}

BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test )
{
try
{
ACTORS( (alice) (bob) );
transfer(committee_account, alice_id, asset(10000000 * GRAPHENE_BLOCKCHAIN_PRECISION));

const auto& core = asset_id_type()(db);

// attempt to increase current supply beyond max_supply
const auto& bitjmj = create_bitasset( "JMJBIT", alice_id );
share_type original_max_supply = bitjmj.options.max_supply;

{
BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" );
update_feed_producers( bitjmj, {alice_id} );
price_feed current_feed;
current_feed.settlement_price = bitjmj.amount( 100000 ) / core.amount(1);
publish_feed( bitjmj, alice, current_feed );
}

{
BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" );
call_order_update_operation op;
op.funding_account = alice_id;
op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION );
op.delta_debt = asset( bitjmj.options.max_supply + 1, bitjmj.id );
transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );
generate_block();
}

// advance past hardfork
generate_blocks( HARDFORK_CORE_1465_TIME );
set_expiration( db, trx );

// bitjmj should have its problem corrected
auto newbitjmj = bitjmj.get_id()(db);
jmjatlanta marked this conversation as resolved.
Show resolved Hide resolved
BOOST_REQUIRE_GT(newbitjmj.options.max_supply.value, original_max_supply.value);

// now try with an asset after the hardfork
const auto& bitusd = create_bitasset( "USDBIT", alice_id );

{
BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" );
update_feed_producers( bitusd, {alice_id} );
price_feed current_feed;
current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1);
publish_feed( bitusd, alice, current_feed );
}

{
BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" );
call_order_update_operation op;
op.funding_account = alice_id;
op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION );
op.delta_debt = asset( bitusd.options.max_supply + 1, bitusd.id );
transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception );
}

{
BOOST_TEST_MESSAGE( "Creating 2 bitusd and transferring to bob (increases current supply)" );
call_order_update_operation op;
op.funding_account = alice_id;
op.delta_collateral = asset( 100 * GRAPHENE_BLOCKCHAIN_PRECISION );
op.delta_debt = asset( 2, bitusd.id );
transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );
transfer( alice, bob, asset( 2, bitusd.id ) );
}

{
BOOST_TEST_MESSAGE( "Again attempting a call_order_update_operation that is max_supply - 1 (should throw)" );
call_order_update_operation op;
op.funding_account = alice_id;
op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION );
op.delta_debt = asset( bitusd.options.max_supply - 1, bitusd.id );
transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception);
}

{
BOOST_TEST_MESSAGE( "Again attempting a call_order_update_operation that equals max_supply (should work)" );
call_order_update_operation op;
op.funding_account = alice_id;
op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION );
op.delta_debt = asset( bitusd.options.max_supply - 2, bitusd.id );
transaction tx;
tx.operations.push_back( op );
set_expiration( db, tx );
PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );
}
} FC_LOG_AND_RETHROW()
}

/**
* This test demonstrates how using the call_order_update_operation to
* trigger a margin call is legal if there is a matching order.
Expand Down