Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extensions to several asset operations #2176

Merged
merged 30 commits into from
May 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
841b442
Add extensions to several asset operations
abitmore May 14, 2020
ec02a5c
Fix test cases
abitmore May 14, 2020
e698796
Add todo
abitmore May 14, 2020
95b2820
Add functions to create proposals in db fixture
abitmore May 13, 2020
7d71ee0
Add test case for bsip 48/75 hardfork protection
abitmore May 14, 2020
7330afe
Fix validation of asset flags
abitmore May 15, 2020
31746d3
Add limitations about updating asset precision
abitmore May 16, 2020
77f9ff6
Fix a bug about updating maximum supply of assets
abitmore May 16, 2020
ca12bbe
Requires global_settle permission enabled for PM
abitmore May 16, 2020
5f87be4
Remove equality function of price_feed struct
abitmore May 16, 2020
dd1f169
Add test case for PM global_settle permission bug
abitmore May 17, 2020
5796380
Update tests about price_feed equality function
abitmore May 17, 2020
7b6936d
Slightly refactor code about ICR and MCR handling
abitmore May 17, 2020
c48295d
Add test case about updating an asset's max_supply
abitmore May 17, 2020
33b1234
Add test case about lock_max_supply flag
abitmore May 17, 2020
f277306
Add function to reserve asset in db fixture
abitmore May 17, 2020
2e77e4c
Add more test cases about lock_max_supply flag
abitmore May 17, 2020
531cc2c
Add test case for disable_new_supply flag
abitmore May 17, 2020
9a5917f
Add proposal tests for the flags about supply
abitmore May 17, 2020
f61fb94
Add test case for skip_core_exchange_rate
abitmore May 17, 2020
17eb11e
Add test case about valid bits in asset flags
abitmore May 18, 2020
2dbb6bc
Add test cases about updating asset precision
abitmore May 18, 2020
910c922
Update database fixture to be able to feed ICR
abitmore May 18, 2020
534f508
Test asset creation with all permission bits
abitmore May 19, 2020
3979b2d
Add tests about asset owner's permissions ...
abitmore May 19, 2020
3fba84c
Add tests for valid range of ICR, MCR and MSSR
abitmore May 20, 2020
235b100
Add test case for feeding ICR
abitmore May 20, 2020
01c3424
Fix tests about CER feeding bug (Graphene HF480)
abitmore May 20, 2020
bfbb407
Add tests about margin calls after ratios updated
abitmore May 21, 2020
704cac2
Merge hardfork branch
abitmore May 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 227 additions & 30 deletions libraries/chain/asset_evaluator.cpp

Large diffs are not rendered by default.

73 changes: 44 additions & 29 deletions libraries/chain/asset_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
{
bool after_core_hardfork_1270 = ( next_maintenance_time > HARDFORK_CORE_1270_TIME ); // call price caching issue
current_feed_publication_time = current_time;
vector<std::reference_wrapper<const price_feed>> current_feeds;
vector<std::reference_wrapper<const price_feed_with_icr>> current_feeds;
// find feeds that were alive at current_time
for( const pair<account_id_type, pair<time_point_sec,price_feed>>& f : feeds )
for( const pair<account_id_type, pair<time_point_sec,price_feed_with_icr>>& f : feeds )
{
if( (current_time - f.second.first).to_seconds() < options.feed_lifetime_sec &&
f.second.first != time_point_sec() )
Expand All @@ -67,13 +67,11 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
//... don't calculate a median, and set a null feed
feed_cer_updated = false; // new median cer is null, won't update asset_object anyway, set to false for better performance
current_feed_publication_time = current_time;
current_feed = price_feed();
current_feed = price_feed_with_icr();
if( after_core_hardfork_1270 )
{
// update data derived from MCR
current_maintenance_collateralization = price();
// update data derived from ICR
current_initial_collateralization = price();
// update data derived from MCR, ICR and etc
refresh_cache();
}
return;
}
Expand All @@ -85,25 +83,40 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
// Note: perhaps can defer updating current_maintenance_collateralization for better performance
if( after_core_hardfork_1270 )
{
// update data derived from MCR
current_maintenance_collateralization = current_feed.maintenance_collateralization();
// update data derived from ICR
refresh_current_initial_collateralization();
const auto& exts = options.extensions.value;
if( exts.maintenance_collateral_ratio.valid() )
current_feed.maintenance_collateral_ratio = *exts.maintenance_collateral_ratio;
if( exts.maximum_short_squeeze_ratio.valid() )
current_feed.maximum_short_squeeze_ratio = *exts.maximum_short_squeeze_ratio;
if( exts.initial_collateral_ratio.valid() )
current_feed.initial_collateral_ratio = *exts.initial_collateral_ratio;
// update data derived from MCR, ICR and etc
refresh_cache();
}
return;
}

