Skip to content

Commit 6e532a8

Browse files
authored
Merge pull request #2176 from bitshares/pr-bsip75-bitasset-ops
Add extensions to several asset operations
2 parents 5323174 + 704cac2 commit 6e532a8

22 files changed

+2675
-146
lines changed

libraries/chain/asset_evaluator.cpp

+227-30
Large diffs are not rendered by default.

libraries/chain/asset_object.cpp

+44-29
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
4949
{
5050
bool after_core_hardfork_1270 = ( next_maintenance_time > HARDFORK_CORE_1270_TIME ); // call price caching issue
5151
current_feed_publication_time = current_time;
52-
vector<std::reference_wrapper<const price_feed>> current_feeds;
52+
vector<std::reference_wrapper<const price_feed_with_icr>> current_feeds;
5353
// find feeds that were alive at current_time
54-
for( const pair<account_id_type, pair<time_point_sec,price_feed>>& f : feeds )
54+
for( const pair<account_id_type, pair<time_point_sec,price_feed_with_icr>>& f : feeds )
5555
{
5656
if( (current_time - f.second.first).to_seconds() < options.feed_lifetime_sec &&
5757
f.second.first != time_point_sec() )
@@ -67,13 +67,11 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
6767
//... don't calculate a median, and set a null feed
6868
feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance
6969
current_feed_publication_time = current_time;
70-
current_feed = price_feed();
70+
current_feed = price_feed_with_icr();
7171
if( after_core_hardfork_1270 )
7272
{
73-
// update data derived from MCR
74-
current_maintenance_collateralization = price();
75-
// update data derived from ICR
76-
current_initial_collateralization = price();
73+
// update data derived from MCR, ICR and etc
74+
refresh_cache();
7775
}
7876
return;
7977
}
@@ -85,25 +83,40 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
8583
// Note: perhaps can defer updating current_maintenance_collateralization for better performance
8684
if( after_core_hardfork_1270 )
8785
{
88-
// update data derived from MCR
89-
current_maintenance_collateralization = current_feed.maintenance_collateralization();
90-
// update data derived from ICR
91-
refresh_current_initial_collateralization();
86+
const auto& exts = options.extensions.value;
87+
if( exts.maintenance_collateral_ratio.valid() )
88+
current_feed.maintenance_collateral_ratio = *exts.maintenance_collateral_ratio;
89+
if( exts.maximum_short_squeeze_ratio.valid() )
90+
current_feed.maximum_short_squeeze_ratio = *exts.maximum_short_squeeze_ratio;
91+
if( exts.initial_collateral_ratio.valid() )
92+
current_feed.initial_collateral_ratio = *exts.initial_collateral_ratio;
93+
// update data derived from MCR, ICR and etc
94+
refresh_cache();
9295
}
9396
return;
9497
}
9598

9699
// *** Begin Median Calculations ***
97-
price_feed median_feed;
100+
price_feed_with_icr median_feed;
98101
const auto median_itr = current_feeds.begin() + current_feeds.size() / 2;
99102
#define CALCULATE_MEDIAN_VALUE(r, data, field_name) \
100103
std::nth_element( current_feeds.begin(), median_itr, current_feeds.end(), \
101-
[](const price_feed& a, const price_feed& b) { \
104+
[](const price_feed_with_icr& a, const price_feed_with_icr& b) { \
102105
return a.field_name < b.field_name; \
103106
}); \
104107
median_feed.field_name = median_itr->get().field_name;
105108

106-
BOOST_PP_SEQ_FOR_EACH( CALCULATE_MEDIAN_VALUE, ~, GRAPHENE_PRICE_FEED_FIELDS )
109+
#define CHECK_AND_CALCULATE_MEDIAN_VALUE(r, data, field_name) \
110+
if( options.extensions.value.field_name.valid() ) { \
111+
median_feed.field_name = *options.extensions.value.field_name; \
112+
} else { \
113+
CALCULATE_MEDIAN_VALUE(r, data, field_name); \
114+
}
115+
116+
BOOST_PP_SEQ_FOR_EACH( CALCULATE_MEDIAN_VALUE, ~, (settlement_price)(core_exchange_rate) )
117+
BOOST_PP_SEQ_FOR_EACH( CHECK_AND_CALCULATE_MEDIAN_VALUE, ~,
118+
(maintenance_collateral_ratio)(maximum_short_squeeze_ratio)(initial_collateral_ratio) )
119+
#undef CHECK_AND_CALCULATE_MEDIAN_VALUE
107120
#undef CALCULATE_MEDIAN_VALUE
108121
// *** End Median Calculations ***
109122

@@ -113,25 +126,25 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
113126
// Note: perhaps can defer updating current_maintenance_collateralization for better performance
114127
if( after_core_hardfork_1270 )
115128
{
116-
// update data derived from MCR
117-
current_maintenance_collateralization = current_feed.maintenance_collateralization();
118-
// update data derived from ICR
119-
refresh_current_initial_collateralization();
129+
// update data derived from MCR, ICR and etc
130+
refresh_cache();
120131
}
121132
}
122133

