BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
asset_evaluator.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2018 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  */
30 #include <graphene/chain/hardfork.hpp>
32 
33 #include <functional>
34 
35 namespace graphene { namespace chain {
36 namespace detail {
37 
38  // TODO review and remove code below and links to it after hf_1774
39  void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options)
40  {
41  if( block_time < HARDFORK_1774_TIME )
42  {
45  "Asset extension reward percent must be less than 100% till HARDFORK_1774_TIME!");
46  }
47  }
48 
49  void check_bitasset_options_hf_bsip74( const fc::time_point_sec& block_time, const bitasset_options& options)
50  {
51  // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:
52  FC_ASSERT( block_time >= HARDFORK_CORE_BSIP74_TIME
53  || !options.extensions.value.margin_call_fee_ratio.valid(),
54  "A BitAsset's MCFR cannot be set before Hardfork BSIP74" );
55  }
56 
57  // TODO review and remove code below and links to it after HARDFORK_BSIP_81_TIME
58  void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options)
59  {
60  if (block_time < HARDFORK_BSIP_81_TIME) {
61  // Taker fees should not be set until activation of BSIP81
63  "Taker fee percent should not be defined before HARDFORK_BSIP_81_TIME");
64  }
65  }
66 
67  // TODO review and remove code below and links to it after HARDFORK_BSIP_48_75_TIME
68  void check_asset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const asset_options& options)
69  {
70  if ( !HARDFORK_BSIP_48_75_PASSED( block_time ) )
71  {
72  // new issuer permissions should not be set until activation of BSIP_48_75
73  FC_ASSERT( !(options.issuer_permissions & ~ASSET_ISSUER_PERMISSION_ENABLE_BITS_MASK),
74  "New asset issuer permission bits should not be set before HARDFORK_BSIP_48_75_TIME" );
75  // Note: no check for flags here because we didn't check in the past
76  }
77  }
78 
79  // TODO review and remove code below and links to it after HARDFORK_BSIP_48_75_TIME
81  {
82  if ( !HARDFORK_BSIP_48_75_PASSED( block_time ) )
83  {
84  // new params should not be set until activation of BSIP_48_75
85  FC_ASSERT( !options.extensions.value.maintenance_collateral_ratio.valid(),
86  "Maintenance collateral ratio should not be defined by asset owner "
87  "before HARDFORK_BSIP_48_75_TIME" );
88  FC_ASSERT( !options.extensions.value.maximum_short_squeeze_ratio.valid(),
89  "Maximum short squeeze ratio should not be defined by asset owner "
90  "before HARDFORK_BSIP_48_75_TIME" );
91  }
92  }
93 
94  // TODO review and remove code below and links to it after HARDFORK_BSIP_48_75_TIME
96  const asset_update_operation::ext& extensions )
97  {
98  if ( !HARDFORK_BSIP_48_75_PASSED( block_time ) )
99  {
100  // new extensions should not be set until activation of BSIP_48_75
101  FC_ASSERT( !extensions.new_precision.valid(),
102  "new_precision should not be set before HARDFORK_BSIP_48_75_TIME" );
103  FC_ASSERT( !extensions.skip_core_exchange_rate.valid(),
104  "skip_core_exchange_rate should not be set before HARDFORK_BSIP_48_75_TIME" );
105  }
106  }
107 
108  // TODO review and remove code below and links to it after HARDFORK_BSIP_77_TIME
110  const asset_publish_feed_operation::ext& extensions )
111  {
112  if ( !HARDFORK_BSIP_77_PASSED( block_time ) )
113  {
114  // new extensions should not be set until activation of BSIP_77
116  "Initial collateral ratio should not be defined before HARDFORK_BSIP_77_TIME" );
117  }
118  }
119 
120  // TODO review and remove code below and links to it after HARDFORK_BSIP_77_TIME
122  {
123  if ( !HARDFORK_BSIP_77_PASSED( block_time ) ) {
124  // ICR should not be set until activation of BSIP77
125  FC_ASSERT(!options.extensions.value.initial_collateral_ratio.valid(),
126  "Initial collateral ratio should not be defined before HARDFORK_BSIP_77_TIME");
127  }
128  }
129 
131  {
132  // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:
133  FC_ASSERT( !options.extensions.value.force_settle_fee_percent.valid()
134  || block_time >= HARDFORK_CORE_BSIP87_TIME,
135  "A BitAsset's FSFP cannot be set before Hardfork BSIP87" );
136  }
137 
139  const asset_claim_fees_operation& op)
140  {
141  // HF_REMOVABLE: Following hardfork check should be removable after hardfork date passes:
142  FC_ASSERT( !op.extensions.value.claim_from_asset_id.valid() ||
143  block_time >= HARDFORK_CORE_BSIP_87_74_COLLATFEE_TIME,
144  "Collateral-denominated fees are not yet active and therefore cannot be claimed." );
145  }
146 
147 } // graphene::chain::detail
148 
150 { try {
151 
152  const database& d = db();
153  const time_point_sec now = d.head_block_time();
154 
155  // Hardfork Checks:
159  if( op.bitasset_opts ) {
161  detail::check_bitasset_options_hf_bsip74( now, *op.bitasset_opts ); // HF_REMOVABLE
162  detail::check_bitasset_options_hf_bsip77( now, *op.bitasset_opts ); // HF_REMOVABLE
163  detail::check_bitasset_options_hf_bsip87( now, *op.bitasset_opts ); // HF_REMOVABLE
164  }
165 
166  // TODO move as many validations as possible to validate() if not triggered before hardfork
167  if( HARDFORK_BSIP_48_75_PASSED( now ) )
168  {
170  }
171 
175 
176  // Check that all authorities do exist
177  for( auto id : op.common_options.whitelist_authorities )
178  d.get_object(id);
179  for( auto id : op.common_options.blacklist_authorities )
180  d.get_object(id);
181 
182  auto& asset_indx = d.get_index_type<asset_index>().indices().get<by_symbol>();
183  auto asset_symbol_itr = asset_indx.find( op.symbol );
184  FC_ASSERT( asset_symbol_itr == asset_indx.end() );
185 
186  // This must remain due to "BOND.CNY" being allowed before this HF
187  if( now > HARDFORK_385_TIME )
188  {
189  auto dotpos = op.symbol.rfind( '.' );
190  if( dotpos != std::string::npos )
191  {
192  auto prefix = op.symbol.substr( 0, dotpos );
193  auto asset_symbol_itr = asset_indx.find( prefix );
194  FC_ASSERT( asset_symbol_itr != asset_indx.end(),
195  "Asset ${s} may only be created by issuer of asset ${p}, but asset ${p} has not been created",
196  ("s",op.symbol)("p",prefix) );
197  FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
198  ("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
199  }
200  }
201 
202  if( op.bitasset_opts )
203  {
204  const asset_object& backing = op.bitasset_opts->short_backing_asset(d);
205  if( backing.is_market_issued() )
206  {
207  const asset_bitasset_data_object& backing_bitasset_data = backing.bitasset_data(d);
208  const asset_object& backing_backing = backing_bitasset_data.options.short_backing_asset(d);
209  FC_ASSERT( !backing_backing.is_market_issued(),
210  "May not create a bitasset backed by a bitasset backed by a bitasset." );
211  FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing_backing.get_id() == asset_id_type(),
212  "May not create a blockchain-controlled market asset which is not backed by CORE.");
213  } else
214  FC_ASSERT( op.issuer != GRAPHENE_COMMITTEE_ACCOUNT || backing.get_id() == asset_id_type(),
215  "May not create a blockchain-controlled market asset which is not backed by CORE.");
216  FC_ASSERT( op.bitasset_opts->feed_lifetime_sec > chain_parameters.block_interval &&
217  op.bitasset_opts->force_settlement_delay_sec > chain_parameters.block_interval );
218  }
219 
220  if( op.is_prediction_market )
221  {
222  FC_ASSERT( op.bitasset_opts );
223  FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision );
224  }
225 
226  return void_result();
227 } FC_CAPTURE_AND_RETHROW( (op) ) }
228 
230 {
231  fee_is_odd = core_fee_paid.value & 1;
232  core_fee_paid -= core_fee_paid.value/2;
234 }
235 
237 { try {
238  database& d = db();
239 
240  bool hf_429 = fee_is_odd && d.head_block_time() > HARDFORK_CORE_429_TIME;
241 
242  const asset_dynamic_data_object& dyn_asset =
244  a.current_supply = 0;
245  a.fee_pool = core_fee_paid - (hf_429 ? 1 : 0);
246  });
247 
248  if( fee_is_odd && !hf_429 )
249  {
251  dd.current_supply++;
252  });
253  }
254 
255  asset_bitasset_data_id_type bit_asset_id;
256 
257  auto next_asset_id = d.get_index_type<asset_index>().get_next_id();
258 
259  if( op.bitasset_opts.valid() )
260  bit_asset_id = d.create<asset_bitasset_data_object>( [&op,next_asset_id]( asset_bitasset_data_object& a ) {
261  a.options = *op.bitasset_opts;
263  a.asset_id = next_asset_id;
264  }).id;
265 
266  const asset_object& new_asset =
267  d.create<asset_object>( [&op,next_asset_id,&dyn_asset,bit_asset_id]( asset_object& a ) {
268  a.issuer = op.issuer;
269  a.symbol = op.symbol;
270  a.precision = op.precision;
271  a.options = op.common_options;
272  if( a.options.core_exchange_rate.base.asset_id.instance.value == 0 )
273  a.options.core_exchange_rate.quote.asset_id = next_asset_id;
274  else
275  a.options.core_exchange_rate.base.asset_id = next_asset_id;
276  a.dynamic_asset_data_id = dyn_asset.id;
277  if( op.bitasset_opts.valid() )
278  a.bitasset_data_id = bit_asset_id;
279  });
280  FC_ASSERT( new_asset.id == next_asset_id, "Unexpected object database error, object id mismatch" );
281 
282  return new_asset.id;
283 } FC_CAPTURE_AND_RETHROW( (op) ) }
284 
286 { try {
287  const database& d = db();
288 
289  const asset_object& a = o.asset_to_issue.asset_id(d);
290  FC_ASSERT( o.issuer == a.issuer );
291  FC_ASSERT( !a.is_market_issued(), "Cannot manually issue a market-issued asset." );
292 
293  FC_ASSERT( !a.is_liquidity_pool_share_asset(), "Cannot manually issue a liquidity pool share asset." );
294 
295  FC_ASSERT( a.can_create_new_supply(), "Can not create new supply" );
296 
297  to_account = &o.issue_to_account(d);
298  FC_ASSERT( is_authorized_asset( d, *to_account, a ) );
299 
300  asset_dyn_data = &a.dynamic_asset_data_id(d);
301  FC_ASSERT( (asset_dyn_data->current_supply + o.asset_to_issue.amount) <= a.options.max_supply );
302 
303  return void_result();
304 } FC_CAPTURE_AND_RETHROW( (o) ) }
305 
307 { try {
308  db().adjust_balance( o.issue_to_account, o.asset_to_issue );
309 
310  db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){
312  });
313 
314  return void_result();
315 } FC_CAPTURE_AND_RETHROW( (o) ) }
316 
318 { try {
319  const database& d = db();
320 
321  const asset_object& a = o.amount_to_reserve.asset_id(d);
323  !a.is_market_issued(),
324  asset_reserve_invalid_on_mia,
325  "Cannot reserve ${sym} because it is a market-issued asset",
326  ("sym", a.symbol)
327  );
328 
329  from_account = fee_paying_account;
330  FC_ASSERT( is_authorized_asset( d, *from_account, a ) );
331 
332  asset_dyn_data = &a.dynamic_asset_data_id(d);
334  {
335  FC_ASSERT( asset_dyn_data->current_supply >= o.amount_to_reserve.amount,
336  "Can not reserve an amount that is more than the current supply" );
337  }
338  else
339  {
340  FC_ASSERT( asset_dyn_data->current_supply > o.amount_to_reserve.amount,
341  "The asset is a liquidity pool share asset thus can only reserve an amount "
342  "that is less than the current supply" );
343  }
344 
345  return void_result();
346 } FC_CAPTURE_AND_RETHROW( (o) ) }
347 
349 { try {
350  db().adjust_balance( o.payer, -o.amount_to_reserve );
351 
352  db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ){
354  });
355 
356  return void_result();
357 } FC_CAPTURE_AND_RETHROW( (o) ) }
358 
360 { try {
361  database& d = db();
362 
363  const asset_object& a = o.asset_id(d);
364 
365  asset_dyn_data = &a.dynamic_asset_data_id(d);
366 
367  return void_result();
368 } FC_CAPTURE_AND_RETHROW( (o) ) }
369 
371 { try {
372  db().adjust_balance(o.from_account, -o.amount);
373 
374  db().modify( *asset_dyn_data, [&o]( asset_dynamic_data_object& data ) {
375  data.fee_pool += o.amount;
376  });
377 
378  return void_result();
379 } FC_CAPTURE_AND_RETHROW( (o) ) }
380 
381 static void validate_new_issuer( const database& d, const asset_object& a, account_id_type new_issuer )
382 { try {
383  FC_ASSERT(d.find_object(new_issuer));
384  if( a.is_market_issued() && new_issuer == GRAPHENE_COMMITTEE_ACCOUNT )
385  {
386  const asset_object& backing = a.bitasset_data(d).options.short_backing_asset(d);
387  if( backing.is_market_issued() )
388  {
389  const asset_object& backing_backing = backing.bitasset_data(d).options.short_backing_asset(d);
390  FC_ASSERT( backing_backing.get_id() == asset_id_type(),
391  "May not create a blockchain-controlled market asset which is not backed by CORE.");
392  } else
393  FC_ASSERT( backing.get_id() == asset_id_type(),
394  "May not create a blockchain-controlled market asset which is not backed by CORE.");
395  }
396 } FC_CAPTURE_AND_RETHROW( (a)(new_issuer) ) }
397 
399 { try {
400  const database& d = db();
401  const time_point_sec now = d.head_block_time();
402 
403  // Hardfork Checks:
408 
409  bool hf_bsip_48_75_passed = ( HARDFORK_BSIP_48_75_PASSED( now ) );
410 
411  const asset_object& a = o.asset_to_update(d);
412  auto a_copy = a;
413  a_copy.options = o.new_options;
414  a_copy.validate();
415 
416  if( o.new_issuer )
417  {
418  FC_ASSERT( now < HARDFORK_CORE_199_TIME,
419  "Since Hardfork #199, updating issuer requires the use of asset_update_issuer_operation.");
420  validate_new_issuer( d, a, *o.new_issuer );
421  }
422 
423  uint16_t enabled_issuer_permissions_mask = a.options.get_enabled_issuer_permissions_mask();
424  if( hf_bsip_48_75_passed && a.is_market_issued() )
425  {
426  bitasset_data = &a.bitasset_data(d);
427  if( bitasset_data->is_prediction_market )
428  {
429  // Note: if the global_settle permission was unset, it should be corrected
430  FC_ASSERT( a_copy.can_global_settle(),
431  "The global_settle permission should be enabled for prediction markets" );
432  enabled_issuer_permissions_mask |= global_settle;
433  }
434  }
435 
436  const auto& dyn_data = a.dynamic_asset_data_id(d);
437  if( dyn_data.current_supply != 0 )
438  {
439  // new issuer_permissions must be subset of old issuer permissions
440  FC_ASSERT(!(o.new_options.get_enabled_issuer_permissions_mask() & ~enabled_issuer_permissions_mask),
441  "Cannot reinstate previously revoked issuer permissions on an asset if current supply is non-zero.");
442  // precision can not be changed
443  FC_ASSERT( !o.extensions.value.new_precision.valid(),
444  "Cannot update precision if current supply is non-zero" );
445 
446  if( hf_bsip_48_75_passed ) // TODO review after hard fork, probably can assert unconditionally
447  {
448  FC_ASSERT( dyn_data.current_supply <= o.new_options.max_supply,
449  "Max supply should not be smaller than current supply" );
450  }
451  }
452 
453  // TODO move as many validations as possible to validate() if not triggered before hardfork
454  if( hf_bsip_48_75_passed )
455  {
457  }
458 
459  // changed flags must be subset of old issuer permissions
460  if( hf_bsip_48_75_passed )
461  {
462  // Note: if an invalid bit was set, it can be unset regardless of the permissions
463  uint16_t check_bits = ( a.is_market_issued() ? VALID_FLAGS_MASK : UIA_VALID_FLAGS_MASK );
464 
465  FC_ASSERT( !((o.new_options.flags ^ a.options.flags) & check_bits & ~enabled_issuer_permissions_mask),
466  "Flag change is forbidden by issuer permissions" );
467  }
468  else
469  {
471  "Flag change is forbidden by issuer permissions" );
472  }
473 
474  asset_to_update = &a;
475  FC_ASSERT( o.issuer == a.issuer,
476  "Incorrect issuer for asset! (${o.issuer} != ${a.issuer})",
477  ("o.issuer", o.issuer)("a.issuer", a.issuer) );
478 
480  "Can not update max supply" );
481 
482  if( o.extensions.value.new_precision.valid() )
483  {
484  FC_ASSERT( *o.extensions.value.new_precision != a.precision,
485  "Specified a new precision but it does not change" );
486 
487  if( a.is_market_issued() )
488  {
489  if( !bitasset_data )
490  bitasset_data = &asset_to_update->bitasset_data(d);
491  FC_ASSERT( !bitasset_data->is_prediction_market, "Can not update precision of a prediction market" );
492  }
493 
494  // If any other asset is backed by this asset, this asset's precision can't be updated
496  .indices().get<by_short_backing_asset>();
497  auto itr = idx.lower_bound( o.asset_to_update );
498  bool backing_another_asset = ( itr != idx.end() && itr->options.short_backing_asset == o.asset_to_update );
499  FC_ASSERT( !backing_another_asset,
500  "Asset ${a} is backed by this asset, can not update precision",
501  ("a",itr->asset_id) );
502  }
503 
505 
507  for( auto id : o.new_options.whitelist_authorities )
508  d.get_object(id);
510  for( auto id : o.new_options.blacklist_authorities )
511  d.get_object(id);
512 
513  return void_result();
514 } FC_CAPTURE_AND_RETHROW((o)) }
515 
517 { try {
518  database& d = db();
519 
520  // If we are now disabling force settlements, cancel all open force settlement orders
521  if( (o.new_options.flags & disable_force_settle) && asset_to_update->can_force_settle() )
522  {
523  const auto& idx = d.get_index_type<force_settlement_index>().indices().get<by_expiration>();
524  // Funky iteration code because we're removing objects as we go. We have to re-initialize itr every loop instead
525  // of simply incrementing it.
526  for( auto itr = idx.lower_bound(o.asset_to_update);
527  itr != idx.end() && itr->settlement_asset_id() == o.asset_to_update;
528  itr = idx.lower_bound(o.asset_to_update) )
529  d.cancel_settle_order(*itr);
530  }
531 
532  // For market-issued assets, if core exchange rate changed, update flag in bitasset data
533  if( !o.extensions.value.skip_core_exchange_rate.valid() && asset_to_update->is_market_issued()
534  && asset_to_update->options.core_exchange_rate != o.new_options.core_exchange_rate )
535  {
536  const auto& bitasset = ( bitasset_data ? *bitasset_data : asset_to_update->bitasset_data(d) );
537  if( !bitasset.asset_cer_updated )
538  {
539  d.modify( bitasset, [](asset_bitasset_data_object& b)
540  {
541  b.asset_cer_updated = true;
542  });
543  }
544  }
545 
546  d.modify(*asset_to_update, [&o](asset_object& a) {
547  if( o.new_issuer )
548  a.issuer = *o.new_issuer;
549  if( o.extensions.value.new_precision.valid() )
550  a.precision = *o.extensions.value.new_precision;
551  if( o.extensions.value.skip_core_exchange_rate.valid() )
552  {
553  const auto old_cer = a.options.core_exchange_rate;
554  a.options = o.new_options;
555  a.options.core_exchange_rate = old_cer;
556  }
557  else
558  a.options = o.new_options;
559  });
560 
561  return void_result();
562 } FC_CAPTURE_AND_RETHROW( (o) ) }
563 
565 { try {
566  database& d = db();
567 
568  const asset_object& a = o.asset_to_update(d);
569 
570  validate_new_issuer( d, a, o.new_issuer );
571 
572  asset_to_update = &a;
573  FC_ASSERT( o.issuer == a.issuer,
574  "Incorrect issuer for asset! (${o.issuer} != ${a.issuer})",
575  ("o.issuer", o.issuer)("a.issuer", a.issuer) );
576 
577  return void_result();
578 } FC_CAPTURE_AND_RETHROW((o)) }
579 
581 { try {
582  database& d = db();
583  d.modify(*asset_to_update, [&](asset_object& a) {
584  a.issuer = o.new_issuer;
585  });
586 
587  return void_result();
588 } FC_CAPTURE_AND_RETHROW( (o) ) }
589 
590 /****************
591  * Loop through assets, looking for ones that are backed by the asset being changed. When found,
592  * perform checks to verify validity
593  *
594  * @param d the database
595  * @param op the bitasset update operation being performed
596  * @param new_backing_asset
597  * @param true if after hf 922/931 (if nothing triggers, this and the logic that depends on it
598  * should be removed).
599  */
601  const asset_object& new_backing_asset)
602 {
603  // no need to do these checks if the new backing asset is CORE
604  if ( new_backing_asset.get_id() == asset_id_type() )
605  return;
606 
607  // loop through all assets that have this asset as a backing asset
609  .indices()
610  .get<by_short_backing_asset>();
611  auto backed_range = idx.equal_range(op.asset_to_update);
612  std::for_each( backed_range.first, backed_range.second,
613  [&new_backing_asset, &d, &op](const asset_bitasset_data_object& bitasset_data)
614  {
615  const auto& child = bitasset_data.asset_id(d);
616  FC_ASSERT( child.get_id() != op.new_options.short_backing_asset,
617  "A BitAsset would be invalidated by changing this backing asset ('A' backed by 'B' backed by 'A')." );
618 
619  FC_ASSERT( child.issuer != GRAPHENE_COMMITTEE_ACCOUNT,
620  "A blockchain-controlled market asset would be invalidated by changing this backing asset." );
621 
622  FC_ASSERT( !new_backing_asset.is_market_issued(),
623  "A non-blockchain controlled BitAsset would be invalidated by changing this backing asset.");
624  } ); // end of lambda and std::for_each()
625 } // check_children_of_bitasset
626 
628 { try {
629  const database& d = db();
630  const time_point_sec now = d.head_block_time();
631 
632  // Hardfork Checks:
634  detail::check_bitasset_options_hf_bsip74( now, op.new_options ); // HF_REMOVABLE
635  detail::check_bitasset_options_hf_bsip77( now, op.new_options ); // HF_REMOVABLE
636  detail::check_bitasset_options_hf_bsip87( now, op.new_options ); // HF_REMOVABLE
637 
638  const asset_object& asset_obj = op.asset_to_update(d);
639 
640  FC_ASSERT( asset_obj.is_market_issued(), "Cannot update BitAsset-specific settings on a non-BitAsset." );
641 
642  FC_ASSERT( op.issuer == asset_obj.issuer, "Only asset issuer can update bitasset_data of the asset." );
643 
644  const asset_bitasset_data_object& current_bitasset_data = asset_obj.bitasset_data(d);
645 
646  FC_ASSERT( !current_bitasset_data.has_settlement(),
647  "Cannot update a bitasset after a global settlement has executed" );
648 
649  // TODO simplify code below when made sure operator==(optional,optional) works
650  if( !asset_obj.can_owner_update_mcr() )
651  {
652  // check if MCR will change
653  const auto& old_mcr = current_bitasset_data.options.extensions.value.maintenance_collateral_ratio;
654  const auto& new_mcr = op.new_options.extensions.value.maintenance_collateral_ratio;
655  bool mcr_changed = ( ( old_mcr.valid() != new_mcr.valid() )
656  || ( old_mcr.valid() && *old_mcr != *new_mcr ) );
657  FC_ASSERT( !mcr_changed, "No permission to update MCR" );
658  }
659  if( !asset_obj.can_owner_update_icr() )
660  {
661  // check if ICR will change
662  const auto& old_icr = current_bitasset_data.options.extensions.value.initial_collateral_ratio;
663  const auto& new_icr = op.new_options.extensions.value.initial_collateral_ratio;
664  bool icr_changed = ( ( old_icr.valid() != new_icr.valid() )
665  || ( old_icr.valid() && *old_icr != *new_icr ) );
666  FC_ASSERT( !icr_changed, "No permission to update ICR" );
667  }
668  if( !asset_obj.can_owner_update_mssr() )
669  {
670  // check if MSSR will change
671  const auto& old_mssr = current_bitasset_data.options.extensions.value.maximum_short_squeeze_ratio;
672  const auto& new_mssr = op.new_options.extensions.value.maximum_short_squeeze_ratio;
673  bool mssr_changed = ( ( old_mssr.valid() != new_mssr.valid() )
674  || ( old_mssr.valid() && *old_mssr != *new_mssr ) );
675  FC_ASSERT( !mssr_changed, "No permission to update MSSR" );
676  }
677 
678  // hf 922_931 is a consensus/logic change. This hf cannot be removed.
679  bool after_hf_core_922_931 = ( d.get_dynamic_global_properties().next_maintenance_time
680  > HARDFORK_CORE_922_931_TIME );
681 
682  // Are we changing the backing asset?
683  if( op.new_options.short_backing_asset != current_bitasset_data.options.short_backing_asset )
684  {
685  const asset_dynamic_data_object& dyn = asset_obj.dynamic_asset_data_id(d);
686  FC_ASSERT( dyn.current_supply == 0,
687  "Cannot update a bitasset if there is already a current supply." );
688 
690  "Must claim collateral-denominated fees before changing backing asset." );
691 
692  const asset_object& new_backing_asset = op.new_options.short_backing_asset(d); // check if the asset exists
693 
694  if( after_hf_core_922_931 )
695  {
696  FC_ASSERT( op.new_options.short_backing_asset != asset_obj.get_id(),
697  "Cannot update an asset to be backed by itself." );
698 
699  if( current_bitasset_data.is_prediction_market )
700  {
701  FC_ASSERT( asset_obj.precision == new_backing_asset.precision,
702  "The precision of the asset and backing asset must be equal." );
703  }
704 
705  if( asset_obj.issuer == GRAPHENE_COMMITTEE_ACCOUNT )
706  {
707  if( new_backing_asset.is_market_issued() )
708  {
709  FC_ASSERT( new_backing_asset.bitasset_data(d).options.short_backing_asset == asset_id_type(),
710  "May not modify a blockchain-controlled market asset to be backed by an asset which is not "
711  "backed by CORE." );
712 
713  check_children_of_bitasset( d, op, new_backing_asset );
714  }
715  else
716  {
717  FC_ASSERT( new_backing_asset.get_id() == asset_id_type(),
718  "May not modify a blockchain-controlled market asset to be backed by an asset which is not "
719  "market issued asset nor CORE." );
720  }
721  }
722  else
723  {
724  // not a committee issued asset
725 
726  // If we're changing to a backing_asset that is not CORE, we need to look at any
727  // asset ( "CHILD" ) that has this one as a backing asset. If CHILD is committee-owned,
728  // the change is not allowed. If CHILD is user-owned, then this asset's backing
729  // asset must be either CORE or a UIA.
730  if ( new_backing_asset.get_id() != asset_id_type() ) // not backed by CORE
731  {
732  check_children_of_bitasset( d, op, new_backing_asset );
733  }
734 
735  }
736 
737  // Check if the new backing asset is itself backed by something. It must be CORE or a UIA
738  if ( new_backing_asset.is_market_issued() )
739  {
740  asset_id_type backing_backing_asset_id = new_backing_asset.bitasset_data(d).options.short_backing_asset;
741  FC_ASSERT( (backing_backing_asset_id == asset_id_type() || !backing_backing_asset_id(d).is_market_issued()),
742  "A BitAsset cannot be backed by a BitAsset that itself is backed by a BitAsset.");
743  }
744  }
745  }
746 
748  if( after_hf_core_922_931 )
749  {
751  "Feed lifetime must exceed block interval." );
753  "Force settlement delay must exceed block interval." );
754  }
755 
756  bitasset_to_update = &current_bitasset_data;
757  asset_to_update = &asset_obj;
758 
759  return void_result();
760 } FC_CAPTURE_AND_RETHROW( (op) ) }
761 
762 /*******
763  * @brief Apply requested changes to bitasset options
764  *
765  * This applies the requested changes to the bitasset object. It also cleans up the
766  * releated feeds, and checks conditions that might necessitate a call to check_call_orders.
767  * Called from asset_update_bitasset_evaluator::do_apply().
768  *
769  * @param op the requested operation
770  * @param db the database
771  * @param bdo the actual database object
772  * @param asset_to_update the asset_object related to this bitasset_data_object
773  *
774  * @returns true if we should check call orders, such as if if the feed price is changed, or some
775  * cases after hf core-868-890, or if the margin_call_fee_ratio has changed, which affects the
776  * matching price of margin call orders.
777  */
778 static bool update_bitasset_object_options(
780  asset_bitasset_data_object& bdo, const asset_object& asset_to_update )
781 {
783  bool after_hf_core_868_890 = ( next_maint_time > HARDFORK_CORE_868_890_TIME );
784 
785  // If the minimum number of feeds to calculate a median has changed, we need to recalculate the median
786  bool should_update_feeds = false;
788  should_update_feeds = true;
789 
790  // after hardfork core-868-890, we also should call update_median_feeds if the feed_lifetime_sec changed
791  if( after_hf_core_868_890
793  {
794  should_update_feeds = true;
795  }
796 
797  // feeds must be reset if the backing asset is changed after hardfork core-868-890
798  bool backing_asset_changed = false;
799  bool is_witness_or_committee_fed = false;
800  if( after_hf_core_868_890
802  {
803  backing_asset_changed = true;
804  should_update_feeds = true;
805  if( asset_to_update.options.flags & ( witness_fed_asset | committee_fed_asset ) )
806  is_witness_or_committee_fed = true;
807  }
808 
809  // TODO simplify code below when made sure operator==(optional,optional) works
810  // check if ICR will change
811  if( !should_update_feeds )
812  {
813  const auto& old_icr = bdo.options.extensions.value.initial_collateral_ratio;
814  const auto& new_icr = op.new_options.extensions.value.initial_collateral_ratio;
815  bool icr_changed = ( ( old_icr.valid() != new_icr.valid() )
816  || ( old_icr.valid() && *old_icr != *new_icr ) );
817  should_update_feeds = icr_changed;
818  }
819  // check if MCR will change
820  if( !should_update_feeds )
821  {
822  const auto& old_mcr = bdo.options.extensions.value.maintenance_collateral_ratio;
823  const auto& new_mcr = op.new_options.extensions.value.maintenance_collateral_ratio;
824  bool mcr_changed = ( ( old_mcr.valid() != new_mcr.valid() )
825  || ( old_mcr.valid() && *old_mcr != *new_mcr ) );
826  should_update_feeds = mcr_changed;
827  }
828  // check if MSSR will change
829  if( !should_update_feeds )
830  {
831  const auto& old_mssr = bdo.options.extensions.value.maximum_short_squeeze_ratio;
832  const auto& new_mssr = op.new_options.extensions.value.maximum_short_squeeze_ratio;
833  bool mssr_changed = ( ( old_mssr.valid() != new_mssr.valid() )
834  || ( old_mssr.valid() && *old_mssr != *new_mssr ) );
835  should_update_feeds = mssr_changed;
836  }
837 
838  // check if MCFR will change
839  const auto& old_mcfr = bdo.options.extensions.value.margin_call_fee_ratio;
840  const auto& new_mcfr = op.new_options.extensions.value.margin_call_fee_ratio;
841  const bool mcfr_changed = ( ( old_mcfr.valid() != new_mcfr.valid() )
842  || ( old_mcfr.valid() && *old_mcfr != *new_mcfr ) );
843 
844  // Apply changes to bitasset options
845  bdo.options = op.new_options;
846 
847  // are we modifying the underlying? If so, reset the feeds
848  if( backing_asset_changed )
849  {
850  if( is_witness_or_committee_fed )
851  {
852  bdo.feeds.clear();
853  }
854  else
855  {
856  // for non-witness-feeding and non-committee-feeding assets, modify all feeds
857  // published by producers to nothing, since we can't simply remove them. For more information:
858  // https://github.com/bitshares/bitshares-core/pull/832#issuecomment-384112633
859  for( auto& current_feed : bdo.feeds )
860  {
861  current_feed.second.second.settlement_price = price();
862  }
863  }
864  }
865 
866  bool feed_actually_changed = false;
867  if( should_update_feeds )
868  {
869  const auto old_feed = bdo.current_feed;
870  bdo.update_median_feeds( db.head_block_time(), next_maint_time );
871 
872  // We need to call check_call_orders if the settlement price changes after hardfork core-868-890
873  feed_actually_changed = ( after_hf_core_868_890 && !old_feed.margin_call_params_equal( bdo.current_feed ) );
874  }
875 
876  // Conditions under which a call to check_call_orders is needed in response to the updates applied here:
877  const bool retval = feed_actually_changed || mcfr_changed;
878 
879  return retval;
880 }
881 
883 {
884  try
885  {
886  auto& db_conn = db();
887  const auto& asset_being_updated = (*asset_to_update);
888  bool to_check_call_orders = false;
889 
890  db_conn.modify( *bitasset_to_update,
891  [&op, &asset_being_updated, &to_check_call_orders, &db_conn]( asset_bitasset_data_object& bdo )
892  {
893  to_check_call_orders = update_bitasset_object_options( op, db_conn, bdo, asset_being_updated );
894  });
895 
896  if( to_check_call_orders )
897  // Process margin calls, allow black swan, not for a new limit order
898  db_conn.check_call_orders( asset_being_updated, true, false, bitasset_to_update );
899 
900  return void_result();
901 
902  } FC_CAPTURE_AND_RETHROW( (op) )
903 }
904 
906 { try {
907  database& d = db();
908 
910  "Cannot specify more feed producers than maximum allowed" );
911 
912  const asset_object& a = o.asset_to_update(d);
913 
914  FC_ASSERT(a.is_market_issued(), "Cannot update feed producers on a non-BitAsset.");
915  FC_ASSERT(!(a.options.flags & committee_fed_asset), "Cannot set feed producers on a committee-fed asset.");
916  FC_ASSERT(!(a.options.flags & witness_fed_asset), "Cannot set feed producers on a witness-fed asset.");
917 
918  FC_ASSERT( a.issuer == o.issuer, "Only asset issuer can update feed producers of an asset" );
919 
920  asset_to_update = &a;
921 
922  // Make sure all producers exist. Check these after asset because account lookup is more expensive
923  for( auto id : o.new_feed_producers )
924  d.get_object(id);
925 
926  return void_result();
927 } FC_CAPTURE_AND_RETHROW( (o) ) }
928 
930 { try {
931  database& d = db();
932  const auto head_time = d.head_block_time();
933  const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;
934  const asset_bitasset_data_object& bitasset_to_update = asset_to_update->bitasset_data(d);
935  d.modify( bitasset_to_update, [&o,head_time,next_maint_time](asset_bitasset_data_object& a) {
936  //This is tricky because I have a set of publishers coming in, but a map of publisher to feed is stored.
937  //I need to update the map such that the keys match the new publishers, but not munge the old price feeds from
938  //publishers who are being kept.
939 
940  // TODO possible performance optimization:
941  // Since both the map and the set are ordered by account already, we can iterate through them only once
942  // and avoid lookups while iterating by maintaining two iterators at same time.
943  // However, this operation is not used much, and both the set and the map are small,
944  // so likely we won't gain much with the optimization.
945 
946  //First, remove any old publishers who are no longer publishers
947  for( auto itr = a.feeds.begin(); itr != a.feeds.end(); )
948  {
949  if( !o.new_feed_producers.count(itr->first) )
950  itr = a.feeds.erase(itr);
951  else
952  ++itr;
953  }
954  //Now, add any new publishers
955  for( const account_id_type acc : o.new_feed_producers )
956  {
957  a.feeds[acc];
958  }
959  a.update_median_feeds( head_time, next_maint_time );
960  });
961  // Process margin calls, allow black swan, not for a new limit order
962  d.check_call_orders( *asset_to_update, true, false, &bitasset_to_update );
963 
964  return void_result();
965 } FC_CAPTURE_AND_RETHROW( (o) ) }
966 
968 { try {
969  const database& d = db();
970  asset_to_settle = &op.asset_to_settle(d);
971  FC_ASSERT( asset_to_settle->is_market_issued(), "Can only globally settle market-issued assets" );
972  FC_ASSERT( asset_to_settle->can_global_settle(), "The global_settle permission of this asset is disabled" );
973  FC_ASSERT( asset_to_settle->issuer == op.issuer, "Only asset issuer can globally settle an asset" );
974  FC_ASSERT( asset_to_settle->dynamic_data(d).current_supply > 0, "Can not globally settle an asset with zero supply" );
975 
976  const asset_bitasset_data_object& _bitasset_data = asset_to_settle->bitasset_data(d);
977  // if there is a settlement for this asset, then no further global settle may be taken
978  FC_ASSERT( !_bitasset_data.has_settlement(), "This asset has settlement, cannot global settle again" );
979 
980  const auto& idx = d.get_index_type<call_order_index>().indices().get<by_collateral>();
981  FC_ASSERT( !idx.empty(), "Internal error: no debt position found" );
982  auto itr = idx.lower_bound( price::min( _bitasset_data.options.short_backing_asset, op.asset_to_settle ) );
983  FC_ASSERT( itr != idx.end() && itr->debt_type() == op.asset_to_settle, "Internal error: no debt position found" );
984  const call_order_object& least_collateralized_short = *itr;
985  FC_ASSERT(least_collateralized_short.get_debt() * op.settle_price <= least_collateralized_short.get_collateral(),
986  "Cannot force settle at supplied price: least collateralized short lacks sufficient collateral to settle.");
987 
988  return void_result();
989 } FC_CAPTURE_AND_RETHROW( (op) ) }
990 
992 { try {
993  database& d = db();
994  d.globally_settle_asset( *asset_to_settle, op.settle_price );
995  return void_result();
996 } FC_CAPTURE_AND_RETHROW( (op) ) }
997 
999 { try {
1000  const database& d = db();
1001  asset_to_settle = &op.amount.asset_id(d);
1002  FC_ASSERT(asset_to_settle->is_market_issued());
1003  const auto& bitasset = asset_to_settle->bitasset_data(d);
1004  FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() );
1005  if( bitasset.is_prediction_market )
1006  FC_ASSERT( bitasset.has_settlement(), "global settlement must occur before force settling a prediction market" );
1007  else if( bitasset.current_feed.settlement_price.is_null()
1008  && ( d.head_block_time() <= HARDFORK_CORE_216_TIME // TODO check whether the HF check can be removed
1009  || !bitasset.has_settlement() ) )
1010  FC_THROW_EXCEPTION(insufficient_feeds, "Cannot force settle with no price feed.");
1011  FC_ASSERT( d.get_balance( op.account, op.amount.asset_id ) >= op.amount, "Insufficient balance" );
1012 
1013  return void_result();
1014 } FC_CAPTURE_AND_RETHROW( (op) ) }
1015 
1017 { try {
1018  database& d = db();
1019 
1020  const auto& bitasset = asset_to_settle->bitasset_data(d);
1021  if( bitasset.has_settlement() )
1022  {
1023  const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d);
1024 
1025  auto settled_amount = op.amount * bitasset.settlement_price; // round down, in favor of global settlement fund
1026  if( op.amount.amount == mia_dyn.current_supply )
1027  settled_amount.amount = bitasset.settlement_fund; // avoid rounding problems
1028  else
1029  FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund ); // should be strictly < except for PM with zero outcome
1030 
1031  if( settled_amount.amount == 0 && !bitasset.is_prediction_market )
1032  {
1033  if( d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_184_TIME )
1034  FC_THROW( "Settle amount is too small to receive anything due to rounding" );
1035  // else do nothing. Before the hf, something for nothing issue (#184, variant F) could occur
1036  }
1037 
1038  asset pays = op.amount;
1039  if( op.amount.amount != mia_dyn.current_supply
1040  && settled_amount.amount != 0
1041  && d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_342_TIME )
1042  {
1043  pays = settled_amount.multiply_and_round_up( bitasset.settlement_price );
1044  }
1045 
1046  d.adjust_balance( op.account, -pays );
1047 
1048  if( settled_amount.amount > 0 )
1049  {
1050  d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
1051  obj.settlement_fund -= settled_amount.amount;
1052  });
1053 
1054  // The account who settles pays market fees to the issuer of the collateral asset after HF core-1780
1055  //
1056  // TODO Check whether the HF check can be removed after the HF.
1057  // Note: even if logically it can be removed, perhaps the removal will lead to a small
1058  // performance loss. Needs testing.
1059  if( d.head_block_time() >= HARDFORK_CORE_1780_TIME )
1060  {
1061  auto issuer_fees = d.pay_market_fees( fee_paying_account, settled_amount.asset_id(d),
1062  settled_amount, false );
1063  settled_amount -= issuer_fees;
1064  }
1065 
1066  if( settled_amount.amount > 0 )
1067  d.adjust_balance( op.account, settled_amount );
1068  }
1069 
1070  d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
1071  obj.current_supply -= pays.amount;
1072  });
1073 
1074  return settled_amount;
1075  }
1076  else
1077  {
1078  d.adjust_balance( op.account, -op.amount );
1080  s.owner = op.account;
1081  s.balance = op.amount;
1082  s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec;
1083  }).id;
1084  }
1085 } FC_CAPTURE_AND_RETHROW( (op) ) }
1086 
1088 { try {
1089  const database& d = db();
1090  const time_point_sec now = d.head_block_time();
1091 
1092  // TODO remove check after hard fork
1094 
1095  const asset_object& base = o.asset_id(d);
1096  //Verify that this feed is for a market-issued asset and that asset is backed by the base
1097  FC_ASSERT( base.is_market_issued(), "Can only publish price feeds for market-issued assets" );
1098 
1099  const asset_bitasset_data_object& bitasset = base.bitasset_data(d);
1100  if( bitasset.is_prediction_market || now <= HARDFORK_CORE_216_TIME )
1101  {
1102  FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
1103  }
1104 
1105  // the settlement price must be quoted in terms of the backing asset
1106  FC_ASSERT( o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset,
1107  "Quote asset type in settlement price should be same as backing asset of this asset" );
1108 
1109  if( now > HARDFORK_480_TIME )
1110  {
1111  if( !o.feed.core_exchange_rate.is_null() )
1112  {
1113  FC_ASSERT( o.feed.core_exchange_rate.quote.asset_id == asset_id_type(),
1114  "Quote asset in core exchange rate should be CORE asset" );
1115  }
1116  }
1117  else
1118  {
1120  {
1121  // Old buggy code, but we have to live with it
1123  }
1124  }
1125 
1126  //Verify that the publisher is authoritative to publish a feed
1127  if( base.options.flags & witness_fed_asset )
1128  {
1129  FC_ASSERT( d.get(GRAPHENE_WITNESS_ACCOUNT).active.account_auths.count(o.publisher),
1130  "Only active witnesses are allowed to publish price feeds for this asset" );
1131  }
1132  else if( base.options.flags & committee_fed_asset )
1133  {
1134  FC_ASSERT( d.get(GRAPHENE_COMMITTEE_ACCOUNT).active.account_auths.count(o.publisher),
1135  "Only active committee members are allowed to publish price feeds for this asset" );
1136  }
1137  else
1138  {
1139  FC_ASSERT( bitasset.feeds.count(o.publisher),
1140  "The account is not in the set of allowed price feed producers of this asset" );
1141  }
1142 
1143  asset_ptr = &base;
1144  bitasset_ptr = &bitasset;
1145 
1146  return void_result();
1147 } FC_CAPTURE_AND_RETHROW((o)) }
1148 
1150 { try {
1151 
1152  database& d = db();
1153  const auto head_time = d.head_block_time();
1154  const auto next_maint_time = d.get_dynamic_global_properties().next_maintenance_time;
1155 
1156  const asset_object& base = *asset_ptr;
1157  const asset_bitasset_data_object& bad = *bitasset_ptr;
1158 
1159  auto old_feed = bad.current_feed;
1160  // Store medians for this asset
1161  d.modify( bad , [&o,head_time,next_maint_time](asset_bitasset_data_object& a) {
1162  a.feeds[o.publisher] = make_pair( head_time, price_feed_with_icr( o.feed,
1163  o.extensions.value.initial_collateral_ratio ) );
1164  a.update_median_feeds( head_time, next_maint_time );
1165  });
1166 
1167  if( !old_feed.margin_call_params_equal(bad.current_feed) )
1168  {
1169  // Check whether need to revive the asset and proceed if need
1170  if( bad.has_settlement() // has globally settled, implies head_block_time > HARDFORK_CORE_216_TIME
1171  && !bad.current_feed.settlement_price.is_null() ) // has a valid feed
1172  {
1173  bool should_revive = false;
1174  const auto& mia_dyn = base.dynamic_asset_data_id(d);
1175  if( mia_dyn.current_supply == 0 ) // if current supply is zero, revive the asset
1176  should_revive = true;
1177  else // if current supply is not zero, when collateral ratio of settlement fund is greater than MCR, revive the asset
1178  {
1179  if( next_maint_time <= HARDFORK_CORE_1270_TIME )
1180  {
1181  // before core-1270 hard fork, calculate call_price and compare to median feed
1182  if( ~price::call_price( asset(mia_dyn.current_supply, o.asset_id),
1185  should_revive = true;
1186  }
1187  else
1188  {
1189  // after core-1270 hard fork, calculate collateralization and compare to maintenance_collateralization
1191  asset( mia_dyn.current_supply, o.asset_id ) ) > bad.current_maintenance_collateralization )
1192  should_revive = true;
1193  }
1194  }
1195  if( should_revive )
1196  d.revive_bitasset(base);
1197  }
1198  // Process margin calls, allow black swan, not for a new limit order
1199  d.check_call_orders( base, true, false, bitasset_ptr );
1200  }
1201 
1202  return void_result();
1203 } FC_CAPTURE_AND_RETHROW((o)) }
1204 
1205 
1206 /***
1207  * @brief evaluator for asset_claim_fees operation
1208  *
1209  * Checks that we are able to claim fees denominated in asset Y (the amount_to_claim asset),
1210  * from some container asset X which is presumed to have accumulated the fees we wish to claim.
1211  * The container asset is either explicitly named in the extensions, or else assumed as the same
1212  * asset as the amount_to_claim asset. Evaluation fails if either (a) operation issuer is not
1213  * the same as the container_asset issuer, or (b) container_asset has no fee bucket for
1214  * amount_to_claim asset, or (c) accumulated fees are insufficient to cover amount claimed.
1215  */
1217 { try {
1218  const database& d = db();
1219 
1221 
1222  container_asset = o.extensions.value.claim_from_asset_id.valid() ?
1223  &(*o.extensions.value.claim_from_asset_id)(d) : &o.amount_to_claim.asset_id(d);
1224 
1225  FC_ASSERT( container_asset->issuer == o.issuer, "Asset fees may only be claimed by the issuer" );
1226  FC_ASSERT( container_asset->can_accumulate_fee(d,o.amount_to_claim),
1227  "Asset ${a} (${id}) is not backed by asset (${fid}) and does not hold it as fees.",
1228  ("a",container_asset->symbol)("id",container_asset->id)("fid",o.amount_to_claim.asset_id) );
1229 
1230  container_ddo = &container_asset->dynamic_asset_data_id(d);
1231 
1232  if (container_asset->get_id() == o.amount_to_claim.asset_id) {
1233  FC_ASSERT( o.amount_to_claim.amount <= container_ddo->accumulated_fees,
1234  "Attempt to claim more fees than have accumulated within asset ${a} (${id}). "
1235  "Asset DDO: ${ddo}. Fee claim: ${claim}.", ("a",container_asset->symbol)
1236  ("id",container_asset->id)("ddo",*container_ddo)("claim",o.amount_to_claim) );
1237  } else {
1238  FC_ASSERT( o.amount_to_claim.amount <= container_ddo->accumulated_collateral_fees,
1239  "Attempt to claim more backing-asset fees than have accumulated within asset ${a} (${id}) "
1240  "backed by (${fid}). Asset DDO: ${ddo}. Fee claim: ${claim}.", ("a",container_asset->symbol)
1241  ("id",container_asset->id)("fid",o.amount_to_claim.asset_id)("ddo",*container_ddo)
1242  ("claim",o.amount_to_claim) );
1243  }
1244 
1245  return void_result();
1246 } FC_CAPTURE_AND_RETHROW( (o) ) }
1247 
1248 
1249 /***
1250  * @brief apply asset_claim_fees operation
1251  */
1253 { try {
1254  database& d = db();
1255 
1256  if ( container_asset->get_id() == o.amount_to_claim.asset_id ) {
1257  d.modify( *container_ddo, [&o]( asset_dynamic_data_object& _addo ) {
1259  });
1260  } else {
1261  d.modify( *container_ddo, [&o]( asset_dynamic_data_object& _addo ) {
1263  });
1264  }
1265 
1267 
1268  return void_result();
1269 } FC_CAPTURE_AND_RETHROW( (o) ) }
1270 
1271 
1273 { try {
1274  FC_ASSERT( o.asset_id(db()).issuer == o.issuer, "Asset fee pool may only be claimed by the issuer" );
1275 
1276  return void_result();
1277 } FC_CAPTURE_AND_RETHROW( (o) ) }
1278 
1280 { try {
1281  database& d = db();
1282 
1283  const asset_object& a = o.asset_id(d);
1285  FC_ASSERT( o.amount_to_claim.amount <= addo.fee_pool, "Attempt to claim more fees than is available", ("addo",addo) );
1286 
1287  d.modify( addo, [&o]( asset_dynamic_data_object& _addo ) {
1288  _addo.fee_pool -= o.amount_to_claim.amount;
1289  });
1290 
1292 
1293  return void_result();
1294 } FC_CAPTURE_AND_RETHROW( (o) ) }
1295 
1296 
1297 } } // graphene::chain
uint8_t maximum_asset_feed_publishers
the maximum number of feed publishers for a given asset
asset_id_type asset_id
asset for which the feed is published
Definition: asset_ops.hpp:427
account_id_type issuer
must match issuer of asset from which we claim fees
Definition: asset_ops.hpp:499
uint32_t force_settlement_delay_sec
This is the delay between the time a long requests settlement and the chain evaluates the settlement...
Definition: asset_ops.hpp:130
asset_id_type asset_id
The asset this object belong to.
void modify(const T &obj, const Lambda &m)
Update the set of feed-producing accounts for a BitAssetBitAssets have price feeds selected by taking...
Definition: asset_ops.hpp:384
flat_set< account_id_type > blacklist_authorities
Definition: asset_ops.hpp:81
void check_asset_options_hf_bsip81(const fc::time_point_sec &block_time, const asset_options &options)
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
void check_bitasset_options_hf_bsip74(const fc::time_point_sec &block_time, const bitasset_options &options)
contains properties that only apply to bitassets (market issued assets)
void check_bitasset_options_hf_bsip87(const fc::time_point_sec &block_time, const bitasset_options &options)
bool is_authorized_asset(const database &d, const account_object &acct, const asset_object &asset_obj)
extension< additional_options_type > extensions
Definition: asset_ops.hpp:502
const object * find_object(object_id_type id) const
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
void_result do_evaluate(const asset_update_issuer_operation &o)
asset amount
Amount of asset to force settle. This must be a market-issued asset.
Definition: asset_ops.hpp:242
Definition: api.cpp:56
The asset_options struct contains options available on all assets in the network. ...
Definition: asset_ops.hpp:47
used to transfer accumulated fees back to the issuer&#39;s balance.
Definition: asset_ops.hpp:482
void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec &block_time, const bitasset_options &options)
tracks debt and call price information
asset get_balance(account_id_type owner, asset_id_type asset_id) const
Retrieve a particular account&#39;s balance in a given asset.
Definition: db_balance.cpp:35
bool is_prediction_market
For BitAssets, set this to true if the asset implements a Prediction Market; false otherwise...
Definition: asset_ops.hpp:181
void globally_settle_asset(const asset_object &bitasset, const price &settle_price)
Market Helpers
Definition: db_market.cpp:57
Schedules a market-issued asset for automatic settlementHolders of market-issued assests may request ...
Definition: asset_ops.hpp:226
const dynamic_global_property_object & get_dynamic_global_properties() const
Definition: db_getter.cpp:54
bool valid() const
Definition: optional.hpp:186
void_result do_evaluate(const asset_update_bitasset_operation &o)
void_result do_apply(const asset_publish_feed_operation &o)
#define FC_THROW(...)
Definition: exception.hpp:366
defines market parameters for margin positions, extended with an initial_collateral_ratio field ...
void check_asset_publish_feed_extensions_hf_bsip77(const fc::time_point_sec &block_time, const asset_publish_feed_operation::ext &extensions)
object_id_type do_apply(const asset_create_operation &o)
uint8_t maximum_asset_whitelist_authorities
maximum number of accounts which an asset may list as authorities for its whitelist OR blacklist ...
account_id_type issuer
ID of the account which issued this asset.
void_result do_evaluate(const asset_reserve_operation &o)
Publish price feeds for market-issued assetsPrice feed providers use this operation to publish their ...
Definition: asset_ops.hpp:415
fc::optional< uint16_t > initial_collateral_ratio
After BSIP77, price feed producers can feed ICR too.
Definition: asset_ops.hpp:420
uint8_t precision
Number of digits to the right of decimal point, must be less than or equal to 12. ...
Definition: asset_ops.hpp:169
asset pay_market_fees(const account_object *seller, const asset_object &recv_asset, const asset &receives, const bool &is_maker, const optional< asset > &calculated_market_fees={})
Definition: db_market.cpp:1377
bool is_null() const
Definition: asset.cpp:225
share_type accumulated_collateral_fees
accumulated collateral-denominated fees (for bitassets)
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:44
void_result do_apply(const asset_update_issuer_operation &o)
const T & get(object_id_type id) const
price min() const
Definition: asset.hpp:131
void_result do_evaluate(const asset_issue_operation &o)
void cancel_settle_order(const force_settlement_object &order, bool create_virtual_op=true)
Definition: db_market.cpp:223
string symbol
The ticker symbol of this asset.
Definition: asset_ops.hpp:167
void update_median_feeds(time_point_sec current_time, time_point_sec next_maintenance_time)
bool is_prediction_market
True if this asset implements a Prediction Market.
fc::optional< uint16_t > reward_percent
Definition: asset_ops.hpp:33
used to take an asset out of circulation, returning to the issuer
Definition: asset_ops.hpp:466
void_result do_apply(const asset_claim_pool_operation &o)
object_id_type id
Definition: object.hpp:69
uint16_t maintenance_collateral_ratio
Definition: asset.hpp:190
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:114
#define GRAPHENE_100_PERCENT
Definition: config.hpp:102
operation_result do_apply(const operation_type &op)
uint16_t get_enabled_issuer_permissions_mask() const
Definition: asset_ops.cpp:319
void_result do_apply(const asset_claim_fees_operation &o)
time_point_sec head_block_time() const
Definition: db_getter.cpp:64
uint8_t minimum_feeds
Minimum number of unexpired feeds required to extract a median feed from.
Definition: asset_ops.hpp:128
#define GRAPHENE_COMMITTEE_ACCOUNT
Definition: config.hpp:125
uint16_t issuer_permissions
The flags which the issuer has permission to update. See asset_issuer_permission_flags.
Definition: asset_ops.hpp:61
asset_dynamic_data_id_type dynamic_asset_data_id
Current supply, fee pool, and collected fees are stored in a separate object as they change frequentl...
string symbol
Ticker symbol for this asset, i.e. "USD".
tracks bitassets scheduled for force settlement at some point in the future.
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
Update options specific to BitAssetsBitAssets have some options which are not relevant to other asset...
Definition: asset_ops.hpp:353
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
account_id_type account
Account requesting the force settlement. This account pays the fee.
Definition: asset_ops.hpp:240
void_result do_apply(const asset_update_operation &o)
tracks the asset information that changes frequentlyBecause the asset_object is very large it doesn&#39;t...
uint32_t feed_lifetime_sec
Time before a price feed expires.
Definition: asset_ops.hpp:126
account_id_type issuer
Must be asset_to_issue->asset_id->issuer.
Definition: asset_ops.hpp:446
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:378
Update options common to all assetsThere are a number of options which all assets in the network use...
Definition: asset_ops.hpp:307
void_result do_evaluate(const asset_claim_pool_operation &o)
void_result do_evaluate(const operation_type &op)
account_id_type issuer
This account must sign and pay the fee for this operation. Later, this account may update the asset...
Definition: asset_ops.hpp:165
bool is_liquidity_pool_share_asset() const
Transfers BTS from the fee pool of a specified asset back to the issuer&#39;s balance.
Definition: asset_ops.hpp:554
void_result do_evaluate(const asset_update_operation &o)
void_result do_evaluate(const operation_type &op)
void_result do_evaluate(const asset_create_operation &o)
void check_asset_update_extensions_hf_bsip_48_75(const fc::time_point_sec &block_time, const asset_update_operation::ext &extensions)
void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec &block_time, const asset_claim_fees_operation &op)
share_type accumulated_fees
fees accumulate to be paid out over time
const asset_dynamic_data_object & get_core_dynamic_data() const
Definition: db_getter.cpp:39
void check_bitasset_options_hf_bsip77(const fc::time_point_sec &block_time, const bitasset_options &options)
tracks the parameters of an assetAll assets have a globally unique symbol name that controls how they...
uint8_t precision
Maximum number of digits after the decimal point (must be <= 12)
void check_asset_options_hf_bsip_48_75(const fc::time_point_sec &block_time, const asset_options &options)
the bitasset is to be fed by witnesses
Definition: types.hpp:147
const object & get(object_id_type id) const
Definition: index.hpp:111
asset_id_type asset_id
Definition: asset.hpp:39
void_result do_apply(const asset_issue_operation &o)
allow the bitasset owner to force a global settling, permission only
Definition: types.hpp:145
optional< bitasset_options > bitasset_opts
Definition: asset_ops.hpp:179
bitasset_options options
The tunable options for BitAssets are stored in this field.
void validate_flags(bool is_market_issued) const
Definition: asset_ops.cpp:301
void_result do_evaluate(const asset_claim_fees_operation &o)
asset amount_to_claim
fee.asset_id must != asset_id
Definition: asset_ops.hpp:563
void_result do_apply(const asset_reserve_operation &o)
fc::optional< uint16_t > taker_fee_percent
Definition: asset_ops.hpp:36
asset_id_type get_id() const
uint16_t flags
The currently active flags on this permission. See asset_issuer_permission_flags. ...
Definition: asset_ops.hpp:63
void_result do_apply(const asset_update_bitasset_operation &o)
flat_set< account_id_type > whitelist_authorities
Definition: asset_ops.hpp:76
void revive_bitasset(const asset_object &bitasset)
Definition: db_market.cpp:129
void_result do_evaluate(const asset_publish_feed_operation &o)
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
void_result do_apply(const operation_type &op)
Update issuer of an assetAn issuer has general administrative power of an asset and in some cases als...
Definition: asset_ops.hpp:518
void check_children_of_bitasset(const database &d, const asset_update_bitasset_operation &op, const asset_object &new_backing_asset)
void_result do_evaluate(const operation_type &o)
share_type current_supply
The number of shares currently in existence.
flat_map< account_id_type, pair< time_point_sec, price_feed_with_icr > > feeds
void check_asset_options_hf_1774(const fc::time_point_sec &block_time, const asset_options &options)
void_result do_evaluate(const asset_fund_fee_pool_operation &op)
void for_each(T &&t, const account_object &a, seq< Is... >)
Definition: database.hpp:701
bool asset_cer_updated
Track whether core_exchange_rate in corresponding asset_object has updated.
additional_asset_options_t extensions
Definition: asset_ops.hpp:93
static price call_price(const asset &debt, const asset &collateral, uint16_t collateral_ratio)
Definition: asset.cpp:212
const asset_bitasset_data_object & bitasset_data(const DB &db) const
const T & create(F &&constructor)
share_type settlement_fund
Amount of collateral which is available for force settlement.
uint8_t block_interval
interval in seconds between blocks
account_id_type issuer
must equal asset_to_settle->issuer
Definition: asset_ops.hpp:204
The bitasset_options struct contains configurable options available only to BitAssets.
Definition: asset_ops.hpp:109
const object & get_object(object_id_type id) const
const IndexType & get_index_type() const
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
allows global settling of bitassets (black swan or prediction markets)
Definition: asset_ops.hpp:199
#define GRAPHENE_WITNESS_ACCOUNT
Represents the current witnesses.
Definition: config.hpp:127
optional< account_id_type > new_issuer
If the asset is to be given a new issuer, specify his ID here.
Definition: asset_ops.hpp:331
void_result do_apply(const asset_fund_fee_pool_operation &op)
asset multiply_and_round_up(const price &p) const
Multiply and round up.
Definition: asset.cpp:78
price core_exchange_rate
Price at which automatically exchanging this asset for CORE from fee pool occurs (used for paying fee...
Definition: asset.hpp:187