// *** Begin Median Calculations ***
price_feed median_feed;
price_feed_with_icr median_feed;
const auto median_itr = current_feeds.begin() + current_feeds.size() / 2;
#define CALCULATE_MEDIAN_VALUE(r, data, field_name) \
std::nth_element( current_feeds.begin(), median_itr, current_feeds.end(), \
[](const price_feed& a, const price_feed& b) { \
[](const price_feed_with_icr& a, const price_feed_with_icr& b) { \
return a.field_name < b.field_name; \
}); \
median_feed.field_name = median_itr->get().field_name;

BOOST_PP_SEQ_FOR_EACH( CALCULATE_MEDIAN_VALUE, ~, GRAPHENE_PRICE_FEED_FIELDS )
#define CHECK_AND_CALCULATE_MEDIAN_VALUE(r, data, field_name) \
if( options.extensions.value.field_name.valid() ) { \
median_feed.field_name = *options.extensions.value.field_name; \
} else { \
CALCULATE_MEDIAN_VALUE(r, data, field_name); \
}

BOOST_PP_SEQ_FOR_EACH( CALCULATE_MEDIAN_VALUE, ~, (settlement_price)(core_exchange_rate) )
BOOST_PP_SEQ_FOR_EACH( CHECK_AND_CALCULATE_MEDIAN_VALUE, ~,
(maintenance_collateral_ratio)(maximum_short_squeeze_ratio)(initial_collateral_ratio) )
#undef CHECK_AND_CALCULATE_MEDIAN_VALUE
#undef CALCULATE_MEDIAN_VALUE
// *** End Median Calculations ***

Expand All @@ -113,25 +126,25 @@ void graphene::chain::asset_bitasset_data_object::update_median_feeds( time_poin
// Note: perhaps can defer updating current_maintenance_collateralization for better performance
if( after_core_hardfork_1270 )
{
// update data derived from MCR
current_maintenance_collateralization = current_feed.maintenance_collateralization();
// update data derived from ICR
refresh_current_initial_collateralization();
// update data derived from MCR, ICR and etc
refresh_cache();
}
}

void asset_bitasset_data_object::refresh_current_initial_collateralization()
void asset_bitasset_data_object::refresh_cache()
{
if( current_feed.settlement_price.is_null() )
current_initial_collateralization = price();
else
{
const auto& icr = options.extensions.value.initial_collateral_ratio;
if( icr.valid() && *icr > current_feed.maintenance_collateral_ratio ) // if ICR is set and is above MCR
current_initial_collateralization = current_feed.calculate_initial_collateralization( *icr );
else // if ICR is not set, or not above MCR
current_initial_collateralization = current_maintenance_collateralization;
}
current_maintenance_collateralization = current_feed.maintenance_collateralization();
if( current_feed.initial_collateral_ratio > current_feed.maintenance_collateral_ratio ) // if ICR is above MCR
current_initial_collateralization = current_feed.calculate_initial_collateralization();
else // if ICR is not above MCR
current_initial_collateralization = current_maintenance_collateralization;
}

price price_feed_with_icr::calculate_initial_collateralization()const
{
if( settlement_price.is_null() )
return price();
return ~settlement_price * ratio_type( initial_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
}

asset asset_object::amount_from_string(string amount_string) const
Expand Down Expand Up @@ -223,6 +236,8 @@ FC_REFLECT_DERIVED_NO_TYPENAME( graphene::chain::asset_bitasset_data_object, (gr
(feed_cer_updated)
)

GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::price_feed_with_icr )

GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_object )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object )
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object )
3 changes: 2 additions & 1 deletion libraries/chain/db_update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ void database::update_expired_feeds()
abdo.feed_cer_updated = false;
}
});
if( !b.current_feed.settlement_price.is_null() && !( b.current_feed == old_median_feed ) ) // `==` check is safe here
if( !b.current_feed.settlement_price.is_null()
&& !b.current_feed.margin_call_params_equal( old_median_feed ) )
{
asset_ptr = &b.asset_id( *this );
check_call_orders( *asset_ptr, true, false, &b );
Expand Down
8 changes: 8 additions & 0 deletions libraries/chain/hardfork.d/BSIP_48_75.hf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// hardfork check for
// - BSIP 48 : new issuer permissions "lock_max_supply" and "disable_new_supply", precision update, skip cer
// - BSIP 75 : asset owner set MCR, ICR and MSSR
#ifndef HARDFORK_BSIP_48_75_TIME
// Jan 1 2030, midnight; this is a dummy date until a hardfork date is scheduled
#define HARDFORK_BSIP_48_75_TIME (fc::time_point_sec( 1893456000 ))
#define HARDFORK_BSIP_48_75_PASSED(now) (now >= HARDFORK_BSIP_48_75_TIME)
#endif
1 change: 1 addition & 0 deletions libraries/chain/include/graphene/chain/asset_evaluator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace graphene { namespace chain {
void_result do_apply( const asset_update_operation& o );

const asset_object* asset_to_update = nullptr;
const asset_bitasset_data_object* bitasset_data = nullptr;
};

class asset_update_issuer_evaluator : public evaluator<asset_update_issuer_evaluator>
Expand Down
52 changes: 44 additions & 8 deletions libraries/chain/include/graphene/chain/asset_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ namespace graphene { namespace chain {
bool is_transfer_restricted()const { return options.flags & transfer_restricted; }
bool can_override()const { return options.flags & override_authority; }
bool allow_confidential()const { return !(options.flags & asset_issuer_permission_flags::disable_confidential); }
/// @return true if max supply of the asset can be updated
bool can_update_max_supply()const { return !(options.flags & lock_max_supply); }
/// @return true if can create new supply for the asset
bool can_create_new_supply()const { return !(options.flags & disable_new_supply); }
/// @return true if the asset owner can update MCR directly
bool can_owner_update_mcr()const { return !(options.issuer_permissions & disable_mcr_update); }
/// @return true if the asset owner can update ICR directly
bool can_owner_update_icr()const { return !(options.issuer_permissions & disable_icr_update); }
/// @return true if the asset owner can update MSSR directly
bool can_owner_update_mssr()const { return !(options.issuer_permissions & disable_mssr_update); }

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

};

/**
* @brief defines market parameters for margin positions, extended with an initial_collateral_ratio field
*/
struct price_feed_with_icr : public price_feed
{
/// After BSIP77, when creating a new debt position or updating an existing position,
/// the position will be checked against this parameter.
/// Fixed point between 1.000 and 10.000, implied fixed point denominator is GRAPHENE_COLLATERAL_RATIO_DENOM
uint16_t initial_collateral_ratio = GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO;

price_feed_with_icr( const price_feed& pf = {}, const optional<uint16_t>& icr = {} )
: price_feed( pf ), initial_collateral_ratio( icr.valid() ? *icr : pf.maintenance_collateral_ratio )
{}

/// The result will be used to check new debt positions and position updates.
/// Calculation: ~settlement_price * initial_collateral_ratio / GRAPHENE_COLLATERAL_RATIO_DENOM
price calculate_initial_collateralization()const;
};

/**
* @brief contains properties that only apply to bitassets (market issued assets)
*
Expand All @@ -228,24 +257,26 @@ namespace graphene { namespace chain {
/// Feeds published for this asset. If issuer is not committee, the keys in this map are the feed publishing
/// accounts; otherwise, the feed publishers are the currently active committee_members and witnesses and this map
/// should be treated as an implementation detail. The timestamp on each feed is the time it was published.
flat_map<account_id_type, pair<time_point_sec,price_feed>> feeds;
flat_map<account_id_type, pair<time_point_sec,price_feed_with_icr>> feeds;
/// This is the currently active price feed, calculated as the median of values from the currently active
/// feeds.
price_feed current_feed;
price_feed_with_icr current_feed;
/// This is the publication time of the oldest feed which was factored into current_feed.
time_point_sec current_feed_publication_time;
/// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin call territory.

/// Call orders with collateralization (aka collateral/debt) not greater than this value are in margin
/// call territory.
/// This value is derived from @ref current_feed for better performance and should be kept consistent.
price current_maintenance_collateralization;
/// After BSIP77, when creating a new debt position or updating an existing position, the position
/// will be checked against the `initial_collateral_ratio` (ICR) parameter in the bitasset options.
/// This value is derived from @ref current_feed and `ICR` for better performance and should be kept
/// consistent.
/// This value is derived from @ref current_feed (which includes `ICR`) for better performance and
/// should be kept consistent.
price current_initial_collateralization;

/// Derive @ref current_initial_collateralization from other member variables.
/// Note: this assumes @ref current_maintenance_collateralization is fresh.
void refresh_current_initial_collateralization();
/// Derive @ref current_maintenance_collateralization and @ref current_initial_collateralization from
/// other member variables.
void refresh_cache();

/// True if this asset implements a @ref prediction_market
bool is_prediction_market = false;
Expand Down Expand Up @@ -373,6 +404,9 @@ MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_object)
MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_dynamic_data_object)
MAP_OBJECT_ID_TO_TYPE(graphene::chain::asset_bitasset_data_object)

FC_REFLECT_DERIVED( graphene::chain::price_feed_with_icr, (graphene::protocol::price_feed),
(initial_collateral_ratio) )

FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
(symbol)
(precision)
Expand All @@ -386,6 +420,8 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
FC_REFLECT_TYPENAME( graphene::chain::asset_bitasset_data_object )
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_object )

GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::price_feed_with_icr )

GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_object )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_bitasset_data_object )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::asset_dynamic_data_object )
2 changes: 2 additions & 0 deletions libraries/chain/market_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
("sym", _debt_asset->symbol) );

FC_ASSERT( o.delta_debt.amount <= 0 || _debt_asset->can_create_new_supply(), "Can not create new supply" );

_dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d);

/***
Expand Down
37 changes: 34 additions & 3 deletions libraries/chain/proposal_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,16 @@ namespace graphene { namespace chain {
namespace detail {
void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options);

void check_bitasset_options_hf_bsip74(const fc::time_point_sec& block_time,
const bitasset_options& options); // HF_REMOVABLE
void check_asset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const asset_options& options);
void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const bitasset_options& options);
void check_asset_update_extensions_hf_bsip_48_75( const fc::time_point_sec& block_time,
const asset_update_operation::ext& extensions );

void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time,
void check_asset_publish_feed_extensions_hf_bsip77( const fc::time_point_sec& block_time,
const asset_publish_feed_operation::ext& extensions );
void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time, const bitasset_options& options);

void check_bitasset_options_hf_bsip74(const fc::time_point_sec& block_time,
const bitasset_options& options); // HF_REMOVABLE

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

void operator()(const graphene::chain::asset_create_operation &v) const {
detail::check_asset_options_hf_1774(block_time, v.common_options);
detail::check_asset_options_hf_bsip_48_75(block_time, v.common_options);
detail::check_asset_options_hf_bsip81(block_time, v.common_options);
if( v.bitasset_opts.valid() ) {
detail::check_bitasset_options_hf_bsip_48_75( block_time, *v.bitasset_opts );
detail::check_bitasset_options_hf_bsip74( block_time, *v.bitasset_opts ); // HF_REMOVABLE
detail::check_bitasset_options_hf_bsip77( block_time, *v.bitasset_opts ); // HF_REMOVABLE
detail::check_bitasset_options_hf_bsip87( block_time, *v.bitasset_opts ); // HF_REMOVABLE
}

// TODO move as many validations as possible to validate() if not triggered before hardfork
if( HARDFORK_BSIP_48_75_PASSED( block_time ) )
{
v.common_options.validate_flags( v.bitasset_opts.valid() );
}
}

void operator()(const graphene::chain::asset_update_operation &v) const {
detail::check_asset_options_hf_1774(block_time, v.new_options);
detail::check_asset_options_hf_bsip_48_75(block_time, v.new_options);
detail::check_asset_options_hf_bsip81(block_time, v.new_options);

detail::check_asset_update_extensions_hf_bsip_48_75( block_time, v.extensions.value );

// TODO move as many validations as possible to validate() if not triggered before hardfork
if( HARDFORK_BSIP_48_75_PASSED( block_time ) )
{
v.new_options.validate_flags( true );
}

}

void operator()(const graphene::chain::asset_update_bitasset_operation &v) const {
detail::check_bitasset_options_hf_bsip_48_75( block_time, v.new_options );
detail::check_bitasset_options_hf_bsip74( block_time, v.new_options ); // HF_REMOVABLE
detail::check_bitasset_options_hf_bsip77( block_time, v.new_options ); // HF_REMOVABLE
detail::check_bitasset_options_hf_bsip87( block_time, v.new_options ); // HF_REMOVABLE
Expand All @@ -84,6 +109,12 @@ struct proposal_operation_hardfork_visitor
detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE
}

void operator()(const graphene::chain::asset_publish_feed_operation &v) const {

detail::check_asset_publish_feed_extensions_hf_bsip77( block_time, v.extensions.value );

}

void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const {
if (block_time < HARDFORK_CORE_1468_TIME) {
FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(),
Expand Down
7 changes: 0 additions & 7 deletions libraries/protocol/asset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,6 @@ namespace graphene { namespace protocol {
return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
}

price price_feed::calculate_initial_collateralization( uint16_t initial_collateral_ratio )const
{
if( settlement_price.is_null() )
return price();
return ~settlement_price * ratio_type( initial_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
}

// compile-time table of powers of 10 using template metaprogramming

template< int N >
Expand Down
Loading