123-
void asset_bitasset_data_object::refresh_current_initial_collateralization()
134+
void asset_bitasset_data_object::refresh_cache()
124135
{
125-
if( current_feed.settlement_price.is_null() )
126-
current_initial_collateralization = price();
127-
else
128-
{
129-
const auto& icr = options.extensions.value.initial_collateral_ratio;
130-
if( icr.valid() && *icr > current_feed.maintenance_collateral_ratio ) // if ICR is set and is above MCR
131-
current_initial_collateralization = current_feed.calculate_initial_collateralization( *icr );
132-
else // if ICR is not set, or not above MCR
133-
current_initial_collateralization = current_maintenance_collateralization;
134-
}
136+
current_maintenance_collateralization = current_feed.maintenance_collateralization();
137+
if( current_feed.initial_collateral_ratio > current_feed.maintenance_collateral_ratio ) // if ICR is above MCR
138+
current_initial_collateralization = current_feed.calculate_initial_collateralization();
139+
else // if ICR is not above MCR
140+
current_initial_collateralization = current_maintenance_collateralization;
141+
}
142+
143+
price price_feed_with_icr::calculate_initial_collateralization()const
144+
{
145+
if( settlement_price.is_null() )
146+
return price();
147+
return ~settlement_price * ratio_type( initial_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
135148
}
136149

137150
asset asset_object::amount_from_string(string amount_string) const
@@ -223,6 +236,8 @@ FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_bitasset_data_object, (gr
223236
(feed_cer_updated)
224237
)
225238

239+
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::price_feed_with_icr )
240+
226241
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_object )
227242
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object )
228243
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object )

