diff --git a/libraries/chain/db_maint.cpp b/libraries/chain/db_maint.cpp index 0842a74975..ee9a9ecd79 100644 --- a/libraries/chain/db_maint.cpp +++ b/libraries/chain/db_maint.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -1091,6 +1092,22 @@ void delete_expired_custom_authorities( database& db ) db.remove(*index.begin()); } +/// A one-time data process to set values of existing liquid tickets to zero. +void process_hf_2262( database& db ) +{ + for( const auto& ticket_obj : db.get_index_type().indices().get() ) + { + if( ticket_obj.current_type != liquid ) // only update liquid tickets + continue; + db.modify( db.get_account_stats_by_owner( ticket_obj.account ), [&ticket_obj](account_statistics_object& aso) { + aso.total_pol_value -= ticket_obj.value; + }); + db.modify( ticket_obj, []( ticket_object& t ) { + t.value = 0; + }); + } +} + namespace detail { struct vote_recalc_times @@ -1180,6 +1197,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g const dynamic_global_property_object& dprops; const time_point_sec now; const bool hf2103_passed; + const bool hf2262_passed; const bool pob_activated; optional witness_recalc_times; @@ -1190,6 +1208,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g vote_tally_helper( database& db ) : d(db), props( d.get_global_properties() ), dprops( d.get_dynamic_global_properties() ), now( d.head_block_time() ), hf2103_passed( HARDFORK_CORE_2103_PASSED( now ) ), + hf2262_passed( HARDFORK_CORE_2262_PASSED( now ) ), pob_activated( dprops.total_pob > 0 || dprops.total_inactive > 0 ) { d._vote_tally_buffer.resize( props.next_available_vote_id, 0 ); @@ -1224,8 +1243,9 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g uint64_t voting_stake[3]; // 0=committee, 1=witness, 2=worker, as in vote_id_type::vote_type uint64_t num_committee_voting_stake; // number of committee members voting_stake[2] = ( pob_activated ? 0 : stats.total_core_in_orders.value ) - + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0) - + stats.core_in_balance.value; + + ( ( !hf2262_passed && stake_account.cashback_vb.valid() ) ? + (*stake_account.cashback_vb)(d).balance.amount.value : 0 ) + + ( hf2262_passed ? 0 : stats.core_in_balance.value ); // voting power stats uint64_t vp_all = 0; ///< all voting power. @@ -1448,6 +1468,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g if ( dgpo.next_maintenance_time <= HARDFORK_CORE_2103_TIME && next_maintenance_time > HARDFORK_CORE_2103_TIME ) process_hf_2103(*this); + // Update tickets. Note: the new values will take effect only on the next maintenance interval + if ( dgpo.next_maintenance_time <= HARDFORK_CORE_2262_TIME && next_maintenance_time > HARDFORK_CORE_2262_TIME ) + process_hf_2262(*this); + modify(dgpo, [last_vote_tally_time, next_maintenance_time](dynamic_global_property_object& d) { d.next_maintenance_time = next_maintenance_time; d.last_vote_tally_time = last_vote_tally_time; diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index 889f62739a..7c0fec421c 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -604,6 +604,9 @@ void database::clear_expired_htlcs() generic_operation_result database::process_tickets() { + const auto maint_time = get_dynamic_global_properties().next_maintenance_time; + ticket_version version = ( HARDFORK_CORE_2262_PASSED(maint_time) ? ticket_v2 : ticket_v1 ); + generic_operation_result result; share_type total_delta_pob; share_type total_delta_inactive; @@ -627,8 +630,8 @@ generic_operation_result database::process_tickets() { ticket_type old_type = ticket.current_type; share_type old_value = ticket.value; - modify( ticket, []( ticket_object& o ) { - o.auto_update(); + modify( ticket, [version]( ticket_object& o ) { + o.auto_update( version ); }); result.updated_objects.insert( ticket.id ); diff --git a/libraries/chain/hardfork.d/CORE_2262.hf b/libraries/chain/hardfork.d/CORE_2262.hf new file mode 100644 index 0000000000..9dabc8c188 --- /dev/null +++ b/libraries/chain/hardfork.d/CORE_2262.hf @@ -0,0 +1,6 @@ +// bitshares-core issue #2262: Remove voting power from liquid BTS and tickets +#ifndef HARDFORK_CORE_2262_TIME +// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled +#define HARDFORK_CORE_2262_TIME (fc::time_point_sec( 1893456000 )) +#define HARDFORK_CORE_2262_PASSED(next_maint_time) (next_maint_time > HARDFORK_CORE_2262_TIME) +#endif diff --git a/libraries/chain/include/graphene/chain/ticket_object.hpp b/libraries/chain/include/graphene/chain/ticket_object.hpp index 04c29d3e8c..d6c18e93e1 100644 --- a/libraries/chain/include/graphene/chain/ticket_object.hpp +++ b/libraries/chain/include/graphene/chain/ticket_object.hpp @@ -46,6 +46,13 @@ enum ticket_status TICKET_STATUS_COUNT }; +/// Version of a ticket +enum ticket_version +{ + ticket_v1 = 1, + ticket_v2 = 2 +}; + /** * @brief a ticket for governance voting * @ingroup object @@ -79,31 +86,33 @@ class ticket_object : public abstract_object static constexpr uint32_t _seconds_to_downgrade[] = { 180 * 86400, 180 * 86400, 360 * 86400 }; return _seconds_to_downgrade[ static_cast(i) ]; } - static uint8_t value_multiplier( ticket_type i ) { - static constexpr uint32_t _value_multiplier[] = { 1, 2, 4, 8, 8, 0 }; - return _value_multiplier[ static_cast(i) ]; + static uint8_t value_multiplier( ticket_type i, ticket_version version ) { + static constexpr uint32_t _value_multiplier_v1[] = { 1, 2, 4, 8, 8, 0 }; + static constexpr uint32_t _value_multiplier_v2[] = { 0, 2, 4, 8, 8, 0 }; + return ( version == ticket_v1 ? _value_multiplier_v1[ static_cast(i) ] + : _value_multiplier_v2[ static_cast(i) ] ); } /// Initialize member variables for a ticket newly created from account balance void init_new( time_point_sec now, account_id_type new_account, - ticket_type new_target_type, const asset& new_amount ); + ticket_type new_target_type, const asset& new_amount, ticket_version version ); /// Initialize member variables for a ticket split from another ticket void init_split( time_point_sec now, const ticket_object& old_ticket, - ticket_type new_target_type, const asset& new_amount ); + ticket_type new_target_type, const asset& new_amount, ticket_version version ); /// Set a new target type and update member variables accordingly - void update_target_type( time_point_sec now, ticket_type new_target_type ); + void update_target_type( time_point_sec now, ticket_type new_target_type, ticket_version version ); /// Adjust amount and update member variables accordingly - void adjust_amount( const asset& delta_amount ); + void adjust_amount( const asset& delta_amount, ticket_version version ); /// Update the ticket when it's time - void auto_update(); + void auto_update( ticket_version version ); private: /// Recalculate value of the ticket - void update_value(); + void update_value( ticket_version version ); }; diff --git a/libraries/chain/ticket_evaluator.cpp b/libraries/chain/ticket_evaluator.cpp index 5962e9d28a..24eef0ee33 100644 --- a/libraries/chain/ticket_evaluator.cpp +++ b/libraries/chain/ticket_evaluator.cpp @@ -47,11 +47,14 @@ object_id_type ticket_create_evaluator::do_apply(const ticket_create_operation& { try { database& d = db(); const auto block_time = d.head_block_time(); + const auto maint_time = d.get_dynamic_global_properties().next_maintenance_time; + + ticket_version version = ( HARDFORK_CORE_2262_PASSED(maint_time) ? ticket_v2 : ticket_v1 ); d.adjust_balance( op.account, -op.amount ); - const auto& new_ticket_object = d.create([&op,block_time](ticket_object& obj){ - obj.init_new( block_time, op.account, op.target_type, op.amount ); + const auto& new_ticket_object = d.create([&op,block_time,version](ticket_object& obj){ + obj.init_new( block_time, op.account, op.target_type, op.amount, version ); }); // Note: amount.asset_id is checked in validate(), so no check here @@ -88,6 +91,9 @@ generic_operation_result ticket_update_evaluator::do_apply(const ticket_update_o { try { database& d = db(); const auto block_time = d.head_block_time(); + const auto maint_time = d.get_dynamic_global_properties().next_maintenance_time; + + ticket_version version = ( HARDFORK_CORE_2262_PASSED(maint_time) ? ticket_v2 : ticket_v1 ); generic_operation_result result; @@ -97,21 +103,21 @@ generic_operation_result ticket_update_evaluator::do_apply(const ticket_update_o // To partially update the ticket, aka splitting if ( op.amount_for_new_target.valid() && *op.amount_for_new_target < _ticket->amount ) { - const auto& new_ticket_object = d.create([&op,this,block_time](ticket_object& obj){ - obj.init_split( block_time, *_ticket, op.target_type, *op.amount_for_new_target ); + const auto& new_ticket_object = d.create([&op,this,block_time,version](ticket_object& obj){ + obj.init_split( block_time, *_ticket, op.target_type, *op.amount_for_new_target, version ); }); result.new_objects.insert( new_ticket_object.id ); - d.modify( *_ticket, [&op](ticket_object& obj){ - obj.adjust_amount( -(*op.amount_for_new_target) ); + d.modify( *_ticket, [&op,version](ticket_object& obj){ + obj.adjust_amount( -(*op.amount_for_new_target), version ); }); delta_value = new_ticket_object.value + _ticket->value - old_value; } else // To update the whole ticket { - d.modify( *_ticket, [&op,block_time](ticket_object& obj){ - obj.update_target_type( block_time, op.target_type ); + d.modify( *_ticket, [&op,block_time,version](ticket_object& obj){ + obj.update_target_type( block_time, op.target_type, version ); }); delta_value = _ticket->value - old_value; } diff --git a/libraries/chain/ticket_object.cpp b/libraries/chain/ticket_object.cpp index f77a69716c..346d4dd34f 100644 --- a/libraries/chain/ticket_object.cpp +++ b/libraries/chain/ticket_object.cpp @@ -29,7 +29,7 @@ using namespace graphene::chain; void ticket_object::init_new( time_point_sec now, account_id_type new_account, - ticket_type new_target_type, const asset& new_amount ) + ticket_type new_target_type, const asset& new_amount, ticket_version version ) { account = new_account; target_type = new_target_type; @@ -40,11 +40,11 @@ void ticket_object::init_new( time_point_sec now, account_id_type new_account, next_auto_update_time = now + seconds_per_charging_step; next_type_downgrade_time = time_point_sec::maximum(); - update_value(); + update_value( version ); } void ticket_object::init_split( time_point_sec now, const ticket_object& old_ticket, - ticket_type new_target_type, const asset& new_amount ) + ticket_type new_target_type, const asset& new_amount, ticket_version version ) { account = old_ticket.account; target_type = old_ticket.target_type; @@ -55,10 +55,10 @@ void ticket_object::init_split( time_point_sec now, const ticket_object& old_tic next_auto_update_time = old_ticket.next_auto_update_time; next_type_downgrade_time = old_ticket.next_type_downgrade_time; - update_target_type( now, new_target_type ); + update_target_type( now, new_target_type, version ); } -void ticket_object::update_target_type( time_point_sec now, ticket_type new_target_type ) +void ticket_object::update_target_type( time_point_sec now, ticket_type new_target_type, ticket_version version ) { if( current_type < new_target_type ) { @@ -89,16 +89,16 @@ void ticket_object::update_target_type( time_point_sec now, ticket_type new_targ } target_type = new_target_type; - update_value(); + update_value( version ); } -void ticket_object::adjust_amount( const asset& delta_amount ) +void ticket_object::adjust_amount( const asset& delta_amount, ticket_version version ) { amount += delta_amount; - update_value(); + update_value( version ); } -void ticket_object::auto_update() +void ticket_object::auto_update( ticket_version version ) { if( status == charging ) { @@ -119,7 +119,7 @@ void ticket_object::auto_update() { status = withdrawing; next_auto_update_time += seconds_per_lock_forever_update_step; - value = amount.amount * value_multiplier(current_type); + value = amount.amount * value_multiplier( current_type, version ); } } } @@ -128,7 +128,8 @@ void ticket_object::auto_update() // Note: current_type != liquid, guaranteed by the caller if( current_type == lock_forever ) { - share_type delta_value = amount.amount * value_multiplier(current_type) / lock_forever_update_steps; + share_type delta_value = amount.amount * value_multiplier( current_type, version ) + / lock_forever_update_steps; if( delta_value <= 0 ) delta_value = 1; if( value <= delta_value ) @@ -157,14 +158,14 @@ void ticket_object::auto_update() } } - update_value(); + update_value( version ); } -void ticket_object::update_value() +void ticket_object::update_value( ticket_version version ) { if( current_type != lock_forever ) { - value = amount.amount * value_multiplier(current_type); + value = amount.amount * value_multiplier( current_type, version ); } // else lock forever and to be updated, do nothing here } diff --git a/tests/tests/pob_tests.cpp b/tests/tests/pob_tests.cpp index e0048d843f..58e3eebbf3 100644 --- a/tests/tests/pob_tests.cpp +++ b/tests/tests/pob_tests.cpp @@ -3504,4 +3504,228 @@ BOOST_AUTO_TEST_CASE( multiple_tickets ) } } +BOOST_AUTO_TEST_CASE( hf2262_test ) +{ try { + + // Proceed to a time near the core-2262 hard fork. + // Note: only works if the maintenance interval is less than 14 days + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks( HARDFORK_CORE_2262_TIME - mi ); + set_expiration( db, trx ); + + ACTORS((sam)); + + auto init_amount = 10000000 * GRAPHENE_BLOCKCHAIN_PRECISION; + fund( sam, asset(init_amount) ); + + int64_t sam_balance = init_amount; + + // create a ticket + const ticket_object& tick_1 = create_ticket( sam_id, lock_180_days, asset(100) ); + ticket_id_type tick_1_id = tick_1.id; + + BOOST_CHECK( tick_1_id(db).account == sam_id ); + BOOST_CHECK( tick_1_id(db).target_type == lock_180_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 ); + sam_balance -= 100; + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + auto create_time = db.head_block_time(); + + // activate hf2262 + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + generate_block(); + + BOOST_REQUIRE( db.head_block_time() < create_time + fc::days(14) ); + + BOOST_CHECK( tick_1_id(db).account == sam_id ); + BOOST_CHECK( tick_1_id(db).target_type == lock_180_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, 0 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // 14 days passed + generate_blocks( create_time + fc::days(14) ); + set_expiration( db, trx ); + + // no change + BOOST_CHECK( tick_1_id(db).account == sam_id ); + BOOST_CHECK( tick_1_id(db).target_type == lock_180_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, 0 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // unable to update ticket if not to change target type + BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, {} ), fc::exception ); + BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(1) ), fc::exception ); + BOOST_CHECK_THROW( update_ticket( tick_1_id(db), lock_180_days, asset(100) ), fc::exception ); + + // split ticket 1, cancel some + auto result = update_ticket( tick_1_id(db), liquid, asset(6) ); + + BOOST_CHECK( tick_1_id(db).account == sam_id ); + BOOST_CHECK( tick_1_id(db).target_type == lock_180_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(94) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 0 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u ); + + ticket_id_type tick_2_id = *result.new_objects.begin(); + BOOST_CHECK( tick_2_id(db).target_type == liquid ); + BOOST_CHECK( tick_2_id(db).current_type == liquid ); + BOOST_CHECK( tick_2_id(db).status == withdrawing ); + BOOST_CHECK( tick_2_id(db).amount == asset(6) ); + BOOST_CHECK_EQUAL( tick_2_id(db).value.value, 0 ); + + // 1 day passed + generate_blocks( db.head_block_time() + fc::days(1) ); + set_expiration( db, trx ); + + // ticket should be stable now + BOOST_CHECK( tick_1_id(db).account == sam_id ); + 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(94) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 94 * 2 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // split ticket 1, downgrade some + result = update_ticket( tick_1_id(db), liquid, asset(10) ); + + BOOST_CHECK( tick_1_id(db).account == sam_id ); + 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(84) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 84 * 2 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + BOOST_REQUIRE_EQUAL( result.new_objects.size(), 1u ); + + ticket_id_type tick_3_id = *result.new_objects.begin(); + BOOST_CHECK( tick_3_id(db).target_type == liquid ); + BOOST_CHECK( tick_3_id(db).current_type == liquid ); + BOOST_CHECK( tick_3_id(db).status == withdrawing ); + BOOST_CHECK( tick_3_id(db).amount == asset(10) ); + BOOST_CHECK_EQUAL( tick_3_id(db).value.value, 0 ); + + // update ticket 1, downgrade all + update_ticket( tick_1_id(db), liquid, {} ); + + // check new data + BOOST_CHECK( tick_1_id(db).account == sam_id ); + 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(84) ); + BOOST_CHECK_EQUAL( tick_1_id(db).value.value, 0 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // create a new ticket + const ticket_object& tick_4 = create_ticket( sam_id, lock_360_days, asset(200) ); + ticket_id_type tick_4_id = tick_4.id; + + BOOST_CHECK( tick_4_id(db).account == sam_id ); + BOOST_CHECK( tick_4_id(db).target_type == lock_360_days ); + BOOST_CHECK( tick_4_id(db).current_type == liquid ); + BOOST_CHECK( tick_4_id(db).status == charging ); + BOOST_CHECK( tick_4_id(db).amount == asset(200) ); + BOOST_CHECK_EQUAL( tick_4_id(db).value.value, 0 ); + sam_balance -= 200; + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + } FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( hf2262_auto_update_test ) +{ try { + + INVOKE( one_lock_360_ticket ); + + // activate hf2262 + auto mi = db.get_global_properties().parameters.maintenance_interval; + generate_blocks( HARDFORK_CORE_2262_TIME - mi ); + generate_blocks( db.get_dynamic_global_properties().next_maintenance_time ); + + GET_ACTOR( sam ); + + int64_t sam_balance = db.get_balance( sam_id, asset_id_type() ).amount.value; + + ticket_id_type tick_1_id; // default value + + // withdraw the ticket + auto result = update_ticket( tick_1_id(db), liquid, {} ); + BOOST_CHECK_EQUAL( result.new_objects.size(), 0u ); + + BOOST_CHECK( tick_1_id(db).target_type == liquid ); + 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_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // 179 days passed + generate_blocks( db.head_block_time() + fc::days(179) ); + set_expiration( db, trx ); + + // no change + BOOST_CHECK( tick_1_id(db).target_type == liquid ); + 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_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // 1 day passed + generate_blocks( db.head_block_time() + fc::days(1) ); + set_expiration( db, trx ); + + // the ticket should have downgraded + 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, 0 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // 179 days passed + generate_blocks( db.head_block_time() + fc::days(179) ); + set_expiration( db, trx ); + + // no change + 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, 0 ); + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance ); + + // 1 day passed + generate_blocks( db.head_block_time() + fc::days(1) ); + set_expiration( db, trx ); + + // the ticket should be freed + BOOST_CHECK( !db.find( tick_1_id ) ); + + BOOST_CHECK_EQUAL( db.get_balance( sam_id, asset_id_type() ).amount.value, sam_balance + 100 ); + + } FC_LOG_AND_RETHROW() +} + + BOOST_AUTO_TEST_SUITE_END()