BitShares-Core  6.1.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 
31 #include <graphene/chain/hardfork.hpp>
39 
41 
42 namespace graphene { namespace chain {
43 
44 void database::update_global_dynamic_data( const signed_block& b, const uint32_t missed_blocks )
45 {
46  const dynamic_global_property_object& _dgp = get_dynamic_global_properties();
47 
48  // dynamic global properties updating
49  modify( _dgp, [&b,this,missed_blocks]( dynamic_global_property_object& dgp ){
50  const uint32_t block_num = b.block_num();
51  if( BOOST_UNLIKELY( block_num == 1 ) )
52  dgp.recently_missed_count = 0;
53  else if( !_checkpoints.empty() && _checkpoints.rbegin()->first >= block_num )
54  dgp.recently_missed_count = 0;
55  else if( missed_blocks )
56  dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
57  else if( dgp.recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )
58  dgp.recently_missed_count -= GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT;
59  else if( dgp.recently_missed_count > 0 )
60  dgp.recently_missed_count--;
61 
62  dgp.head_block_number = block_num;
63  dgp.head_block_id = b.id();
64  dgp.time = b.timestamp;
65  dgp.current_witness = b.witness;
66  dgp.recent_slots_filled = (
67  (dgp.recent_slots_filled << 1)
68  + 1) << missed_blocks;
69  dgp.current_aslot += missed_blocks+1;
70  });
71 
73  {
74  GRAPHENE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception,
75  "The database does not have enough undo history to support a blockchain with so many missed blocks. "
76  "Please add a checkpoint if you would like to continue applying blocks beyond this point.",
77  ("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number)
78  ("recently_missed",_dgp.recently_missed_count)("max_undo",GRAPHENE_MAX_UNDO_HISTORY) );
79  }
80 
81  _undo_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
82  _fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 );
83 }
84 
85 void database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block)
86 {
87  const global_property_object& gpo = get_global_properties();
88  const dynamic_global_property_object& dpo = get_dynamic_global_properties();
89  uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time( new_block.timestamp );
90 
91  share_type witness_pay = std::min( gpo.parameters.witness_pay_per_block, dpo.witness_budget );
92 
93  modify( dpo, [&]( dynamic_global_property_object& _dpo )
94  {
95  _dpo.witness_budget -= witness_pay;
96  } );
97 
98  deposit_witness_pay( signing_witness, witness_pay );
99 
100  modify( signing_witness, [&]( witness_object& _wit )
101  {
102  _wit.last_aslot = new_block_aslot;
103  _wit.last_confirmed_block_num = new_block.block_num();
104  } );
105 }
106 
107 void database::update_last_irreversible_block()
108 {
109  const global_property_object& gpo = get_global_properties();
110  const dynamic_global_property_object& dpo = get_dynamic_global_properties();
111 
112  // TODO for better performance, move this to db_maint, because only need to do it once per maintenance interval
113  vector< const witness_object* > wit_objs;
114  wit_objs.reserve( gpo.active_witnesses.size() );
115  for( const witness_id_type& wid : gpo.active_witnesses )
116  wit_objs.push_back( &(wid(*this)) );
117 
118  static_assert( GRAPHENE_IRREVERSIBLE_THRESHOLD > 0, "irreversible threshold must be nonzero" );
119 
120  // 1 1 1 2 2 2 2 2 2 2 -> 2 .3*10 = 3
121  // 1 1 1 1 1 1 1 2 2 2 -> 1
122  // 3 3 3 3 3 3 3 3 3 3 -> 3
123  // 3 3 3 4 4 4 4 4 4 4 -> 4
124 
125  size_t offset = ((GRAPHENE_100_PERCENT - GRAPHENE_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / GRAPHENE_100_PERCENT);
126 
127  std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(),
128  []( const witness_object* a, const witness_object* b )
129  {
130  return a->last_confirmed_block_num < b->last_confirmed_block_num;
131  } );
132 
133  uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num;
134 
135  if( new_last_irreversible_block_num > dpo.last_irreversible_block_num )
136  {
137  modify( dpo, [&]( dynamic_global_property_object& _dpo )
138  {
139  _dpo.last_irreversible_block_num = new_last_irreversible_block_num;
140  } );
141  }
142 }
143 
144 void database::clear_expired_transactions()
145 { try {
146  //Look for expired transactions in the deduplication list, and remove them.
147  //Transactions must have expired by at least two forking windows in order to be removed.
148  auto& transaction_idx = static_cast<transaction_index&>(get_mutable_index(implementation_ids,
149  impl_transaction_history_object_type));
150  const auto& dedupe_index = transaction_idx.indices().get<by_expiration>();
151  while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.begin()->trx.expiration) )
152  transaction_idx.remove(*dedupe_index.begin());
154 
155 void database::clear_expired_proposals()
156 {
157  const auto& proposal_expiration_index = get_index_type<proposal_index>().indices().get<by_expiration>();
158  while( !proposal_expiration_index.empty() && proposal_expiration_index.begin()->expiration_time <= head_block_time() )
159  {
160  const proposal_object& proposal = *proposal_expiration_index.begin();
161  processed_transaction result;
162  try {
163  if( proposal.is_authorized_to_execute(*this) )
164  {
165  result = push_proposal(proposal);
166  //TODO: Do something with result so plugins can process it.
167  continue;
168  }
169  } catch( const fc::exception& e ) {
170  elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
171  ("proposal", proposal)("error", e.to_detail_string()));
172  }
173  remove(proposal);
174  }
175 }
176 
177 // Helper function to check whether we need to udpate current_feed.settlement_price.
178 static optional<price> get_derived_current_feed_price( const database& db,
179  const asset_bitasset_data_object& bitasset )
180 {
181  optional<price> result;
182  // check for null first
183  if( bitasset.median_feed.settlement_price.is_null() )
184  {
185  if( bitasset.current_feed.settlement_price.is_null() )
186  return result;
187  else
188  return bitasset.median_feed.settlement_price;
189  }
190 
192  const auto bsrm = bitasset.get_black_swan_response_method();
193  if( bsrm_type::no_settlement == bsrm )
194  {
195  // Find the call order with the least collateral ratio
196  const call_order_object* call_ptr = db.find_least_collateralized_short( bitasset, true );
197  if( call_ptr )
198  {
199  // GS if : call_ptr->collateralization() < ~( bitasset.median_feed.max_short_squeeze_price() )
200  auto least_collateral = call_ptr->collateralization();
201  auto lowest_callable_feed_price = (~least_collateral) / ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM,
202  bitasset.current_feed.maximum_short_squeeze_ratio );
203  result = std::max( bitasset.median_feed.settlement_price, lowest_callable_feed_price );
204  }
205  else // there is no call order of this bitasset
206  result = bitasset.median_feed.settlement_price;
207  }
208  else if( bsrm_type::individual_settlement_to_fund == bsrm && bitasset.individual_settlement_debt > 0 )
209  {
210  // Check whether to cap
211  price fund_price = asset( bitasset.individual_settlement_debt, bitasset.asset_id )
212  / asset( bitasset.individual_settlement_fund, bitasset.options.short_backing_asset );
213  auto lowest_callable_feed_price = fund_price * bitasset.get_margin_call_order_ratio();
214  result = std::max( bitasset.median_feed.settlement_price, lowest_callable_feed_price );
215  }
216  else // should not cap
217  result = bitasset.median_feed.settlement_price;
218 
219  // Check whether it's necessary to update
220  if( result.valid() && (*result) == bitasset.current_feed.settlement_price )
221  result.reset();
222  return result;
223 }
224 
225 void database::update_bitasset_current_feed( const asset_bitasset_data_object& bitasset, bool skip_median_update )
226 {
227  // For better performance, if nothing to update, we return
228  optional<price> new_current_feed_price;
230  const auto bsrm = bitasset.get_black_swan_response_method();
231  if( skip_median_update )
232  {
233  if( bsrm_type::no_settlement != bsrm && bsrm_type::individual_settlement_to_fund != bsrm )
234  {
235  // it's possible that current_feed was capped thus we still need to update it
236  if( bitasset.current_feed.settlement_price == bitasset.median_feed.settlement_price )
237  return;
238  new_current_feed_price = bitasset.median_feed.settlement_price;
239  }
240  else
241  {
242  new_current_feed_price = get_derived_current_feed_price( *this, bitasset );
243  if( !new_current_feed_price.valid() )
244  return;
245  }
246  }
247 
248  // We need to update the database
249  modify( bitasset, [this, skip_median_update, &new_current_feed_price, &bsrm]
251  {
252  if( !skip_median_update )
253  {
254  const auto& head_time = head_block_time();
255  const auto& maint_time = get_dynamic_global_properties().next_maintenance_time;
256  abdo.update_median_feeds( head_time, maint_time );
257  abdo.current_feed = abdo.median_feed;
258  if( bsrm_type::no_settlement == bsrm || bsrm_type::individual_settlement_to_fund == bsrm )
259  new_current_feed_price = get_derived_current_feed_price( *this, abdo );
260  }
261  if( new_current_feed_price.valid() )
262  abdo.current_feed.settlement_price = *new_current_feed_price;
263  } );
264 }
265 
266 void database::clear_expired_orders()
267 { try {
268  //Cancel expired limit orders
269  auto head_time = head_block_time();
271 
272  bool before_core_hardfork_606 = ( maint_time <= HARDFORK_CORE_606_TIME ); // feed always trigger call
273 
274  auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>();
275  while( !limit_index.empty() && limit_index.begin()->expiration <= head_time )
276  {
277  const limit_order_object& order = *limit_index.begin();
278  auto base_asset = order.sell_price.base.asset_id;
279  auto quote_asset = order.sell_price.quote.asset_id;
280  cancel_limit_order( order );
281  if( before_core_hardfork_606 )
282  {
283  // check call orders
284  // Comments below are copied from limit_order_cancel_evaluator::do_apply(...)
285  // Possible optimization: order can be called by cancelling a limit order
286  // if the canceled order was at the top of the book.
287  // Do I need to check calls in both assets?
288  check_call_orders( base_asset( *this ) );
289  check_call_orders( quote_asset( *this ) );
290  }
291  }
293 
294 void database::clear_expired_force_settlements()
295 { try {
296  // Process expired force settlement orders
297 
298  // TODO Possible performance optimization. Looping through all assets is not ideal.
299  // - One idea is to check time first, if any expired settlement found, check asset.
300  // However, due to max_settlement_volume, this does not work, i.e. time meets but have to
301  // skip due to volume limit.
302  // - Instead, maintain some data e.g. (whether_force_settle_volome_meets, first_settle_time)
303  // in bitasset_data object and index by them, then we could process here faster.
304  // Note: due to rounding, even when settled < max_volume, it is still possible that we have to skip
305  const auto& settlement_index = get_index_type<force_settlement_index>().indices().get<by_expiration>();
306  if( settlement_index.empty() )
307  return;
308 
309  const auto& head_time = head_block_time();
310  const auto& maint_time = get_dynamic_global_properties().next_maintenance_time;
311 
312  const bool before_core_hardfork_184 = ( maint_time <= HARDFORK_CORE_184_TIME ); // something-for-nothing
313  const bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding
314 
315  asset_id_type current_asset = settlement_index.begin()->settlement_asset_id();
316  const asset_object* mia_object_ptr = &get(current_asset);
317  const asset_bitasset_data_object* mia_ptr = &mia_object_ptr->bitasset_data(*this);
318 
319  asset max_settlement_volume;
320  price settlement_fill_price;
321  price settlement_price;
322  bool current_asset_finished = false;
323 
324  auto next_asset = [&current_asset, &mia_object_ptr, &mia_ptr, &current_asset_finished, &settlement_index, this] {
325  const auto bound = settlement_index.upper_bound(current_asset);
326  if( bound == settlement_index.end() )
327  return false;
328  current_asset = bound->settlement_asset_id();
329  mia_object_ptr = &get(current_asset);
330  mia_ptr = &mia_object_ptr->bitasset_data(*this);
331  current_asset_finished = false;
332  return true;
333  };
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  const force_settlement_object& settle_order = *itr;
341  auto settle_order_id = settle_order.id;
342 
343  if( current_asset != settle_order.settlement_asset_id() )
344  {
345  current_asset = settle_order.settlement_asset_id();
346  mia_object_ptr = &get(current_asset);
347  mia_ptr = &mia_object_ptr->bitasset_data(*this);
348  // Note: we did not reset current_asset_finished to false here, it is OK,
349  // because current_asset should not have changed if current_asset_finished is true
350  }
351  const asset_object& mia_object = *mia_object_ptr;
352  const asset_bitasset_data_object& mia = *mia_ptr;
353 
354  if( mia.has_settlement() )
355  {
356  ilog( "Canceling a force settlement because of black swan" );
357  cancel_settle_order( settle_order );
358  continue;
359  }
360 
361  // Has this order not reached its settlement date?
362  if( settle_order.settlement_date > head_time )
363  {
364  if( next_asset() )
365  continue;
366  break;
367  }
368  // Can we still settle in this asset?
370  {
371  ilog("Canceling a force settlement in ${asset} because settlement price is null",
372  ("asset", mia_object.symbol));
373  cancel_settle_order(settle_order);
374  continue;
375  }
376  if( GRAPHENE_100_PERCENT == mia.options.force_settlement_offset_percent ) // settle something for nothing
377  {
378  ilog( "Canceling a force settlement in ${asset} because settlement offset is 100%",
379  ("asset", mia_object.symbol));
380  cancel_settle_order(settle_order);
381  continue;
382  }
383  // Note: although current supply would decrease during filling the settle orders,
384  // we always calculate with the initial value
385  if( max_settlement_volume.asset_id != current_asset )
386  max_settlement_volume = mia_object.amount( mia.max_force_settlement_volume(
387  mia_object.dynamic_data(*this).current_supply ) );
388  // When current_asset_finished is true, this would be the 2nd time processing the same order.
389  // In this case, we move to the next asset.
390  if( mia.force_settled_volume >= max_settlement_volume.amount || current_asset_finished )
391  {
392  if( next_asset() )
393  continue;
394  break;
395  }
396 
397  if( settlement_fill_price.base.asset_id != current_asset ) // only calculate once per asset
398  settlement_fill_price = mia.current_feed.settlement_price
401 
402  if( before_core_hardfork_342 )
403  {
404  auto& pays = settle_order.balance;
405  auto receives = (settle_order.balance * mia.current_feed.settlement_price);
406  receives.amount = static_cast<uint64_t>( ( fc::uint128_t(receives.amount.value) *
409  assert(receives <= settle_order.balance * mia.current_feed.settlement_price);
410  settlement_price = pays / receives;
411  }
412  else if( settlement_price.base.asset_id != current_asset ) // only calculate once per asset
413  settlement_price = settlement_fill_price;
414 
415  asset settled = mia_object.amount(mia.force_settled_volume);
416  // Match against the least collateralized short until the settlement is finished or we reach max settlements
417  while( settled < max_settlement_volume && find_object(settle_order_id) )
418  {
419  if( 0 == settle_order.balance.amount )
420  {
421  wlog( "0 settlement detected" );
422  cancel_settle_order( settle_order );
423  break;
424  }
425 
426  const call_order_object* call_ptr = find_least_collateralized_short( mia, true );
427  // Note: there can be no debt position due to individual settlements
428  if( !call_ptr ) // no debt position
429  {
430  wlog( "No debt position found when processing force settlement ${o}", ("o",settle_order) );
431  cancel_settle_order( settle_order );
432  break;
433  }
434 
435  try {
436  asset max_settlement = max_settlement_volume - settled;
437 
438  asset new_settled = match( settle_order, *call_ptr, settlement_price, mia,
439  max_settlement, settlement_fill_price );
440  if( !before_core_hardfork_184 && new_settled.amount == 0 ) // unable to fill this settle order
441  {
442  // current asset is finished when the settle order hasn't been cancelled
443  current_asset_finished = ( nullptr != find_object( settle_order_id ) );
444  break;
445  }
446  settled += new_settled;
447  // before hard fork core-342, `new_settled > 0` is always true, we'll have:
448  // * call order is completely filled (thus call_ptr will change in next loop), or
449  // * settle order is completely filled (thus find_object(settle_order_id) will be false so will
450  // break out), or
451  // * reached max_settlement_volume limit (thus new_settled == max_settlement so will break out).
452  //
453  // after hard fork core-342, if new_settled > 0, we'll have:
454  // * call order is completely filled (thus call_ptr will change in next loop), or
455  // * settle order is completely filled (thus find_object(settle_order_id) will be false so will
456  // break out), or
457  // * reached max_settlement_volume limit, but it's possible that new_settled < max_settlement,
458  // in this case, new_settled will be zero in next iteration of the loop, so no need to check here.
459  }
460  catch ( const black_swan_exception& e ) {
461  wlog( "Cancelling a settle_order since it may trigger a black swan: ${o}, ${e}",
462  ("o", settle_order)("e", e.to_detail_string()) );
463  cancel_settle_order( settle_order );
464  break;
465  }
466  }
467  if( mia.force_settled_volume != settled.amount )
468  {
469  modify(mia, [&settled](asset_bitasset_data_object& b) {
470  b.force_settled_volume = settled.amount;
471  });
472  }
473  }
475 
476 void database::update_expired_feeds()
477 {
478  const auto head_time = head_block_time();
479  bool after_hardfork_615 = ( head_time >= HARDFORK_615_TIME );
480  bool after_core_hardfork_2582 = HARDFORK_CORE_2582_PASSED( head_time ); // Price feed issues
481 
482  const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_feed_expiration>();
483  auto itr = idx.begin();
484  while( itr != idx.end() && itr->feed_is_expired( head_time ) )
485  {
486  const asset_bitasset_data_object& b = *itr;
487  ++itr; // not always process begin() because old code skipped updating some assets before hf 615
488 
489  // update feeds, check margin calls
490  if( !( after_hardfork_615 || b.feed_is_expired_before_hf_615( head_time ) ) )
491  continue;
492 
493  auto old_current_feed = b.current_feed;
494  auto old_median_feed = b.median_feed;
495  const asset_object& asset_obj = b.asset_id( *this );
497  // Note: we don't try to revive the bitasset here if it was GSed // TODO probably we should do it
498 
500  && !b.current_feed.margin_call_params_equal( old_current_feed ) )
501  {
502  check_call_orders( asset_obj, true, false, &b, true );
503  }
504  else if( after_core_hardfork_2582 && !b.median_feed.settlement_price.is_null()
505  && !b.median_feed.margin_call_params_equal( old_median_feed ) )
506  {
507  check_call_orders( asset_obj, true, false, &b, true );
508  }
509  // update CER
510  if( b.need_to_update_cer() )
511  {
512  modify( b, []( asset_bitasset_data_object& abdo )
513  {
514  abdo.asset_cer_updated = false;
515  abdo.feed_cer_updated = false;
516  });
518  {
519  modify( asset_obj, [&b]( asset_object& ao )
520  {
522  });
523  }
524  }
525  } // for each asset whose feed is expired
526 
527  // process assets affected by bitshares-core issue 453 before hard fork 615
528  if( !after_hardfork_615 )
529  {
530  for( asset_id_type a : _issue_453_affected_assets )
531  {
532  check_call_orders( a(*this) );
533  }
534  }
535 }
536 
537 void database::update_core_exchange_rates()
538 {
539  const auto& idx = get_index_type<asset_bitasset_data_index>().indices().get<by_cer_update>();
540  if( idx.begin() != idx.end() )
541  {
542  for( auto itr = idx.rbegin(); itr->need_to_update_cer(); itr = idx.rbegin() )
543  {
544  const asset_bitasset_data_object& b = *itr;
545  const asset_object& a = b.asset_id( *this );
547  {
548  modify( a, [&b]( asset_object& ao )
549  {
551  });
552  }
553  modify( b, []( asset_bitasset_data_object& abdo )
554  {
555  abdo.asset_cer_updated = false;
556  abdo.feed_cer_updated = false;
557  });
558  }
559  }
560 }
561 
562 void database::update_maintenance_flag( bool new_maintenance_flag )
563 {
565  {
566  auto maintenance_flag = dynamic_global_property_object::maintenance_flag;
567  dpo.dynamic_flags =
568  (dpo.dynamic_flags & (uint32_t)(~maintenance_flag))
569  | (new_maintenance_flag ? (uint32_t)maintenance_flag : 0U);
570  } );
571  return;
572 }
573 
574 void database::update_withdraw_permissions()
575 {
576  auto& permit_index = get_index_type<withdraw_permission_index>().indices().get<by_expiration>();
577  while( !permit_index.empty() && permit_index.begin()->expiration <= head_block_time() )
578  remove(*permit_index.begin());
579 }
580 
581 void database::clear_expired_htlcs()
582 {
583  const auto& htlc_idx = get_index_type<htlc_index>().indices().get<by_expiration>();
584  while ( htlc_idx.begin() != htlc_idx.end()
585  && htlc_idx.begin()->conditions.time_lock.expiration <= head_block_time() )
586  {
587  const htlc_object& obj = *htlc_idx.begin();
588  const auto amount = asset(obj.transfer.amount, obj.transfer.asset_id);
589  adjust_balance( obj.transfer.from, amount );
590  // notify related parties
591  htlc_refund_operation vop( obj.get_id(), obj.transfer.from, obj.transfer.to, amount,
593  push_applied_operation( vop );
594  remove( obj );
595  }
596 }
597 
599 {
600  const auto maint_time = get_dynamic_global_properties().next_maintenance_time;
601  ticket_version version = ( HARDFORK_CORE_2262_PASSED(maint_time) ? ticket_v2 : ticket_v1 );
602 
604  share_type total_delta_pob;
605  share_type total_delta_inactive;
606  auto& idx = get_index_type<ticket_index>().indices().get<by_next_update>();
607  while( !idx.empty() && idx.begin()->next_auto_update_time <= head_block_time() )
608  {
609  const ticket_object& ticket = *idx.begin();
610  const auto& stat = get_account_stats_by_owner( ticket.account );
611  if( ticket.status == withdrawing && ticket.current_type == liquid )
612  {
613  adjust_balance( ticket.account, ticket.amount );
614  // Note: amount.asset_id is checked when creating the ticket, so no check here
615  modify( stat, [&ticket](account_statistics_object& aso) {
616  aso.total_core_pol -= ticket.amount.amount;
617  aso.total_pol_value -= ticket.value;
618  });
619  result.removed_objects.insert( ticket.id );
620  remove( ticket );
621  }
622  else
623  {
624  ticket_type old_type = ticket.current_type;
625  share_type old_value = ticket.value;
626  modify( ticket, [version]( ticket_object& o ) {
627  o.auto_update( version );
628  });
629  result.updated_objects.insert( ticket.id );
630 
631  share_type delta_inactive_amount;
632  share_type delta_forever_amount;
633  share_type delta_forever_value;
634  share_type delta_other_amount;
635  share_type delta_other_value;
636 
637  if( old_type == lock_forever ) // It implies that the new type is lock_forever too
638  {
639  if( ticket.value == 0 )
640  {
641  total_delta_pob -= ticket.amount.amount;
642  total_delta_inactive += ticket.amount.amount;
643  delta_inactive_amount = ticket.amount.amount;
644  delta_forever_amount = -ticket.amount.amount;
645  }
646  delta_forever_value = ticket.value - old_value;
647  }
648  else // old_type != lock_forever
649  {
650  if( ticket.current_type == lock_forever )
651  {
652  total_delta_pob += ticket.amount.amount;
653  delta_forever_amount = ticket.amount.amount;
654  delta_forever_value = ticket.value;
655  delta_other_amount = -ticket.amount.amount;
656  delta_other_value = -old_value;
657  }
658  else // ticket.current_type != lock_forever
659  {
660  delta_other_value = ticket.value - old_value;
661  }
662  }
663 
664  // Note: amount.asset_id is checked when creating the ticket, so no check here
665  modify( stat, [delta_inactive_amount,delta_forever_amount,delta_forever_value,
666  delta_other_amount,delta_other_value](account_statistics_object& aso) {
667  aso.total_core_inactive += delta_inactive_amount;
668  aso.total_core_pob += delta_forever_amount;
669  aso.total_core_pol += delta_other_amount;
670  aso.total_pob_value += delta_forever_value;
671  aso.total_pol_value += delta_other_value;
672  });
673 
674  }
675  // TODO if a lock_forever ticket lost all the value, remove it
676  }
677 
678  // TODO merge stable tickets with the same account and the same type
679 
680  // Update global data
681  if( total_delta_pob != 0 || total_delta_inactive != 0 )
682  {
684  [total_delta_pob,total_delta_inactive]( dynamic_global_property_object& dgp ) {
685  dgp.total_pob += total_delta_pob;
686  dgp.total_inactive += total_delta_inactive;
687  });
688  }
689 
690  return result;
691 }
692 
693 void database::update_credit_offers_and_deals()
694 {
695  const auto head_time = head_block_time();
696 
697  // Auto-disable offers
698  const auto& offer_idx = get_index_type<credit_offer_index>().indices().get<by_auto_disable_time>();
699  auto offer_itr = offer_idx.lower_bound( true );
700  auto offer_itr_end = offer_idx.upper_bound( boost::make_tuple( true, head_time ) );
701  while( offer_itr != offer_itr_end )
702  {
703  const credit_offer_object& offer = *offer_itr;
704  ++offer_itr;
705  modify( offer, []( credit_offer_object& obj ) {
706  obj.enabled = false;
707  });
708  }
709 
710  // Auto-process deals
711  const auto& deal_idx = get_index_type<credit_deal_index>().indices().get<by_latest_repay_time>();
712  const auto& deal_summary_idx = get_index_type<credit_deal_summary_index>().indices().get<by_offer_borrower>();
713  auto deal_itr_end = deal_idx.upper_bound( head_time );
714  for( auto deal_itr = deal_idx.begin(); deal_itr != deal_itr_end; deal_itr = deal_idx.begin() )
715  {
716  const credit_deal_object& deal = *deal_itr;
717 
718  // Update offer
719  // Note: offer balance can be zero after updated. TODO remove zero-balance offers after a period
720  const credit_offer_object& offer = deal.offer_id(*this);
721  modify( offer, [&deal]( credit_offer_object& obj ){
722  obj.total_balance -= deal.debt_amount;
723  });
724 
725  // Process deal summary
726  auto summ_itr = deal_summary_idx.find( boost::make_tuple( deal.offer_id, deal.borrower ) );
727  if( summ_itr == deal_summary_idx.end() ) // This should not happen, just be defensive here
728  {
729  // We do not do FC_ASSERT or FC_THROW here to avoid halting the chain
730  elog( "Error: unable to find the credit deal summary object for credit deal ${d}",
731  ("d", deal) );
732  }
733  else
734  {
735  const credit_deal_summary_object& summ_obj = *summ_itr;
736  if( summ_obj.total_debt_amount == deal.debt_amount )
737  {
738  remove( summ_obj );
739  }
740  else
741  {
742  modify( summ_obj, [&deal]( credit_deal_summary_object& obj ){
743  obj.total_debt_amount -= deal.debt_amount;
744  });
745  }
746  }
747 
748  // Adjust balance
750 
751  // Notify related parties
753  deal.get_id(), deal.offer_id, deal.offer_owner, deal.borrower,
754  asset( deal.debt_amount, deal.debt_asset ),
756  deal.fee_rate ) );
757 
758  // Remove the deal
759  remove( deal );
760  }
761 }
762 
763 } }
share_type total_debt_amount
How much funds borrowed.
share_type total_core_pol
Total amount of core token in other tickets.
A credit offer is a fund that can be used by other accounts who provide certain collateral.
account_id_type offer_owner
Owner of the credit offer, redundant info for ease of querying.
#define GRAPHENE_COLLATERAL_RATIO_DENOM
Definition: config.hpp:113
asset_id_type asset_id
The asset this object belong to.
void modify(const T &obj, const Lambda &m)
share_type collateral_amount
How much funds in collateral.
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
database object to store HTLCs
Definition: htlc_object.hpp:40
contains properties that only apply to bitassets (market issued assets)
black_swan_response_type
Defines how a BitAsset would respond to black swan events.
Definition: asset_ops.hpp:112
share_type force_settled_volume
This is the volume of this asset which has been force-settled this maintanence interval.
boost::rational< int32_t > ratio_type
Definition: types.hpp:150
time_point_sec head_block_time() const
Definition: db_getter.cpp:67
A credit deal describes the details of a borrower&#39;s borrowing of funds from a credit offer...
Maintains global state information (committee_member list, current fees)This is an implementation det...
bitasset_options::black_swan_response_type get_black_swan_response_method() const
Get the effective black swan response method of this bitasset.
Definition: api.cpp:48
#define elog(FORMAT,...)
Definition: logger.hpp:129
tracks debt and call price information
uint16_t force_settlement_offset_percent
This is the percent to adjust the feed price in the short&#39;s favor in the event of a forced settlement...
Definition: asset_ops.hpp:162
A credit deal summary describes the summary of a borrower&#39;s borrowing of funds from a credit offer...
processed_transaction push_proposal(const proposal_object &proposal)
Definition: db_block.cpp:329
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
share_type max_force_settlement_volume(share_type current_supply) const
Calculate the maximum force settlement volume per maintenance interval, given the current share suppl...
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT
Definition: config.hpp:37
const asset_dynamic_data_object & dynamic_data(const DB &db) const
share_type total_pol_value
Total value of tickets whose current type is not lock_forever.
bool enabled
Whether this offer is available.
#define wlog(FORMAT,...)
Definition: logger.hpp:123
generic_index< transaction_history_object, transaction_multi_index_type > transaction_index
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
void auto_update(ticket_version version)
Update the ticket when it&#39;s time.
const asset_bitasset_data_object & bitasset_data(const DB &db) const
asset amount(share_type a) const
Helper function to get an asset object with the given amount in this asset&#39;s type.
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, bool mute_exceptions=false, bool skip_matching_settle_orders=false)
Definition: db_market.cpp:1745
void update_median_feeds(time_point_sec current_time, time_point_sec next_maintenance_time)
share_type total_balance
Total size of the fund.
void set_max_size(size_t new_max_size)
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT
Definition: config.hpp:38
uint32_t push_applied_operation(const operation &op, bool is_virtual=true)
Definition: db_block.cpp:535
uint32_t get_slot_at_time(fc::time_point_sec when) const
bool feed_cer_updated
Track whether core exchange rate in current feed has updated.
object_id< SpaceID, TypeID > get_id() const
Definition: object.hpp:113
object_id_type id
Definition: object.hpp:69
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)
helper to handle witness pay
Definition: db_balance.cpp:249
#define GRAPHENE_IRREVERSIBLE_THRESHOLD
Definition: config.hpp:45
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:108
#define GRAPHENE_100_PERCENT
Definition: config.hpp:102
asset_id_type debt_asset
Asset type of the debt, redundant info for ease of querying.
void cancel_limit_order(const limit_order_object &order, bool create_virtual_op=true, bool skip_cancel_fee=false)
Definition: db_market.cpp:505
bool valid() const
Definition: optional.hpp:186
#define ilog(FORMAT,...)
Definition: logger.hpp:117
share_type total_core_inactive
Total amount of core token in inactive lock_forever tickets.
string symbol
Ticker symbol for this asset, i.e. "USD".
tracks bitassets scheduled for force settlement at some point in the future.
ticket_type current_type
The current type of the ticket.
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:479
price_feed_with_icr current_feed
This is the currently active price feed, calculated from median_feed and other parameters.
A credit deal expired without being fully repaid.
bool need_to_update_cer() const
Whether need to update core_exchange_rate in asset_object.
const node_property_object & get_node_properties() const
Definition: db_getter.cpp:92
void update_bitasset_current_feed(const asset_bitasset_data_object &bitasset, bool skip_median_update=false)
Definition: db_update.cpp:225
share_type debt_amount
How much funds borrowed.
ticket_status status
The status of the ticket.
std::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
flat_set< object_id_type > removed_objects
Definition: base.hpp:92
tracks the parameters of an assetAll assets have a globally unique symbol name that controls how they...
safe< int64_t > share_type
Definition: types.hpp:309
const account_statistics_object & get_account_stats_by_owner(account_id_type owner) const
Definition: db_getter.cpp:142
const call_order_object * find_least_collateralized_short(const asset_bitasset_data_object &bitasset, bool force_by_collateral_index) const
Definition: db_getter.cpp:161
asset_id_type asset_id
Definition: asset.hpp:37
credit_offer_id_type offer_id
ID of the credit offer.
bitasset_options options
The tunable options for BitAssets are stored in this field.
generic_operation_result process_tickets()
Definition: db_update.cpp:598
flat_set< object_id_type > updated_objects
Definition: base.hpp:91
asset_id_type collateral_asset
Asset type of the collateral.
uint32_t fee_rate
Fee rate, the demominator is GRAPHENE_FEE_RATE_DENOM.
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
const dynamic_global_property_object & get_dynamic_global_properties() const
Definition: db_getter.cpp:57
const object * find_object(const object_id_type &id) const
share_type current_supply
The number of shares currently in existence.
ticket_version
Version of a ticket.
account_id_type account
The account who owns the ticket.
bool is_null() const
Definition: asset.cpp:229
bool asset_cer_updated
Track whether core_exchange_rate in corresponding asset_object has updated.
a ticket for governance voting
bool feed_is_expired_before_hf_615(time_point_sec current_time) const
share_type value
The current value of the ticket.
void cancel_settle_order(const force_settlement_object &order)
Definition: db_market.cpp:496
share_type total_core_pob
Total amount of core token in active lock_forever tickets.
#define GRAPHENE_MAX_UNDO_HISTORY
Definition: config.hpp:31
price_feed_with_icr median_feed
This is the median of values from the currently active feeds.
an offer to sell an amount of an asset at a specified exchange rate by a certain timeThe objects are ...
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:47
bool margin_call_params_equal(const price_feed &b) const
Definition: asset.hpp:291
price core_exchange_rate
Price at which automatically exchanging this asset for CORE from fee pool occurs (used for paying fee...
Definition: asset.hpp:183