libraries/chain/db_update.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ void database::update_expired_feeds()
509509
abdo.feed_cer_updated = false;
510510
}
511511
});
512-
if( !b.current_feed.settlement_price.is_null() && !( b.current_feed == old_median_feed ) ) // `==` check is safe here
512+
if( !b.current_feed.settlement_price.is_null()
513+
&& !b.current_feed.margin_call_params_equal( old_median_feed ) )
513514
{
514515
asset_ptr = &b.asset_id( *this );
515516
check_call_orders( *asset_ptr, true, false, &b );
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// hardfork check for
2+
// - BSIP 48 : new issuer permissions "lock_max_supply" and "disable_new_supply", precision update, skip cer
3+
// - BSIP 75 : asset owner set MCR, ICR and MSSR
4+
#ifndef HARDFORK_BSIP_48_75_TIME
5+
// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled
6+
#define HARDFORK_BSIP_48_75_TIME (fc::time_point_sec( 1893456000 ))
7+
#define HARDFORK_BSIP_48_75_PASSED(now) (now >= HARDFORK_BSIP_48_75_TIME)
8+
#endif

libraries/chain/include/graphene/chain/asset_evaluator.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ namespace graphene { namespace chain {
7979
void_result do_apply( const asset_update_operation& o );
8080

8181
const asset_object* asset_to_update = nullptr;
82+
const asset_bitasset_data_object* bitasset_data = nullptr;
8283
};
8384

8485
class asset_update_issuer_evaluator : public evaluator<asset_update_issuer_evaluator>

libraries/chain/include/graphene/chain/asset_object.hpp

+44-8
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ namespace graphene { namespace chain {
9999
bool is_transfer_restricted()const { return options.flags & transfer_restricted; }
100100
bool can_override()const { return options.flags & override_authority; }
101101
bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); }
102+
/// @return true if max supply of the asset can be updated
103+
bool can_update_max_supply()const { return !(options.flags & lock_max_supply); }
104+
/// @return true if can create new supply for the asset
105+
bool can_create_new_supply()const { return !(options.flags & disable_new_supply); }
106+
/// @return true if the asset owner can update MCR directly
107+
bool can_owner_update_mcr()const { return !(options.issuer_permissions & disable_mcr_update); }
108+
/// @return true if the asset owner can update ICR directly
109+
bool can_owner_update_icr()const { return !(options.issuer_permissions & disable_icr_update); }
110+
/// @return true if the asset owner can update MSSR directly
111+
bool can_owner_update_mssr()const { return !(options.issuer_permissions & disable_mssr_update); }
102112

103113
/// Helper function to get an asset object with the given amount in this asset's type
104114
asset amount(share_type a)const { return asset(a, id); }
@@ -207,6 +217,25 @@ namespace graphene { namespace chain {
207217

208218
};
209219

220+
/**
221+
* @brief defines market parameters for margin positions, extended with an initial_collateral_ratio field
222+
*/
223+
struct price_feed_with_icr : public price_feed
224+
{
225+
/// After BSIP77, when creating a new debt position or updating an existing position,
226+
/// the position will be checked against this parameter.
227+
/// Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM
228+
uint16_t initial_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;
229+
230+
price_feed_with_icr( const price_feed& pf = {}, const optional<uint16_t>& icr = {} )
231+
: price_feed( pf ), initial_collateral_ratio( icr.valid() ? *icr : pf.maintenance_collateral_ratio )
232+
{}
233+
234+
/// The result will be used to check new debt positions and position updates.
235+
/// Calculation: ~settlement_price * initial_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM
236+
price calculate_initial_collateralization()const;
237+
};
238+
210239
/**
211240
* @brief contains properties that only apply to bitassets (market issued assets)
212241
*
@@ -228,24 +257,26 @@ namespace graphene { namespace chain {
228257
/// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing
229258
/// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map
230259
/// should be treated as an implementation detail. The timestamp on each feed is the time it was published.
231-
flat_map<account_id_type, pair<time_point_sec,price_feed>> feeds;
260+
flat_map<account_id_type, pair<time_point_sec,price_feed_with_icr>> feeds;
232261
/// This is the currently active price feed, calculated as the median of values from the currently active
233262
/// feeds.
234-
price_feed current_feed;
263+
price_feed_with_icr current_feed;
235264
/// This is the publication time of the oldest feed which was factored into current_feed.
236265
time_point_sec current_feed_publication_time;
237-
/// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory.
266+
267+
/// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin
268+
/// call territory.
238269
/// This value is derived from @ref current_feed for better performance and should be kept consistent.
239270
price current_maintenance_collateralization;
240271
/// After BSIP77, when creating a new debt position or updating an existing position, the position
241272
/// will be checked against the `initial_collateral_ratio` (ICR) parameter in the bitasset options.
242-
/// This value is derived from @ref current_feed and `ICR` for better performance and should be kept
243-
/// consistent.
273+
/// This value is derived from @ref current_feed (which includes `ICR`) for better performance and
274+
/// should be kept consistent.
244275
price current_initial_collateralization;
245276

246-
/// Derive @ref current_initial_collateralization from other member variables.
247-
/// Note: this assumes @ref current_maintenance_collateralization is fresh.
248-
void refresh_current_initial_collateralization();
277+
/// Derive @ref current_maintenance_collateralization and @ref current_initial_collateralization from
278+
/// other member variables.
279+
void refresh_cache();
249280

250281
/// True if this asset implements a @ref prediction_market
251282
bool is_prediction_market = false;
@@ -373,6 +404,9 @@ MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_object)
373404
MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_dynamic_data_object)
374405
MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_bitasset_data_object)
375406

407+
FC_REFLECT_DERIVED( graphene::chain::price_feed_with_icr, (graphene::protocol::price_feed),
408+
(initial_collateral_ratio) )
409+
376410
FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
377411
(symbol)
378412
(precision)
@@ -386,6 +420,8 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
386420
FC_REFLECT_TYPENAME( graphene::chain::asset_bitasset_data_object )
387421
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_object )
388422

423+
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::price_feed_with_icr )
424+
389425
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_object )
390426
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object )
391427
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object )

libraries/chain/market_evaluator.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
189189
FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
190190
("sym", _debt_asset->symbol) );
191191

192+
FC_ASSERT( o.delta_debt.amount <= 0 || _debt_asset->can_create_new_supply(), "Can not create new supply" );
193+
192194
_dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d);
193195

194196
/***

libraries/chain/proposal_evaluator.cpp

+34-3
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@ namespace graphene { namespace chain {
3131
namespace detail {
3232
void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options);
3333

34-
void check_bitasset_options_hf_bsip74(const fc::time_point_sec& block_time,
35-
const bitasset_options& options); // HF_REMOVABLE
34+
void check_asset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const asset_options& options);
35+
void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const bitasset_options& options);
36+
void check_asset_update_extensions_hf_bsip_48_75( const fc::time_point_sec& block_time,
37+
const asset_update_operation::ext& extensions );
3638

37-
void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time,
39+
void check_asset_publish_feed_extensions_hf_bsip77( const fc::time_point_sec& block_time,
40+
const asset_publish_feed_operation::ext& extensions );
41+
void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time, const bitasset_options& options);
42+
43+
void check_bitasset_options_hf_bsip74(const fc::time_point_sec& block_time,
3844
const bitasset_options& options); // HF_REMOVABLE
3945

4046
void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options);
@@ -61,20 +67,39 @@ struct proposal_operation_hardfork_visitor
6167

6268
void operator()(const graphene::chain::asset_create_operation &v) const {
6369
detail::check_asset_options_hf_1774(block_time, v.common_options);
70+
detail::check_asset_options_hf_bsip_48_75(block_time, v.common_options);
6471
detail::check_asset_options_hf_bsip81(block_time, v.common_options);
6572
if( v.bitasset_opts.valid() ) {
73+
detail::check_bitasset_options_hf_bsip_48_75( block_time, *v.bitasset_opts );
6674
detail::check_bitasset_options_hf_bsip74( block_time, *v.bitasset_opts ); // HF_REMOVABLE
6775
detail::check_bitasset_options_hf_bsip77( block_time, *v.bitasset_opts ); // HF_REMOVABLE
6876
detail::check_bitasset_options_hf_bsip87( block_time, *v.bitasset_opts ); // HF_REMOVABLE
6977
}
78+
79+
// TODO move as many validations as possible to validate() if not triggered before hardfork
80+
if( HARDFORK_BSIP_48_75_PASSED( block_time ) )
81+
{
82+
v.common_options.validate_flags( v.bitasset_opts.valid() );
83+
}
7084
}
7185

7286
void operator()(const graphene::chain::asset_update_operation &v) const {
7387
detail::check_asset_options_hf_1774(block_time, v.new_options);
88+
detail::check_asset_options_hf_bsip_48_75(block_time, v.new_options);
7489
detail::check_asset_options_hf_bsip81(block_time, v.new_options);
90+
91+
detail::check_asset_update_extensions_hf_bsip_48_75( block_time, v.extensions.value );
92+
93+
// TODO move as many validations as possible to validate() if not triggered before hardfork
94+
if( HARDFORK_BSIP_48_75_PASSED( block_time ) )
95+
{
96+
v.new_options.validate_flags( true );
97+
}
98+
7599
}
76100

77101
void operator()(const graphene::chain::asset_update_bitasset_operation &v) const {
102+
detail::check_bitasset_options_hf_bsip_48_75( block_time, v.new_options );
78103
detail::check_bitasset_options_hf_bsip74( block_time, v.new_options ); // HF_REMOVABLE
79104
detail::check_bitasset_options_hf_bsip77( block_time, v.new_options ); // HF_REMOVABLE
80105
detail::check_bitasset_options_hf_bsip87( block_time, v.new_options ); // HF_REMOVABLE
@@ -84,6 +109,12 @@ struct proposal_operation_hardfork_visitor
84109
detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE
85110
}
86111

112+
void operator()(const graphene::chain::asset_publish_feed_operation &v) const {
113+
114+
detail::check_asset_publish_feed_extensions_hf_bsip77( block_time, v.extensions.value );
115+
116+
}
117+
87118
void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const {
88119
if (block_time < HARDFORK_CORE_1468_TIME) {
89120
FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(),

libraries/protocol/asset.cpp

-7
Original file line numberDiff line numberDiff line change
@@ -325,13 +325,6 @@ namespace graphene { namespace protocol {
325325
return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
326326
}
327327

328-
price price_feed::calculate_initial_collateralization( uint16_t initial_collateral_ratio )const
329-
{
330-
if( settlement_price.is_null() )
331-
return price();
332-
return ~settlement_price * ratio_type( initial_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
333-
}
334-
335328
// compile-time table of powers of 10 using template metaprogramming
336329

337330
template< int N >

0 commit comments

Comments
 (0)