diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 0d948f8ae7..889f62739a 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -602,8 +602,9 @@ void database::clear_expired_htlcs() } } -void database::process_tickets() +generic_operation_result database::process_tickets() { + generic_operation_result result; share_type total_delta_pob; share_type total_delta_inactive; auto& idx = get_index_type().indices().get(); @@ -619,6 +620,7 @@ void database::process_tickets() aso.total_core_pol -= ticket.amount.amount; aso.total_pol_value -= ticket.value; }); + result.removed_objects.insert( ticket.id ); remove( ticket ); } else @@ -628,6 +630,7 @@ void database::process_tickets() modify( ticket, []( ticket_object& o ) { o.auto_update(); }); + result.updated_objects.insert( ticket.id ); share_type delta_inactive_amount; share_type delta_forever_amount; @@ -687,6 +690,8 @@ void database::process_tickets() dgp.total_inactive += total_delta_inactive; }); } + + return result; } } } diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 1468e23cc3..24ef7b6180 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -556,7 +556,7 @@ namespace graphene { namespace chain { //////////////////// db_block.cpp //////////////////// - public: + public: // these were formerly private, but they have a fairly well-defined API, so let's make them public void apply_block( const signed_block& next_block, uint32_t skip = skip_nothing ); processed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); @@ -581,6 +581,9 @@ namespace graphene { namespace chain { uint32_t update_witness_missed_blocks( const signed_block& b ); //////////////////// db_update.cpp //////////////////// + public: + generic_operation_result process_tickets(); + private: void update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks ); void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block); void update_last_irreversible_block(); @@ -591,7 +594,6 @@ namespace graphene { namespace chain { void update_core_exchange_rates(); void update_maintenance_flag( bool new_maintenance_flag ); void update_withdraw_permissions(); - void process_tickets(); bool check_for_blackswan( const asset_object& mia, bool enable_black_swan = true, const asset_bitasset_data_object* bitasset_ptr = nullptr ); void clear_expired_htlcs(); diff --git a/libraries/chain/ticket_evaluator.cpp b/libraries/chain/ticket_evaluator.cpp index 3556ac485c..5962e9d28a 100644 --- a/libraries/chain/ticket_evaluator.cpp +++ b/libraries/chain/ticket_evaluator.cpp @@ -123,9 +123,20 @@ generic_operation_result ticket_update_evaluator::do_apply(const ticket_update_o d.modify( stat, [delta_value](account_statistics_object& aso) { aso.total_pol_value += delta_value; }); - result.updated_objects.insert( stat.id ); } + // Do auto-update now. + // Note: calling process_tickets() here won't affect other tickets, + // since head_block_time is not updated after last call, + // even when called via a proposal this time or last time + generic_operation_result process_result = d.process_tickets(); + result.removed_objects.insert( process_result.removed_objects.begin(), process_result.removed_objects.end() ); + result.updated_objects.insert( process_result.updated_objects.begin(), process_result.updated_objects.end() ); + for( const auto id : result.new_objects ) + result.updated_objects.erase( id ); + for( const auto id : result.removed_objects ) + result.updated_objects.erase( id ); + return result; } FC_CAPTURE_AND_RETHROW( (op) ) } diff --git a/tests/tests/pob_tests.cpp b/tests/tests/pob_tests.cpp index 5c3334941d..e0048d843f 100644 --- a/tests/tests/pob_tests.cpp +++ b/tests/tests/pob_tests.cpp @@ -2644,20 +2644,7 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) // downgrade again result = update_ticket( tick_1_id(db), lock_180_days, {} ); BOOST_CHECK_EQUAL( result.new_objects.size(), 0u ); - - // current type should not change - BOOST_CHECK( tick_1_id(db).target_type == lock_180_days ); - BOOST_CHECK( tick_1_id(db).current_type == lock_360_days ); - BOOST_CHECK( tick_1_id(db).status == withdrawing ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 4 ); - BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); - - BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); - BOOST_CHECK( tick_1_id(db).next_auto_update_time == down_time ); - - // generate a block - generate_block(); + BOOST_CHECK_EQUAL( result.updated_objects.size(), 1u ); // the ticket should have downgraded BOOST_CHECK( tick_1_id(db).target_type == lock_180_days ); @@ -2703,15 +2690,32 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) // should have downgraded if not changed to upgrade BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() ); - // cancel charging - result = update_ticket( tick_1_id(db), lock_180_days, {} ); - BOOST_CHECK_EQUAL( result.new_objects.size(), 0u ); + // partially cancel charging + result = update_ticket( tick_1_id(db), lock_180_days, asset(10) ); + BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u ); + BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u ); + BOOST_CHECK_EQUAL( result.removed_objects.size(), 0u ); + BOOST_CHECK( *result.updated_objects.begin() == tick_1_id ); - BOOST_CHECK( tick_1_id(db).target_type == lock_180_days ); + // the new ticket is stable + ticket_id_type tick_2_id = *result.new_objects.begin(); + + BOOST_REQUIRE( db.find( tick_2_id ) ); + BOOST_CHECK( tick_2_id(db).target_type == lock_180_days ); + BOOST_CHECK( tick_2_id(db).current_type == lock_180_days ); + BOOST_CHECK( tick_2_id(db).status == stable ); + BOOST_CHECK( tick_2_id(db).next_auto_update_time == time_point_sec::maximum() ); + BOOST_CHECK( tick_2_id(db).next_type_downgrade_time == time_point_sec::maximum() ); + BOOST_CHECK( tick_2_id(db).amount == asset(10) ); + BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 10 * 2 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // check the remainder + BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); BOOST_CHECK( tick_1_id(db).current_type == lock_180_days ); - BOOST_CHECK( tick_1_id(db).status == withdrawing ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 ); + BOOST_CHECK( tick_1_id(db).status == charging ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 * 2 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 ); @@ -2721,14 +2725,31 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) // generate a block generate_block(); + // no change + BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); + BOOST_CHECK( tick_1_id(db).current_type == lock_180_days ); + BOOST_CHECK( tick_1_id(db).status == charging ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 * 2 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time + 180*86400 ); + // should have downgraded if not changed to upgrade + BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() ); + + // cancel charging + result = update_ticket( tick_1_id(db), lock_180_days, {} ); + BOOST_CHECK_EQUAL( result.new_objects.size(), 0u ); + BOOST_CHECK_EQUAL( result.updated_objects.size(), 1u ); + // the ticket is now stable BOOST_CHECK( tick_1_id(db).target_type == lock_180_days ); BOOST_CHECK( tick_1_id(db).current_type == lock_180_days ); BOOST_CHECK( tick_1_id(db).status == stable ); BOOST_CHECK( tick_1_id(db).next_auto_update_time == time_point_sec::maximum() ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == time_point_sec::maximum() ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 * 2 ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 * 2 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); // downgrade again @@ -2738,14 +2759,14 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) BOOST_CHECK( tick_1_id(db).target_type == liquid ); BOOST_CHECK( tick_1_id(db).current_type == liquid ); BOOST_CHECK( tick_1_id(db).status == withdrawing ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time != time_point_sec::maximum() ); down_time = tick_1_id(db).next_type_downgrade_time; - // 30 days passed + // X days passed, 30 days to downgrade generate_blocks( down_time - fc::days(30) ); set_expiration( db, trx ); @@ -2756,8 +2777,8 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); BOOST_CHECK( tick_1_id(db).current_type == liquid ); BOOST_CHECK( tick_1_id(db).status == charging ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); @@ -2769,8 +2790,8 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) BOOST_CHECK( tick_1_id(db).target_type == liquid ); BOOST_CHECK( tick_1_id(db).current_type == liquid ); BOOST_CHECK( tick_1_id(db).status == withdrawing ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); @@ -2786,8 +2807,8 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); BOOST_CHECK( tick_1_id(db).current_type == liquid ); BOOST_CHECK( tick_1_id(db).status == charging ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); @@ -2800,24 +2821,33 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); BOOST_CHECK( tick_1_id(db).current_type == liquid ); BOOST_CHECK( tick_1_id(db).status == charging ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 ); + BOOST_CHECK( tick_1_id(db).amount == asset(90) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 90 ); BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); // should have freed if not changed to upgrade BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() ); - // cancel charging - result = update_ticket( tick_1_id(db), liquid, {} ); - BOOST_CHECK_EQUAL( result.new_objects.size(), 0u ); + // partially cancel charging + result = update_ticket( tick_1_id(db), liquid, asset(15) ); + BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u ); + BOOST_REQUIRE_EQUAL( result.updated_objects.size(), 1u ); + BOOST_REQUIRE_EQUAL( result.removed_objects.size(), 1u ); + BOOST_CHECK( *result.updated_objects.begin() == tick_1_id ); - BOOST_CHECK( tick_1_id(db).target_type == liquid ); + // the new created ticket is freed already + ticket_id_type tick_3_id = *result.new_objects.begin(); + BOOST_CHECK( *result.removed_objects.begin() == tick_3_id ); + BOOST_CHECK( !db.find( tick_3_id ) ); + + // check the remainder + BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); BOOST_CHECK( tick_1_id(db).current_type == liquid ); - BOOST_CHECK( tick_1_id(db).status == withdrawing ); - BOOST_CHECK( tick_1_id(db).amount == asset(100) ); - BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 100 ); - BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + BOOST_CHECK( tick_1_id(db).status == charging ); + BOOST_CHECK( tick_1_id(db).amount == asset(75) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 75 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 15 ); BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); // should have freed if not changed to upgrade @@ -2826,10 +2856,37 @@ BOOST_AUTO_TEST_CASE( update_from_withdrawing_to_charging_then_withdraw_again ) // generate a block generate_block(); - // the ticket is now freed + // no change + BOOST_CHECK( tick_1_id(db).target_type == lock_720_days ); + BOOST_CHECK( tick_1_id(db).current_type == liquid ); + BOOST_CHECK( tick_1_id(db).status == charging ); + BOOST_CHECK( tick_1_id(db).amount == asset(75) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 75 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 15 ); + + BOOST_CHECK( tick_1_id(db).next_type_downgrade_time == down_time ); + // should have freed if not changed to upgrade + BOOST_CHECK( tick_1_id(db).next_type_downgrade_time < db.head_block_time() ); + + // cancel charging + result = update_ticket( tick_1_id(db), liquid, {} ); + BOOST_CHECK_EQUAL( result.new_objects.size(), 0u ); + BOOST_CHECK_EQUAL( result.updated_objects.size(), 0u ); + BOOST_REQUIRE_EQUAL( result.removed_objects.size(), 1u ); + BOOST_CHECK( *result.removed_objects.begin() == tick_1_id ); + + // the ticket is freed BOOST_CHECK( !db.find( tick_1_id ) ); - BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 90 ); + + // generate a block + generate_block(); + + // the ticket is freed + BOOST_CHECK( !db.find( tick_1_id ) ); + + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 90 ); } FC_LOG_AND_RETHROW() }