2015-06-08 15:50:35 +00:00
/*
2015-10-12 17:48:40 +00:00
* Copyright ( c ) 2015 Cryptonomex , Inc . , and contributors .
*
2016-01-06 09:51:18 +00:00
* The MIT License
2015-10-12 17:48:40 +00:00
*
2016-01-06 09:51:18 +00:00
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
2015-10-12 17:48:40 +00:00
*
2016-01-06 09:51:18 +00:00
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
2015-10-12 17:02:59 +00:00
*
2016-01-06 09:51:18 +00:00
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
2015-06-08 15:50:35 +00:00
*/
# include <graphene/chain/database.hpp>
2015-09-09 15:11:58 +00:00
# include <graphene/chain/db_with.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/asset_object.hpp>
# include <graphene/chain/global_property_object.hpp>
2016-01-08 16:01:25 +00:00
# include <graphene/chain/market_object.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/proposal_object.hpp>
# include <graphene/chain/transaction_object.hpp>
# include <graphene/chain/withdraw_permission_object.hpp>
# include <graphene/chain/witness_object.hpp>
2016-09-03 21:51:27 +00:00
# include <graphene/chain/tournament_object.hpp>
2016-01-08 16:01:25 +00:00
2015-07-08 20:39:23 +00:00
# include <graphene/chain/protocol/fee_schedule.hpp>
2015-06-08 15:50:35 +00:00
# include <fc/uint128.hpp>
namespace graphene { namespace chain {
void database : : update_global_dynamic_data ( const signed_block & b )
{
const dynamic_global_property_object & _dgp =
dynamic_global_property_id_type ( 0 ) ( * this ) ;
2015-08-25 21:54:04 +00:00
uint32_t missed_blocks = get_slot_at_time ( b . timestamp ) ;
assert ( missed_blocks ! = 0 ) ;
missed_blocks - - ;
2015-09-18 17:42:12 +00:00
for ( uint32_t i = 0 ; i < missed_blocks ; + + i ) {
const auto & witness_missed = get_scheduled_witness ( i + 1 ) ( * this ) ;
if ( witness_missed . id ! = b . witness ) {
/*
2015-09-28 14:16:48 +00:00
const auto & witness_account = witness_missed . witness_account ( * this ) ;
2015-09-18 17:42:12 +00:00
if ( ( fc : : time_point : : now ( ) - b . timestamp ) < fc : : seconds ( 30 ) )
wlog ( " Witness ${name} missed block ${n} around ${t} " , ( " name " , witness_account . name ) ( " n " , b . block_num ( ) ) ( " t " , b . timestamp ) ) ;
*/
modify ( witness_missed , [ & ] ( witness_object & w ) {
w . total_missed + + ;
} ) ;
}
2015-09-18 13:13:17 +00:00
}
2015-07-15 18:13:24 +00:00
2015-06-08 15:50:35 +00:00
// dynamic global properties updating
modify ( _dgp , [ & ] ( dynamic_global_property_object & dgp ) {
2015-08-26 22:59:19 +00:00
if ( BOOST_UNLIKELY ( b . block_num ( ) = = 1 ) )
dgp . recently_missed_count = 0 ;
2015-08-27 15:33:42 +00:00
else if ( _checkpoints . size ( ) & & _checkpoints . rbegin ( ) - > first > = b . block_num ( ) )
2015-08-17 17:52:45 +00:00
dgp . recently_missed_count = 0 ;
else if ( missed_blocks )
2015-08-25 21:54:04 +00:00
dgp . recently_missed_count + = GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT * missed_blocks ;
else if ( dgp . recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )
dgp . recently_missed_count - = GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT ;
2015-07-15 18:13:24 +00:00
else if ( dgp . recently_missed_count > 0 )
dgp . recently_missed_count - - ;
2015-06-08 15:50:35 +00:00
dgp . head_block_number = b . block_num ( ) ;
dgp . head_block_id = b . id ( ) ;
dgp . time = b . timestamp ;
dgp . current_witness = b . witness ;
2015-08-25 21:54:04 +00:00
dgp . recent_slots_filled = (
( dgp . recent_slots_filled < < 1 )
+ 1 ) < < missed_blocks ;
dgp . current_aslot + = missed_blocks + 1 ;
2015-06-08 15:50:35 +00:00
} ) ;
2015-07-15 18:13:24 +00:00
if ( ! ( get_node_properties ( ) . skip_flags & skip_undo_history_check ) )
{
2015-10-05 13:46:02 +00:00
GRAPHENE_ASSERT ( _dgp . head_block_number - _dgp . last_irreversible_block_num < GRAPHENE_MAX_UNDO_HISTORY , undo_database_exception ,
2015-07-15 18:13:24 +00:00
" The database does not have enough undo history to support a blockchain with so many missed blocks. "
" Please add a checkpoint if you would like to continue applying blocks beyond this point. " ,
2015-10-05 13:46:02 +00:00
( " last_irreversible_block_num " , _dgp . last_irreversible_block_num ) ( " head " , _dgp . head_block_number )
2015-07-15 18:13:24 +00:00
( " recently_missed " , _dgp . recently_missed_count ) ( " max_undo " , GRAPHENE_MAX_UNDO_HISTORY ) ) ;
}
2015-10-07 14:15:32 +00:00
_undo_db . set_max_size ( _dgp . head_block_number - _dgp . last_irreversible_block_num + 1 ) ;
_fork_db . set_max_size ( _dgp . head_block_number - _dgp . last_irreversible_block_num + 1 ) ;
2015-06-08 15:50:35 +00:00
}
void database : : update_signing_witness ( const witness_object & signing_witness , const signed_block & new_block )
{
const global_property_object & gpo = get_global_properties ( ) ;
const dynamic_global_property_object & dpo = get_dynamic_global_properties ( ) ;
2015-08-25 21:54:04 +00:00
uint64_t new_block_aslot = dpo . current_aslot + get_slot_at_time ( new_block . timestamp ) ;
2015-06-08 15:50:35 +00:00
share_type witness_pay = std : : min ( gpo . parameters . witness_pay_per_block , dpo . witness_budget ) ;
modify ( dpo , [ & ] ( dynamic_global_property_object & _dpo )
{
_dpo . witness_budget - = witness_pay ;
} ) ;
2015-07-15 16:03:10 +00:00
deposit_witness_pay ( signing_witness , witness_pay ) ;
2015-06-08 15:50:35 +00:00
modify ( signing_witness , [ & ] ( witness_object & _wit )
{
2015-08-25 21:54:04 +00:00
_wit . last_aslot = new_block_aslot ;
2015-10-02 21:19:03 +00:00
_wit . last_confirmed_block_num = new_block . block_num ( ) ;
2015-06-08 15:50:35 +00:00
} ) ;
}
2015-10-02 21:19:03 +00:00
void database : : update_last_irreversible_block ( )
{
const global_property_object & gpo = get_global_properties ( ) ;
const dynamic_global_property_object & dpo = get_dynamic_global_properties ( ) ;
vector < const witness_object * > wit_objs ;
wit_objs . reserve ( gpo . active_witnesses . size ( ) ) ;
for ( const witness_id_type & wid : gpo . active_witnesses )
wit_objs . push_back ( & ( wid ( * this ) ) ) ;
static_assert ( GRAPHENE_IRREVERSIBLE_THRESHOLD > 0 , " irreversible threshold must be nonzero " ) ;
// 1 1 1 2 2 2 2 2 2 2 -> 2 .7*10 = 7
// 1 1 1 1 1 1 1 2 2 2 -> 1
// 3 3 3 3 3 3 3 3 3 3 -> 3
size_t offset = ( ( GRAPHENE_100_PERCENT - GRAPHENE_IRREVERSIBLE_THRESHOLD ) * wit_objs . size ( ) / GRAPHENE_100_PERCENT ) ;
std : : nth_element ( wit_objs . begin ( ) , wit_objs . begin ( ) + offset , wit_objs . end ( ) ,
[ ] ( const witness_object * a , const witness_object * b )
{
return a - > last_confirmed_block_num < b - > last_confirmed_block_num ;
} ) ;
uint32_t new_last_irreversible_block_num = wit_objs [ offset ] - > last_confirmed_block_num ;
if ( new_last_irreversible_block_num > dpo . last_irreversible_block_num )
{
modify ( dpo , [ & ] ( dynamic_global_property_object & _dpo )
{
_dpo . last_irreversible_block_num = new_last_irreversible_block_num ;
} ) ;
}
}
2015-06-08 15:50:35 +00:00
void database : : clear_expired_transactions ( )
2016-01-29 01:02:37 +00:00
{ try {
2015-06-08 15:50:35 +00:00
//Look for expired transactions in the deduplication list, and remove them.
//Transactions must have expired by at least two forking windows in order to be removed.
auto & transaction_idx = static_cast < transaction_index & > ( get_mutable_index ( implementation_ids , impl_transaction_object_type ) ) ;
const auto & dedupe_index = transaction_idx . indices ( ) . get < by_expiration > ( ) ;
2015-09-09 20:03:27 +00:00
while ( ( ! dedupe_index . empty ( ) ) & & ( head_block_time ( ) > dedupe_index . rbegin ( ) - > trx . expiration ) )
2015-06-08 15:50:35 +00:00
transaction_idx . remove ( * dedupe_index . rbegin ( ) ) ;
2016-01-29 01:02:37 +00:00
} FC_CAPTURE_AND_RETHROW ( ) }
2015-06-08 15:50:35 +00:00
void database : : clear_expired_proposals ( )
{
const auto & proposal_expiration_index = get_index_type < proposal_index > ( ) . indices ( ) . get < by_expiration > ( ) ;
while ( ! proposal_expiration_index . empty ( ) & & proposal_expiration_index . begin ( ) - > expiration_time < = head_block_time ( ) )
{
const proposal_object & proposal = * proposal_expiration_index . begin ( ) ;
processed_transaction result ;
try {
2015-06-29 21:29:04 +00:00
if ( proposal . is_authorized_to_execute ( * this ) )
2015-06-08 15:50:35 +00:00
{
result = push_proposal ( proposal ) ;
//TODO: Do something with result so plugins can process it.
continue ;
}
} catch ( const fc : : exception & e ) {
elog ( " Failed to apply proposed transaction on its expiration. Deleting it. \n ${proposal} \n ${error} " ,
( " proposal " , proposal ) ( " error " , e . to_detail_string ( ) ) ) ;
}
remove ( proposal ) ;
}
}
2015-09-30 22:30:44 +00:00
/**
* let HB = the highest bid for the collateral ( aka who will pay the most DEBT for the least collateral )
* let SP = current median feed ' s Settlement Price
* let LC = the least collateralized call order ' s swan price ( debt / collateral )
*
* If there is no valid price feed or no bids then there is no black swan .
*
* A black swan occurs if MAX ( HB , SP ) < = LC
*/
bool database : : check_for_blackswan ( const asset_object & mia , bool enable_black_swan )
{
if ( ! mia . is_market_issued ( ) ) return false ;
const asset_bitasset_data_object & bitasset = mia . bitasset_data ( * this ) ;
if ( bitasset . has_settlement ( ) ) return true ; // already force settled
2015-10-01 16:40:42 +00:00
auto settle_price = bitasset . current_feed . settlement_price ;
if ( settle_price . is_null ( ) ) return false ; // no feed
2015-09-30 22:30:44 +00:00
const call_order_index & call_index = get_index_type < call_order_index > ( ) ;
const auto & call_price_index = call_index . indices ( ) . get < by_price > ( ) ;
const limit_order_index & limit_index = get_index_type < limit_order_index > ( ) ;
const auto & limit_price_index = limit_index . indices ( ) . get < by_price > ( ) ;
// looking for limit orders selling the most USD for the least CORE
auto highest_possible_bid = price : : max ( mia . id , bitasset . options . short_backing_asset ) ;
// stop when limit orders are selling too little USD for too much CORE
auto lowest_possible_bid = price : : min ( mia . id , bitasset . options . short_backing_asset ) ;
assert ( highest_possible_bid . base . asset_id = = lowest_possible_bid . base . asset_id ) ;
// NOTE limit_price_index is sorted from greatest to least
auto limit_itr = limit_price_index . lower_bound ( highest_possible_bid ) ;
auto limit_end = limit_price_index . upper_bound ( lowest_possible_bid ) ;
auto call_min = price : : min ( bitasset . options . short_backing_asset , mia . id ) ;
auto call_max = price : : max ( bitasset . options . short_backing_asset , mia . id ) ;
auto call_itr = call_price_index . lower_bound ( call_min ) ;
auto call_end = call_price_index . upper_bound ( call_max ) ;
2015-10-01 16:40:42 +00:00
if ( call_itr = = call_end ) return false ; // no call orders
2015-09-30 22:30:44 +00:00
price highest = settle_price ;
if ( limit_itr ! = limit_end ) {
assert ( settle_price . base . asset_id = = limit_itr - > sell_price . base . asset_id ) ;
highest = std : : max ( limit_itr - > sell_price , settle_price ) ;
}
auto least_collateral = call_itr - > collateralization ( ) ;
if ( ~ least_collateral > = highest )
{
elog ( " Black Swan detected: \n "
" Least collateralized call: ${lc} ${~lc} \n "
// " Highest Bid: ${hb} ${~hb}\n"
" Settle Price: ${sp} ${~sp} \n "
" Max: ${h} ${~h} \n " ,
( " lc " , least_collateral . to_real ( ) ) ( " ~lc " , ( ~ least_collateral ) . to_real ( ) )
// ("hb",limit_itr->sell_price.to_real())("~hb",(~limit_itr->sell_price).to_real())
( " sp " , settle_price . to_real ( ) ) ( " ~sp " , ( ~ settle_price ) . to_real ( ) )
( " h " , highest . to_real ( ) ) ( " ~h " , ( ~ highest ) . to_real ( ) ) ) ;
FC_ASSERT ( enable_black_swan , " Black swan was detected during a margin update which is not allowed to trigger a blackswan " ) ;
globally_settle_asset ( mia , ~ least_collateral ) ;
return true ;
}
return false ;
}
2015-06-08 15:50:35 +00:00
void database : : clear_expired_orders ( )
2016-01-29 01:02:37 +00:00
{ try {
2015-09-09 15:11:58 +00:00
detail : : with_skip_flags ( * this ,
2015-06-18 19:31:30 +00:00
get_node_properties ( ) . skip_flags | skip_authority_check , [ & ] ( ) {
transaction_evaluation_state cancel_context ( this ) ;
2015-06-08 15:50:35 +00:00
2015-06-18 19:31:30 +00:00
//Cancel expired limit orders
auto & limit_index = get_index_type < limit_order_index > ( ) . indices ( ) . get < by_expiration > ( ) ;
while ( ! limit_index . empty ( ) & & limit_index . begin ( ) - > expiration < = head_block_time ( ) )
{
limit_order_cancel_operation canceler ;
const limit_order_object & order = * limit_index . begin ( ) ;
canceler . fee_paying_account = order . seller ;
canceler . order = order . id ;
2016-01-29 01:11:46 +00:00
canceler . fee = current_fee_schedule ( ) . calculate_fee ( canceler ) ;
2016-02-02 18:58:58 +00:00
if ( canceler . fee . amount > order . deferred_fee )
{
// Cap auto-cancel fees at deferred_fee; see #549
wlog ( " At block ${b}, fee for clearing expired order ${oid} was capped at deferred_fee ${fee} " , ( " b " , head_block_num ( ) ) ( " oid " , order . id ) ( " fee " , order . deferred_fee ) ) ;
canceler . fee = asset ( order . deferred_fee , asset_id_type ( ) ) ;
}
// we know the fee for this op is set correctly since it is set by the chain.
// this allows us to avoid a hung chain:
// - if #549 case above triggers
// - if the fee is incorrect, which may happen due to #435 (although since cancel is a fixed-fee op, it shouldn't)
cancel_context . skip_fee_schedule_check = true ;
2015-06-18 19:31:30 +00:00
apply_operation ( cancel_context , canceler ) ;
}
} ) ;
2015-06-08 15:50:35 +00:00
//Process expired force settlement orders
auto & settlement_index = get_index_type < force_settlement_index > ( ) . indices ( ) . get < by_expiration > ( ) ;
if ( ! settlement_index . empty ( ) )
{
asset_id_type current_asset = settlement_index . begin ( ) - > settlement_asset_id ( ) ;
asset max_settlement_volume ;
2016-01-07 20:12:29 +00:00
bool extra_dump = false ;
2015-06-08 15:50:35 +00:00
2016-01-07 20:12:29 +00:00
auto next_asset = [ & current_asset , & settlement_index , & extra_dump ] {
2015-06-08 15:50:35 +00:00
auto bound = settlement_index . upper_bound ( current_asset ) ;
if ( bound = = settlement_index . end ( ) )
2016-01-07 20:12:29 +00:00
{
if ( extra_dump )
{
ilog ( " next_asset() returning false " ) ;
}
2015-06-08 15:50:35 +00:00
return false ;
2016-01-07 20:12:29 +00:00
}
if ( extra_dump )
{
ilog ( " next_asset returning true, bound is ${b} " , ( " b " , * bound ) ) ;
}
2015-06-08 15:50:35 +00:00
current_asset = bound - > settlement_asset_id ( ) ;
return true ;
} ;
2016-01-07 20:12:29 +00:00
uint32_t count = 0 ;
2015-06-08 15:50:35 +00:00
// At each iteration, we either consume the current order and remove it, or we move to the next asset
for ( auto itr = settlement_index . lower_bound ( current_asset ) ;
itr ! = settlement_index . end ( ) ;
itr = settlement_index . lower_bound ( current_asset ) )
{
2016-01-07 20:12:29 +00:00
+ + count ;
2015-06-08 15:50:35 +00:00
const force_settlement_object & order = * itr ;
auto order_id = order . id ;
current_asset = order . settlement_asset_id ( ) ;
const asset_object & mia_object = get ( current_asset ) ;
2016-01-07 20:12:43 +00:00
const asset_bitasset_data_object & mia = mia_object . bitasset_data ( * this ) ;
2015-06-08 15:50:35 +00:00
2016-01-07 20:12:29 +00:00
extra_dump = ( ( count > = 1000 ) & & ( count < = 1020 ) ) ;
if ( extra_dump )
{
wlog ( " clear_expired_orders() dumping extra data for iteration ${c} " , ( " c " , count ) ) ;
ilog ( " head_block_num is ${hb} current_asset is ${a} " , ( " hb " , head_block_num ( ) ) ( " a " , current_asset ) ) ;
}
2015-09-30 22:30:44 +00:00
if ( mia . has_settlement ( ) )
{
ilog ( " Canceling a force settlement because of black swan " ) ;
cancel_order ( order ) ;
continue ;
}
2015-06-08 15:50:35 +00:00
// Has this order not reached its settlement date?
if ( order . settlement_date > head_block_time ( ) )
{
if ( next_asset ( ) )
2016-01-07 20:12:29 +00:00
{
if ( extra_dump )
{
ilog ( " next_asset() returned true when order.settlement_date > head_block_time() " ) ;
}
2015-06-08 15:50:35 +00:00
continue ;
2016-01-07 20:12:29 +00:00
}
2015-06-08 15:50:35 +00:00
break ;
}
// Can we still settle in this asset?
if ( mia . current_feed . settlement_price . is_null ( ) )
{
ilog ( " Canceling a force settlement in ${asset} because settlement price is null " ,
( " asset " , mia_object . symbol ) ) ;
cancel_order ( order ) ;
continue ;
}
if ( max_settlement_volume . asset_id ! = current_asset )
max_settlement_volume = mia_object . amount ( mia . max_force_settlement_volume ( mia_object . dynamic_data ( * this ) . current_supply ) ) ;
if ( mia . force_settled_volume > = max_settlement_volume . amount )
{
2015-06-16 19:56:13 +00:00
/*
2015-06-08 15:50:35 +00:00
ilog ( " Skipping force settlement in ${asset}; settled ${settled_volume} / ${max_volume} " ,
( " asset " , mia_object . symbol ) ( " settlement_price_null " , mia . current_feed . settlement_price . is_null ( ) )
( " settled_volume " , mia . force_settled_volume ) ( " max_volume " , max_settlement_volume ) ) ;
2015-06-16 19:56:13 +00:00
*/
2015-06-08 15:50:35 +00:00
if ( next_asset ( ) )
2016-01-07 20:12:29 +00:00
{
if ( extra_dump )
{
ilog ( " next_asset() returned true when mia.force_settled_volume >= max_settlement_volume.amount " ) ;
}
2015-06-08 15:50:35 +00:00
continue ;
2016-01-07 20:12:29 +00:00
}
2015-06-08 15:50:35 +00:00
break ;
}
auto & pays = order . balance ;
auto receives = ( order . balance * mia . current_feed . settlement_price ) ;
receives . amount = ( fc : : uint128_t ( receives . amount . value ) *
( GRAPHENE_100_PERCENT - mia . options . force_settlement_offset_percent ) / GRAPHENE_100_PERCENT ) . to_uint64 ( ) ;
assert ( receives < = order . balance * mia . current_feed . settlement_price ) ;
price settlement_price = pays / receives ;
auto & call_index = get_index_type < call_order_index > ( ) . indices ( ) . get < by_collateral > ( ) ;
asset settled = mia_object . amount ( mia . force_settled_volume ) ;
// Match against the least collateralized short until the settlement is finished or we reach max settlements
while ( settled < max_settlement_volume & & find_object ( order_id ) )
{
auto itr = call_index . lower_bound ( boost : : make_tuple ( price : : min ( mia_object . bitasset_data ( * this ) . options . short_backing_asset ,
mia_object . get_id ( ) ) ) ) ;
// There should always be a call order, since asset exists!
assert ( itr ! = call_index . end ( ) & & itr - > debt_type ( ) = = mia_object . get_id ( ) ) ;
asset max_settlement = max_settlement_volume - settled ;
2015-09-30 22:30:44 +00:00
2016-01-04 01:40:21 +00:00
if ( order . balance . amount = = 0 )
{
wlog ( " 0 settlement detected " ) ;
cancel_order ( order ) ;
break ;
}
2015-09-30 22:30:44 +00:00
try {
settled + = match ( * itr , order , settlement_price , max_settlement ) ;
}
catch ( const black_swan_exception & e ) {
wlog ( " black swan detected: ${e} " , ( " e " , e . to_detail_string ( ) ) ) ;
cancel_order ( order ) ;
break ;
}
2015-06-08 15:50:35 +00:00
}
2016-01-28 23:14:10 +00:00
if ( mia . force_settled_volume ! = settled . amount )
{
modify ( mia , [ settled ] ( asset_bitasset_data_object & b ) {
b . force_settled_volume = settled . amount ;
} ) ;
}
2015-06-08 15:50:35 +00:00
}
}
2016-01-29 01:02:37 +00:00
} FC_CAPTURE_AND_RETHROW ( ) }
2015-06-08 15:50:35 +00:00
void database : : update_expired_feeds ( )
{
2015-10-30 13:57:45 +00:00
auto & asset_idx = get_index_type < asset_index > ( ) . indices ( ) . get < by_type > ( ) ;
auto itr = asset_idx . lower_bound ( true /** market issued */ ) ;
while ( itr ! = asset_idx . end ( ) )
2015-06-26 14:42:40 +00:00
{
2015-10-30 13:57:45 +00:00
const asset_object & a = * itr ;
+ + itr ;
assert ( a . is_market_issued ( ) ) ;
2015-06-26 14:42:40 +00:00
const asset_bitasset_data_object & b = a . bitasset_data ( * this ) ;
if ( b . feed_is_expired ( head_block_time ( ) ) )
2015-06-25 18:33:46 +00:00
{
2015-06-26 14:42:40 +00:00
modify ( b , [ this ] ( asset_bitasset_data_object & a ) {
2015-06-08 15:50:35 +00:00
a . update_median_feeds ( head_block_time ( ) ) ;
} ) ;
2015-06-26 19:46:16 +00:00
check_call_orders ( b . current_feed . settlement_price . base . asset_id ( * this ) ) ;
}
if ( ! b . current_feed . core_exchange_rate . is_null ( ) & &
a . options . core_exchange_rate ! = b . current_feed . core_exchange_rate )
2015-06-26 14:42:40 +00:00
modify ( a , [ & b ] ( asset_object & a ) {
a . options . core_exchange_rate = b . current_feed . core_exchange_rate ;
} ) ;
}
2015-06-08 15:50:35 +00:00
}
2015-07-24 18:03:59 +00:00
void database : : update_maintenance_flag ( bool new_maintenance_flag )
{
modify ( get_dynamic_global_properties ( ) , [ & ] ( dynamic_global_property_object & dpo )
{
auto maintenance_flag = dynamic_global_property_object : : maintenance_flag ;
dpo . dynamic_flags =
( dpo . dynamic_flags & ~ maintenance_flag )
| ( new_maintenance_flag ? maintenance_flag : 0 ) ;
} ) ;
return ;
}
2015-06-08 15:50:35 +00:00
void database : : update_withdraw_permissions ( )
{
auto & permit_index = get_index_type < withdraw_permission_index > ( ) . indices ( ) . get < by_expiration > ( ) ;
while ( ! permit_index . empty ( ) & & permit_index . begin ( ) - > expiration < = head_block_time ( ) )
remove ( * permit_index . begin ( ) ) ;
}
2016-09-03 21:51:27 +00:00
void database : : update_tournaments ( )
{
// First, cancel any tournaments that didn't get enough players
auto & registration_deadline_index = get_index_type < tournament_index > ( ) . indices ( ) . get < by_registration_deadline > ( ) ;
// this index is sorted on state and deadline, so the tournaments awaiting registrations with the earliest
// deadlines will be at the beginning
while ( registration_deadline_index . empty ( ) & &
2016-09-08 22:34:43 +00:00
registration_deadline_index . begin ( ) - > get_state ( ) = = tournament_state : : accepting_registrations & &
2016-09-03 21:51:27 +00:00
registration_deadline_index . begin ( ) - > options . registration_deadline < = head_block_time ( ) )
{
const tournament_object & tournament_obj = * registration_deadline_index . begin ( ) ;
fc_ilog ( fc : : logger : : get ( " tournament " ) ,
" Canceling tournament ${id} because its deadline expired " ,
( " id " , tournament_obj . id ) ) ;
// cancel this tournament
modify ( tournament_obj , [ & ] ( tournament_object & t ) {
2016-09-08 22:34:43 +00:00
t . on_registration_deadline_passed ( * this ) ;
2016-09-03 21:51:27 +00:00
} ) ;
}
// Next, start any tournaments that have enough players and whose start time just arrived
auto & start_time_index = get_index_type < tournament_index > ( ) . indices ( ) . get < by_start_time > ( ) ;
while ( 1 )
{
// find the first tournament waiting to start; if its start time has arrived, start it
auto start_iter = start_time_index . lower_bound ( boost : : make_tuple ( tournament_state : : awaiting_start ) ) ;
2016-09-08 22:34:43 +00:00
if ( start_iter - > get_state ( ) = = tournament_state : : awaiting_start & &
2016-09-03 21:51:27 +00:00
* start_iter - > start_time < = head_block_time ( ) )
{
modify ( * start_iter , [ & ] ( tournament_object & t ) {
2016-09-08 22:34:43 +00:00
t . on_start_time_arrived ( ) ;
2016-09-03 21:51:27 +00:00
} ) ;
}
else
break ;
}
}
2015-06-08 15:50:35 +00:00
} }