BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
db_update.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
27 
30 #include <graphene/chain/hardfork.hpp>
38 
40 
41 namespace graphene { namespace chain {
42 
43 void database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks )
44 {
45  const dynamic_global_property_object& _dgp = get_dynamic_global_properties();
46 
47  // dynamic global properties updating
48  modify( _dgp, [&b,this,missed_blocks]( dynamic_global_property_object& dgp ){
49  const uint32_t block_num = b.block_num();
50  if( BOOST_UNLIKELY( block_num == 1 ) )
51  dgp.recently_missed_count = 0;
52  else if( _checkpoints.size() && _checkpoints.rbegin()->first >= block_num )
53  dgp.recently_missed_count = 0;
54  else if( missed_blocks )
55  dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
56  else if( dgp.recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )
57  dgp.recently_missed_count -= GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT;
58  else if( dgp.recently_missed_count > 0 )
59  dgp.recently_missed_count--;
60 
61  dgp.head_block_number = block_num;
62  dgp.head_block_id = b.id();
63  dgp.time = b.timestamp;
64  dgp.current_witness = b.witness;
65  dgp.recent_slots_filled = (
66  (dgp.recent_slots_filled << 1)
67  + 1) << missed_blocks;
68  dgp.current_aslot += missed_blocks+1;
69  });
70 
72  {
73  GRAPHENE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,
74  "The database does not have enough undo history to support a blockchain with so many missed blocks. "
75  "Please add a checkpoint if you would like to continue applying blocks beyond this point.",
76  ("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number)
77  ("recently_missed",_dgp.recently_missed_count)("max_undo",GRAPHENE_MAX_UNDO_HISTORY) );
78  }
79 
80  _undo_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
81  _fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
82 }
83 
84 void database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block)
85 {
86  const global_property_object& gpo = get_global_properties();
87  const dynamic_global_property_object& dpo = get_dynamic_global_properties();
88  uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time( new_block.timestamp );
89 
90  share_type witness_pay = std::min( gpo.parameters.witness_pay_per_block, dpo.witness_budget );
91 
92  modify( dpo, [&]( dynamic_global_property_object& _dpo )
93  {
94  _dpo.witness_budget -= witness_pay;
95  } );
96 
97  deposit_witness_pay( signing_witness, witness_pay );
98 
99  modify( signing_witness, [&]( witness_object& _wit )
100  {
101  _wit.last_aslot = new_block_aslot;
102  _wit.last_confirmed_block_num = new_block.block_num();
103  } );
104 }
105 
106 void database::update_last_irreversible_block()
107 {
108  const global_property_object& gpo = get_global_properties();
109  const dynamic_global_property_object& dpo = get_dynamic_global_properties();
110 
111  // TODO for better performance, move this to db_maint, because only need to do it once per maintenance interval
112  vector< const witness_object* > wit_objs;
113  wit_objs.reserve( gpo.active_witnesses.size() );
114  for( const witness_id_type& wid : gpo.active_witnesses )
115  wit_objs.push_back( &(wid(*this)) );
116 
117  static_assert( GRAPHENE_IRREVERSIBLE_THRESHOLD > 0, "irreversible threshold must be nonzero" );
118 
119  // 1 1 1 2 2 2 2 2 2 2 -> 2 .3*10 = 3
120  // 1 1 1 1 1 1 1 2 2 2 -> 1
121  // 3 3 3 3 3 3 3 3 3 3 -> 3
122  // 3 3 3 4 4 4 4 4 4 4 -> 4
123 
124  size_t offset = ((GRAPHENE_100_PERCENT - GRAPHENE_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / GRAPHENE_100_PERCENT);
125 
126  std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(),
127  []( const witness_object* a, const witness_object* b )
128  {
129  return a->last_confirmed_block_num < b->last_confirmed_block_num;
130  } );
131 
132  uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num;
133 
134  if( new_last_irreversible_block_num > dpo.last_irreversible_block_num )
135  {
136  modify( dpo, [&]( dynamic_global_property_object& _dpo )
137  {
138  _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
139  } );
140  }
141 }
142 
143 void database::clear_expired_transactions()
144 { try {
145  //Look for expired transactions in the deduplication list, and remove them.
146  //Transactions must have expired by at least two forking windows in order to be removed.
147  auto& transaction_idx = static_cast<transaction_index&>(get_mutable_index(implementation_ids,
148  impl_transaction_history_object_type));
149  const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
150  while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.begin()->trx.expiration) )
151  transaction_idx.remove(*dedupe_index.begin());
153 
154 void database::clear_expired_proposals()
155 {
156  const auto& proposal_expiration_index = get_index_type<proposal_index>().indices().get<by_expiration>();
157  while( !proposal_expiration_index.empty() && proposal_expiration_index.begin()->expiration_time <= head_block_time() )
158  {
159  const proposal_object& proposal = *proposal_expiration_index.begin();
160  processed_transaction result;
161  try {
162  if( proposal.is_authorized_to_execute(*this) )
163  {
164  result = push_proposal(proposal);
165  //TODO: Do something with result so plugins can process it.
166  continue;
167  }
168  } catch( const fc::exception& e ) {
169  elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
170  ("proposal", proposal)("error", e.to_detail_string()));
171  }
172  remove(proposal);
173  }
174 }
175 
185 bool database::check_for_blackswan( const asset_object& mia, bool enable_black_swan,
186  const asset_bitasset_data_object* bitasset_ptr )
187 {
188  if( !mia.is_market_issued() ) return false;
189 
190  const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) );
191  if( bitasset.has_settlement() ) return true; // already force settled
192  auto settle_price = bitasset.current_feed.settlement_price;
193  if( settle_price.is_null() ) return false; // no feed
194 
195  const call_order_object* call_ptr = nullptr; // place holder for the call order with least collateral ratio
196 
197  asset_id_type debt_asset_id = mia.id;
198  auto call_min = price::min( bitasset.options.short_backing_asset, debt_asset_id );
199 
201  bool before_core_hardfork_1270 = ( maint_time <= HARDFORK_CORE_1270_TIME ); // call price caching issue
202 
203  if( before_core_hardfork_1270 ) // before core-1270 hard fork, check with call_price
204  {
205  const auto& call_price_index = get_index_type<call_order_index>().indices().get<by_price>();
206  auto call_itr = call_price_index.lower_bound( call_min );
207  if( call_itr == call_price_index.end() ) // no call order
208  return false;
209  call_ptr = &(*call_itr);
210  }
211  else // after core-1270 hard fork, check with collateralization
212  {
213  const auto& call_collateral_index = get_index_type<call_order_index>().indices().get<by_collateral>();
214  auto call_itr = call_collateral_index.lower_bound( call_min );
215  if( call_itr == call_collateral_index.end() ) // no call order
216  return false;
217  call_ptr = &(*call_itr);
218  }
219  if( call_ptr->debt_type() != debt_asset_id ) // no call order
220  return false;
221 
222  price highest = settle_price;
223  if( maint_time > HARDFORK_CORE_1270_TIME )
224  // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here
225  highest = bitasset.current_feed.max_short_squeeze_price();
226  else if( maint_time > HARDFORK_CORE_338_TIME )
227  // due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here
228  highest = bitasset.current_feed.max_short_squeeze_price_before_hf_1270();
229 
230  const limit_order_index& limit_index = get_index_type<limit_order_index>();
231  const auto& limit_price_index = limit_index.indices().get<by_price>();
232 
233  // looking for limit orders selling the most USD for the least CORE
234  auto highest_possible_bid = price::max( mia.id, bitasset.options.short_backing_asset );
235  // stop when limit orders are selling too little USD for too much CORE
236  auto lowest_possible_bid = price::min( mia.id, bitasset.options.short_backing_asset );
237 
238  FC_ASSERT( highest_possible_bid.base.asset_id == lowest_possible_bid.base.asset_id );
239  // NOTE limit_price_index is sorted from greatest to least
240  auto limit_itr = limit_price_index.lower_bound( highest_possible_bid );
241  auto limit_end = limit_price_index.upper_bound( lowest_possible_bid );
242 
243  if( limit_itr != limit_end ) {
244  FC_ASSERT( highest.base.asset_id == limit_itr->sell_price.base.asset_id );
245  highest = std::max( limit_itr->sell_price, highest );
246  }
247 
248  auto least_collateral = call_ptr->collateralization();
249  if( ~least_collateral >= highest )
250  {
251  wdump( (*call_ptr) );
252  elog( "Black Swan detected on asset ${symbol} (${id}) at block ${b}: \n"
253  " Least collateralized call: ${lc} ${~lc}\n"
254  // " Highest Bid: ${hb} ${~hb}\n"
255  " Settle Price: ${~sp} ${sp}\n"
256  " Max: ${~h} ${h}\n",
257  ("id",mia.id)("symbol",mia.symbol)("b",head_block_num())
258  ("lc",least_collateral.to_real())("~lc",(~least_collateral).to_real())
259  // ("hb",limit_itr->sell_price.to_real())("~hb",(~limit_itr->sell_price).to_real())
260  ("sp",settle_price.to_real())("~sp",(~settle_price).to_real())
261  ("h",highest.to_real())("~h",(~highest).to_real()) );
262  edump((enable_black_swan));
263  FC_ASSERT( enable_black_swan, "Black swan was detected during a margin update which is not allowed to trigger a blackswan" );
264  if( maint_time > HARDFORK_CORE_338_TIME && ~least_collateral <= settle_price )
265  // global settle at feed price if possible
266  globally_settle_asset(mia, settle_price );
267  else
268  globally_settle_asset(mia, ~least_collateral );
269  return true;
270  }
271  return false;
272 }
273 
274 void database::clear_expired_orders()
275 { try {
276  //Cancel expired limit orders
277  auto head_time = head_block_time();
279 
280  bool before_core_hardfork_184 = ( maint_time <= HARDFORK_CORE_184_TIME ); // something-for-nothing
281  bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding
282  bool before_core_hardfork_606 = ( maint_time <= HARDFORK_CORE_606_TIME ); // feed always trigger call
283 
284  auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>();
285  while( !limit_index.empty() && limit_index.begin()->expiration <= head_time )
286  {
287  const limit_order_object& order = *limit_index.begin();
288  auto base_asset = order.sell_price.base.asset_id;
289  auto quote_asset = order.sell_price.quote.asset_id;
290  cancel_limit_order( order );
291  if( before_core_hardfork_606 )
292  {
293  // check call orders
294  // Comments below are copied from limit_order_cancel_evaluator::do_apply(...)
295  // Possible optimization: order can be called by cancelling a limit order
296  // if the canceled order was at the top of the book.
297  // Do I need to check calls in both assets?
298  check_call_orders( base_asset( *this ) );
299  check_call_orders( quote_asset( *this ) );
300  }
301  }
302 
303  //Process expired force settlement orders
304  auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>();
305  if( !settlement_index.empty() )
306  {
307  asset_id_type current_asset = settlement_index.begin()->settlement_asset_id();
308  asset max_settlement_volume;
309  price settlement_fill_price;
310  price settlement_price;
311  bool current_asset_finished = false;
312  bool extra_dump = false;
313 
314  auto next_asset = [&current_asset, &current_asset_finished, &settlement_index, &extra_dump] {
315  auto bound = settlement_index.upper_bound(current_asset);
316  if( bound == settlement_index.end() )
317  {
318  if( extra_dump )
319  {
320  ilog( "next_asset() returning false" );
321  }
322  return false;
323  }
324  if( extra_dump )
325  {
326  ilog( "next_asset returning true, bound is ${b}", ("b", *bound) );
327  }
328  current_asset = bound->settlement_asset_id();
329  current_asset_finished = false;
330  return true;
331  };
332 
333  uint32_t count = 0;
334 
335  // At each iteration, we either consume the current order and remove it, or we move to the next asset
336  for( auto itr = settlement_index.lower_bound(current_asset);
337  itr != settlement_index.end();
338  itr = settlement_index.lower_bound(current_asset) )
339  {
340  ++count;
341  const force_settlement_object& order = *itr;
342  auto order_id = order.id;
343  current_asset = order.settlement_asset_id();
344  const asset_object& mia_object = get(current_asset);
345  const asset_bitasset_data_object& mia = mia_object.bitasset_data(*this);
346 
347  extra_dump = ((count >= 1000) && (count <= 1020));
348 
349  if( extra_dump )
350  {
351  wlog( "clear_expired_orders() dumping extra data for iteration ${c}", ("c", count) );
352  ilog( "head_block_num is ${hb} current_asset is ${a}", ("hb", head_block_num())("a", current_asset) );
353  }
354 
355  if( mia.has_settlement() )
356  {
357  ilog( "Canceling a force settlement because of black swan" );
358  cancel_settle_order( order );
359  continue;
360  }
361 
362  // Has this order not reached its settlement date?
363  if( order.settlement_date > head_time )
364  {
365  if( next_asset() )
366  {
367  if( extra_dump )
368  {
369  ilog( "next_asset() returned true when order.settlement_date > head_block_time()" );
370  }
371  continue;
372  }
373  break;
374  }
375  // Can we still settle in this asset?
376  if( mia.current_feed.settlement_price.is_null() )
377  {
378  ilog("Canceling a force settlement in ${asset} because settlement price is null",
379  ("asset", mia_object.symbol));
380  cancel_settle_order(order);
381  continue;
382  }
383  if( GRAPHENE_100_PERCENT == mia.options.force_settlement_offset_percent ) // settle something for nothing
384  {
385  ilog( "Canceling a force settlement in ${asset} because settlement offset is 100%",
386  ("asset", mia_object.symbol));
387  cancel_settle_order(order);
388  continue;
389  }
390  if( max_settlement_volume.asset_id != current_asset )
391  max_settlement_volume = mia_object.amount(mia.max_force_settlement_volume(mia_object.dynamic_data(*this).current_supply));
392  // When current_asset_finished is true, this would be the 2nd time processing the same order.
393  // In this case, we move to the next asset.
394  if( mia.force_settled_volume >= max_settlement_volume.amount || current_asset_finished )
395  {
396  /*
397  ilog("Skipping force settlement in ${asset}; settled ${settled_volume} / ${max_volume}",
398  ("asset", mia_object.symbol)("settlement_price_null",mia.current_feed.settlement_price.is_null())
399  ("settled_volume", mia.force_settled_volume)("max_volume", max_settlement_volume));
400  */
401  if( next_asset() )
402  {
403  if( extra_dump )
404  {
405  ilog( "next_asset() returned true when mia.force_settled_volume >= max_settlement_volume.amount" );
406  }
407  continue;
408  }
409  break;
410  }
411 
412  if( settlement_fill_price.base.asset_id != current_asset ) // only calculate once per asset
413  settlement_fill_price = mia.current_feed.settlement_price
414  / ratio_type( GRAPHENE_100_PERCENT - mia.options.force_settlement_offset_percent,
416 
417  if( before_core_hardfork_342 )
418  {
419  auto& pays = order.balance;
420  auto receives = (order.balance * mia.current_feed.settlement_price);
421  receives.amount = static_cast<uint64_t>( fc::uint128_t(receives.amount.value) *
422  (GRAPHENE_100_PERCENT - mia.options.force_settlement_offset_percent) /
424  assert(receives <= order.balance * mia.current_feed.settlement_price);
425  settlement_price = pays / receives;
426  }
427  else if( settlement_price.base.asset_id != current_asset ) // only calculate once per asset
428  settlement_price = settlement_fill_price;
429 
430  auto& call_index = get_index_type<call_order_index>().indices().get<by_collateral>();
431  asset settled = mia_object.amount(mia.force_settled_volume);
432  // Match against the least collateralized short until the settlement is finished or we reach max settlements
433  while( settled < max_settlement_volume && find_object(order_id) )
434  {
435  auto itr = call_index.lower_bound(boost::make_tuple(price::min(mia_object.bitasset_data(*this).options.short_backing_asset,
436  mia_object.get_id())));
437  // There should always be a call order, since asset exists!
438  assert(itr != call_index.end() && itr->debt_type() == mia_object.get_id());
439  asset max_settlement = max_settlement_volume - settled;
440 
441  if( order.balance.amount == 0 )
442  {
443  wlog( "0 settlement detected" );
444  cancel_settle_order( order );
445  break;
446  }
447  try {
448  asset new_settled = match(*itr, order, settlement_price, max_settlement, settlement_fill_price);
449  if( !before_core_hardfork_184 && new_settled.amount == 0 ) // unable to fill this settle order
450  {
451  if( find_object( order_id ) ) // the settle order hasn't been cancelled
452  current_asset_finished = true;
453  break;
454  }
455  settled += new_settled;
456  // before hard fork core-342, `new_settled > 0` is always true, we'll have:
457  // * call order is completely filled (thus itr will change in next loop), or
458  // * settle order is completely filled (thus find_object(order_id) will be false so will break out), or
459  // * reached max_settlement_volume limit (thus new_settled == max_settlement so will break out).
460  //
461  // after hard fork core-342, if new_settled > 0, we'll have:
462  // * call order is completely filled (thus itr will change in next loop), or
463  // * settle order is completely filled (thus find_object(order_id) will be false so will break out), or
464  // * reached max_settlement_volume limit, but it's possible that new_settled < max_settlement,
465  // in this case, new_settled will be zero in next iteration of the loop, so no need to check here.
466  }
467  catch ( const black_swan_exception& e ) {
468  wlog( "Cancelling a settle_order since it may trigger a black swan: ${o}, ${e}",
469  ("o", order)("e", e.to_detail_string()) );
470  cancel_settle_order( order );
471  break;
472  }
473  }
474  if( mia.force_settled_volume != settled.amount )
475  {
476  modify(mia, [settled](asset_bitasset_data_object& b) {
477  b.force_settled_volume = settled.amount;
478  });
479  }
480  }
481  }
483 
484 void database::update_expired_feeds()
485 {
486  const auto head_time = head_block_time();
487  const auto next_maint_time = get_dynamic_global_properties().next_maintenance_time;
488  bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME );
489 
490  const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_feed_expiration>();
491  auto itr = idx.begin();
492  while( itr != idx.end() && itr->feed_is_expired( head_time ) )
493  {
494  const asset_bitasset_data_object& b = *itr;
495  ++itr; // not always process begin() because old code skipped updating some assets before hf 615
496  bool update_cer = false; // for better performance, to only update bitasset once, also check CER in this function
497  const asset_object* asset_ptr = nullptr;
498  // update feeds, check margin calls
499  if( after_hardfork_615 || b.feed_is_expired_before_hardfork_615( head_time ) )
500  {
501  auto old_median_feed = b.current_feed;
502  modify( b, [head_time,next_maint_time,&update_cer]( asset_bitasset_data_object& abdo )
503  {
504  abdo.update_median_feeds( head_time, next_maint_time );
505  if( abdo.need_to_update_cer() )
506  {
507  update_cer = true;
508  abdo.asset_cer_updated = false;
509  abdo.feed_cer_updated = false;
510  }
511  });
512  if( !b.current_feed.settlement_price.is_null()
513  && !b.current_feed.margin_call_params_equal( old_median_feed ) )
514  {
515  asset_ptr = &b.asset_id( *this );
516  check_call_orders( *asset_ptr, true, false, &b );
517  }
518  }
519  // update CER
520  if( update_cer )
521  {
522  if( !asset_ptr )
523  asset_ptr = &b.asset_id( *this );
524  if( asset_ptr->options.core_exchange_rate != b.current_feed.core_exchange_rate )
525  {
526  modify( *asset_ptr, [&b]( asset_object& ao )
527  {
528  ao.options.core_exchange_rate = b.current_feed.core_exchange_rate;
529  });
530  }
531  }
532  } // for each asset whose feed is expired
533 
534  // process assets affected by bitshares-core issue 453 before hard fork 615
535  if( !after_hardfork_615 )
536  {
537  for( asset_id_type a : _issue_453_affected_assets )
538  {
539  check_call_orders( a(*this) );
540  }
541  }
542 }
543 
544 void database::update_core_exchange_rates()
545 {
546  const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_cer_update>();
547  if( idx.begin() != idx.end() )
548  {
549  for( auto itr = idx.rbegin(); itr->need_to_update_cer(); itr = idx.rbegin() )
550  {
551  const asset_bitasset_data_object& b = *itr;
552  const asset_object& a = b.asset_id( *this );
553  if( a.options.core_exchange_rate != b.current_feed.core_exchange_rate )
554  {
555  modify( a, [&b]( asset_object& ao )
556  {
557  ao.options.core_exchange_rate = b.current_feed.core_exchange_rate;
558  });
559  }
560  modify( b, []( asset_bitasset_data_object& abdo )
561  {
562  abdo.asset_cer_updated = false;
563  abdo.feed_cer_updated = false;
564  });
565  }
566  }
567 }
568 
569 void database::update_maintenance_flag( bool new_maintenance_flag )
570 {
571  modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& dpo )
572  {
573  auto maintenance_flag = dynamic_global_property_object::maintenance_flag;
574  dpo.dynamic_flags =
575  (dpo.dynamic_flags & ~maintenance_flag)
576  | (new_maintenance_flag ? maintenance_flag : 0);
577  } );
578  return;
579 }
580 
581 void database::update_withdraw_permissions()
582 {
583  auto& permit_index = get_index_type<withdraw_permission_index>().indices().get<by_expiration>();
584  while( !permit_index.empty() && permit_index.begin()->expiration <= head_block_time() )
585  remove(*permit_index.begin());
586 }
587 
588 void database::clear_expired_htlcs()
589 {
590  const auto& htlc_idx = get_index_type<htlc_index>().indices().get<by_expiration>();
591  while ( htlc_idx.begin() != htlc_idx.end()
592  && htlc_idx.begin()->conditions.time_lock.expiration <= head_block_time() )
593  {
594  const htlc_object& obj = *htlc_idx.begin();
595  const auto amount = asset(obj.transfer.amount, obj.transfer.asset_id);
596  adjust_balance( obj.transfer.from, amount );
597  // notify related parties
598  htlc_refund_operation vop( obj.id, obj.transfer.from, obj.transfer.to, amount,
599  obj.conditions.hash_lock.preimage_hash, obj.conditions.hash_lock.preimage_size );
600  push_applied_operation( vop );
601  remove( obj );
602  }
603 }
604 
606 {
608  share_type total_delta_pob;
609  share_type total_delta_inactive;
610  auto& idx = get_index_type<ticket_index>().indices().get<by_next_update>();
611  while( !idx.empty() && idx.begin()->next_auto_update_time <= head_block_time() )
612  {
613  const ticket_object& ticket = *idx.begin();
614  const auto& stat = get_account_stats_by_owner( ticket.account );
615  if( ticket.status == withdrawing && ticket.current_type == liquid )
616  {
617  adjust_balance( ticket.account, ticket.amount );
618  // Note: amount.asset_id is checked when creating the ticket, so no check here
619  modify( stat, [&ticket](account_statistics_object& aso) {
620  aso.total_core_pol -= ticket.amount.amount;
621  aso.total_pol_value -= ticket.value;
622  });
623  result.removed_objects.insert( ticket.id );
624  remove( ticket );
625  }
626  else
627  {
628  ticket_type old_type = ticket.current_type;
629  share_type old_value = ticket.value;
630  modify( ticket, []( ticket_object& o ) {
631  o.auto_update();
632  });
633  result.updated_objects.insert( ticket.id );
634 
635  share_type delta_inactive_amount;
636  share_type delta_forever_amount;
637  share_type delta_forever_value;
638  share_type delta_other_amount;
639  share_type delta_other_value;
640 
641  if( old_type == lock_forever ) // It implies that the new type is lock_forever too
642  {
643  if( ticket.value == 0 )
644  {
645  total_delta_pob -= ticket.amount.amount;
646  total_delta_inactive += ticket.amount.amount;
647  delta_inactive_amount = ticket.amount.amount;
648  delta_forever_amount = -ticket.amount.amount;
649  }
650  delta_forever_value = ticket.value - old_value;
651  }
652  else // old_type != lock_forever
653  {
654  if( ticket.current_type == lock_forever )
655  {
656  total_delta_pob += ticket.amount.amount;
657  delta_forever_amount = ticket.amount.amount;
658  delta_forever_value = ticket.value;
659  delta_other_amount = -ticket.amount.amount;
660  delta_other_value = -old_value;
661  }
662  else // ticket.current_type != lock_forever
663  {
664  delta_other_value = ticket.value - old_value;
665  }
666  }
667 
668  // Note: amount.asset_id is checked when creating the ticket, so no check here
669  modify( stat, [delta_inactive_amount,delta_forever_amount,delta_forever_value,
670  delta_other_amount,delta_other_value](account_statistics_object& aso) {
671  aso.total_core_inactive += delta_inactive_amount;
672  aso.total_core_pob += delta_forever_amount;
673  aso.total_core_pol += delta_other_amount;
674  aso.total_pob_value += delta_forever_value;
675  aso.total_pol_value += delta_other_value;
676  });
677 
678  }
679  // TODO if a lock_forever ticket lost all the value, remove it
680  }
681 
682  // TODO merge stable tickets with the same account and the same type
683 
684  // Update global data
685  if( total_delta_pob != 0 || total_delta_inactive != 0 )
686  {
688  [total_delta_pob,total_delta_inactive]( dynamic_global_property_object& dgp ) {
689  dgp.total_pob += total_delta_pob;
690  dgp.total_inactive += total_delta_inactive;
691  });
692  }
693 
694  return result;
695 }
696 
697 } }
share_type total_core_pol
Total amount of core token in other tickets.
void modify(const T &obj, const Lambda &m)
ticket_type
Type of a ticket.
Definition: ticket.hpp:33
void adjust_balance(account_id_type account, asset delta)
Adjust a particular account&#39;s balance in a given asset by a delta.
Definition: db_balance.cpp:54
const object * find_object(object_id_type id) const
boost::rational< int32_t > ratio_type
Definition: types.hpp:133
Maintains global state information (committee_member list, current fees)This is an implementation det...
Definition: api.cpp:56
#define elog(FORMAT,...)
Definition: logger.hpp:129
processed_transaction push_proposal(const proposal_object &proposal)
Definition: db_block.cpp:328
void globally_settle_asset(const asset_object &bitasset, const price &settle_price)
Market Helpers
Definition: db_market.cpp:57
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
std::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
const dynamic_global_property_object & get_dynamic_global_properties() const
Definition: db_getter.cpp:54
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT
Definition: config.hpp:35
uint32_t head_block_num() const
Definition: db_getter.cpp:69
share_type total_pol_value
Total value of tickets whose current type is not lock_forever.
generic_index< limit_order_object, limit_order_multi_index_type > limit_order_index
#define wlog(FORMAT,...)
Definition: logger.hpp:123
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:44
generic_index< transaction_history_object, transaction_multi_index_type > transaction_index
price min() const
Definition: asset.hpp:131
void cancel_settle_order(const force_settlement_object &order, bool create_virtual_op=true)
Definition: db_market.cpp:223
uint32_t get_slot_at_time(fc::time_point_sec when) const
void set_max_size(size_t new_max_size)
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT
Definition: config.hpp:36
#define wdump(SEQ)
Definition: logger.hpp:174
object_id_type id
Definition: object.hpp:73
asset amount
The token type and amount in the ticket.
share_type total_pob_value
Total value of tickets whose current type is lock_forever.
void deposit_witness_pay(const witness_object &wit, share_type amount)
Definition: db_balance.cpp:248
#define GRAPHENE_IRREVERSIBLE_THRESHOLD
Definition: config.hpp:45
uint32_t push_applied_operation(const operation &op)
Definition: db_block.cpp:527
#define edump(SEQ)
Definition: logger.hpp:182
#define GRAPHENE_100_PERCENT
Definition: config.hpp:102
int match(const limit_order_object &taker, const limit_order_object &maker, const price &trade_price)
Definition: db_market.cpp:605
void cancel_limit_order(const limit_order_object &order, bool create_virtual_op=true, bool skip_cancel_fee=false)
Definition: db_market.cpp:238
time_point_sec head_block_time() const
Definition: db_getter.cpp:64
#define ilog(FORMAT,...)
Definition: logger.hpp:117
const node_property_object & get_node_properties() const
Definition: db_getter.cpp:89
share_type total_core_inactive
Total amount of core token in inactive lock_forever tickets.
ticket_type current_type
The current type of the ticket.
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
void auto_update()
Update the ticket when it&#39;s time.
ticket_status status
The status of the ticket.
const account_statistics_object & get_account_stats_by_owner(account_id_type owner) const
Definition: db_getter.cpp:139
flat_set< object_id_type > removed_objects
Definition: base.hpp:92
safe< int64_t > share_type
Definition: types.hpp:247
generic_operation_result process_tickets()
Definition: db_update.cpp:605
flat_set< object_id_type > updated_objects
Definition: base.hpp:91
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
account_id_type account
The account who owns the ticket.
a ticket for governance voting
price max() const
Definition: asset.hpp:130
share_type value
The current value of the ticket.
share_type total_core_pob
Total amount of core token in active lock_forever tickets.
#define GRAPHENE_MAX_UNDO_HISTORY
Definition: config.hpp:29
bool check_call_orders(const asset_object &mia, bool enable_black_swan=true, bool for_new_limit_order=false, const asset_bitasset_data_object *bitasset_ptr=nullptr)
Definition: db_market.cpp:1100