more changes

This commit is contained in:
sierra19XX 2021-03-12 15:42:28 +11:00
parent 39e6d6ef99
commit 6c206a517d
19 changed files with 446 additions and 24 deletions

View file

@ -539,6 +539,12 @@ namespace graphene { namespace app {
break;
case impl_global_betting_statistics_object_type:
break;
case impl_collateral_bid_object_type:{
const auto& aobj = dynamic_cast<const collateral_bid_object*>(obj);
assert( aobj != nullptr );
accounts.insert( aobj->bidder );
break;
}
}
}
return result;

View file

@ -733,25 +733,49 @@ operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::
const auto& bitasset = asset_to_settle->bitasset_data(d);
if( bitasset.has_settlement() )
{
auto settled_amount = op.amount * bitasset.settlement_price;
FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund );
d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
obj.settlement_fund -= settled_amount.amount;
});
d.adjust_balance(op.account, settled_amount);
const auto& mia_dyn = asset_to_settle->dynamic_asset_data_id(d);
auto settled_amount = op.amount * bitasset.settlement_price;
if( op.amount.amount == mia_dyn.current_supply )
settled_amount.amount = bitasset.settlement_fund; // avoid rounding problems
else
FC_ASSERT( settled_amount.amount < bitasset.settlement_fund );
if( settled_amount.amount == 0 && !bitasset.is_prediction_market )
{
if( d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_184_TIME )
FC_THROW( "Settle amount is too small to receive anything due to rounding" );
else // TODO remove this warning after hard fork core-184
wlog( "Something for nothing issue (#184, variant F) occurred at block #${block}", ("block",d.head_block_num()) );
}
asset pays = op.amount;
if( op.amount.amount != mia_dyn.current_supply
&& settled_amount.amount != 0
&& d.get_dynamic_global_properties().next_maintenance_time > HARDFORK_CORE_342_TIME )
{
pays = settled_amount.multiply_and_round_up( bitasset.settlement_price );
}
d.adjust_balance( op.account, -pays );
if( settled_amount.amount > 0 )
{
d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
obj.settlement_fund -= settled_amount.amount;
});
d.adjust_balance( op.account, settled_amount );
}
d.modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
obj.current_supply -= op.amount.amount;
});
obj.current_supply -= pays.amount;
});
return settled_amount;
}
else
{
d.adjust_balance( op.account, -op.amount );
return d.create<force_settlement_object>([&](force_settlement_object& s) {
s.owner = op.account;
s.balance = op.amount;
@ -769,7 +793,10 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_
FC_ASSERT(base.is_market_issued());
const asset_bitasset_data_object& bitasset = base.bitasset_data(d);
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
if( bitasset.is_prediction_market || d.head_block_time() <= HARDFORK_CORE_216_TIME )
{
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
}
FC_ASSERT( o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset );
if( d.head_block_time() > HARDFORK_480_TIME )
@ -820,7 +847,19 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
});
if( !(old_feed == bad.current_feed) )
{
if( bad.has_settlement() ) // implies head_block_time > HARDFORK_CORE_216_TIME
{
const auto& mia_dyn = base.dynamic_asset_data_id(d);
if( !bad.current_feed.settlement_price.is_null()
&& ( mia_dyn.current_supply == 0
|| ~price::call_price(asset(mia_dyn.current_supply, o.asset_id),
asset(bad.settlement_fund, bad.options.short_backing_asset),
bad.current_feed.maintenance_collateral_ratio ) < bad.current_feed.settlement_price ) )
d.revive_bitasset(base);
}
db().check_call_orders(base);
}
return void_result();
} FC_CAPTURE_AND_RETHROW((o)) }

View file

@ -43,6 +43,7 @@ void database::debug_dump()
const auto& balance_index = db.get_index_type<account_balance_index>().indices();
const auto& statistics_index = db.get_index_type<account_stats_index>().indices();
const auto& bids = db.get_index_type<collateral_bid_index>().indices();
map<asset_id_type,share_type> total_balances;
map<asset_id_type,share_type> total_debts;
share_type core_in_orders;
@ -58,6 +59,8 @@ void database::debug_dump()
// idump(("statistics")(s));
reported_core_in_orders += s.total_core_in_orders;
}
for( const collateral_bid_object& b : bids )
total_balances[b.inv_swan_price.base.asset_id] += b.inv_swan_price.base.amount;
for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )
{
// idump(("limit_order")(o));

View file

@ -327,6 +327,7 @@ void database::initialize_evaluators()
register_evaluator<sidechain_transaction_send_evaluator>();
register_evaluator<sidechain_transaction_settle_evaluator>();
register_evaluator<random_number_store_evaluator>();
register_evaluator<bid_collateral_evaluator>();
}
void database::initialize_indexes()
@ -419,6 +420,7 @@ void database::initialize_indexes()
add_index< primary_index<nft_lottery_balance_index > >();
add_index< primary_index<son_stats_index > >();
add_index< primary_index<random_number_index > >();
add_index< primary_index<collateral_bid_index > >();
}

View file

@ -1973,6 +1973,57 @@ void update_and_match_call_orders( database& db )
wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) );
}
void database::process_bids( const asset_bitasset_data_object& bad )
{
if( bad.current_feed.settlement_price.is_null() ) return;
asset_id_type to_revive_id = (asset( 0, bad.options.short_backing_asset ) * bad.settlement_price).asset_id;
const asset_object& to_revive = to_revive_id( *this );
const asset_dynamic_data_object& bdd = to_revive.dynamic_data( *this );
const auto& bid_idx = get_index_type< collateral_bid_index >().indices().get<by_price>();
const auto start = bid_idx.lower_bound( boost::make_tuple( to_revive_id, price::max( bad.options.short_backing_asset, to_revive_id ), collateral_bid_id_type() ) );
share_type covered = 0;
auto itr = start;
while( itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == to_revive_id )
{
const collateral_bid_object& bid = *itr;
asset total_collateral = bid.inv_swan_price.quote * bad.settlement_price;
total_collateral += bid.inv_swan_price.base;
price call_price = price::call_price( bid.inv_swan_price.quote, total_collateral, bad.current_feed.maintenance_collateral_ratio );
if( ~call_price >= bad.current_feed.settlement_price ) break;
covered += bid.inv_swan_price.quote.amount;
++itr;
if( covered >= bdd.current_supply ) break;
}
if( covered < bdd.current_supply ) return;
const auto end = itr;
share_type to_cover = bdd.current_supply;
share_type remaining_fund = bad.settlement_fund;
for( itr = start; itr != end; )
{
const collateral_bid_object& bid = *itr;
++itr;
share_type debt = bid.inv_swan_price.quote.amount;
share_type collateral = (bid.inv_swan_price.quote * bad.settlement_price).amount;
if( bid.inv_swan_price.quote.amount >= to_cover )
{
debt = to_cover;
collateral = remaining_fund;
}
to_cover -= debt;
remaining_fund -= collateral;
execute_bid( bid, debt, collateral, bad.current_feed );
}
FC_ASSERT( remaining_fund == 0 );
FC_ASSERT( to_cover == 0 );
_cancel_bids_and_revive_mpa( to_revive, bad );
}
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
{ try {
const auto& gpo = get_global_properties();
@ -2244,9 +2295,12 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
update_and_match_call_orders(*this);
// Reset all BitAsset force settlement volumes to zero
//for( const asset_bitasset_data_object* d : get_index_type<asset_bitasset_data_index>() )
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() )
{
modify( d, [](asset_bitasset_data_object& o) { o.force_settled_volume = 0; });
if( d.has_settlement() )
process_bids(d);
}
// Ideally we have to do this after every block but that leads to longer block applicaiton/replay times.
// So keep it here as it is not critical. valid_to check ensures
// these custom account auths and account roles are not usable.

View file

@ -79,7 +79,7 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
wlog( "Something for nothing issue (#184, variant E) occurred at block #${block}", ("block",head_block_num()) );
}
else
pays = call_itr->get_debt() ^ settlement_price; // round up, in favor of global settlement fund
pays = call_itr->get_debt().multiply_and_round_up( settlement_price ); // round up, in favor of global settlement fund
if( pays > call_itr->get_collateral() )
pays = call_itr->get_collateral();
@ -106,6 +106,91 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
void database::revive_bitasset( const asset_object& bitasset )
{ try {
FC_ASSERT( bitasset.is_market_issued() );
const asset_bitasset_data_object& bad = bitasset.bitasset_data(*this);
FC_ASSERT( bad.has_settlement() );
const asset_dynamic_data_object& bdd = bitasset.dynamic_asset_data_id(*this);
FC_ASSERT( !bad.is_prediction_market );
FC_ASSERT( !bad.current_feed.settlement_price.is_null() );
if( bdd.current_supply > 0 )
{
// Create + execute a "bid" with 0 additional collateral
const collateral_bid_object& pseudo_bid = create<collateral_bid_object>([&](collateral_bid_object& bid) {
bid.bidder = bitasset.issuer;
bid.inv_swan_price = asset(0, bad.options.short_backing_asset)
/ asset(bdd.current_supply, bitasset.id);
});
execute_bid( pseudo_bid, bdd.current_supply, bad.settlement_fund, bad.current_feed );
} else
FC_ASSERT( bad.settlement_fund == 0 );
_cancel_bids_and_revive_mpa( bitasset, bad );
} FC_CAPTURE_AND_RETHROW( (bitasset) ) }
void database::_cancel_bids_and_revive_mpa( const asset_object& bitasset, const asset_bitasset_data_object& bad )
{ try {
FC_ASSERT( bitasset.is_market_issued() );
FC_ASSERT( bad.has_settlement() );
FC_ASSERT( !bad.is_prediction_market );
// cancel remaining bids
const auto& bid_idx = get_index_type< collateral_bid_index >().indices().get<by_price>();
auto itr = bid_idx.lower_bound( boost::make_tuple( bitasset.id, price::max( bad.options.short_backing_asset, bitasset.id ), collateral_bid_id_type() ) );
while( itr != bid_idx.end() && itr->inv_swan_price.quote.asset_id == bitasset.id )
{
const collateral_bid_object& bid = *itr;
++itr;
cancel_bid( bid );
}
// revive
modify( bad, [&]( asset_bitasset_data_object& obj ){
obj.settlement_price = price();
obj.settlement_fund = 0;
});
} FC_CAPTURE_AND_RETHROW( (bitasset) ) }
void database::cancel_bid(const collateral_bid_object& bid, bool create_virtual_op)
{
adjust_balance(bid.bidder, bid.inv_swan_price.base);
if( create_virtual_op )
{
bid_collateral_operation vop;
vop.bidder = bid.bidder;
vop.additional_collateral = bid.inv_swan_price.base;
vop.debt_covered = asset( 0, bid.inv_swan_price.quote.asset_id );
push_applied_operation( vop );
}
remove(bid);
}
void database::execute_bid( const collateral_bid_object& bid, share_type debt_covered, share_type collateral_from_fund, const price_feed& current_feed )
{
const call_order_object& call_obj = create<call_order_object>( [&](call_order_object& call ){
call.borrower = bid.bidder;
call.collateral = bid.inv_swan_price.base.amount + collateral_from_fund;
call.debt = debt_covered;
call.call_price = price::call_price(asset(debt_covered, bid.inv_swan_price.quote.asset_id),
asset(call.collateral, bid.inv_swan_price.base.asset_id),
current_feed.maintenance_collateral_ratio);
});
if( bid.inv_swan_price.base.asset_id == asset_id_type() )
modify(bid.bidder(*this).statistics(*this), [&](account_statistics_object& stats) {
stats.total_core_in_orders += call_obj.collateral;
});
push_applied_operation( execute_bid_operation( bid.bidder, asset( call_obj.collateral, bid.inv_swan_price.quote.asset_id ),
asset( debt_covered, bid.inv_swan_price.base.asset_id ) ) );
remove(bid);
}
void database::cancel_settle_order(const force_settlement_object& order, bool create_virtual_op)
{
adjust_balance(order.owner, order.balance);
@ -469,7 +554,7 @@ int database::match( const limit_order_object& usd, const limit_order_object& co
// so we should cull the order in fill_limit_order() below.
// The order would receive 0 even at `match_price`, so it would receive 0 at its own price,
// so calling maybe_cull_small() will always cull it.
core_receives = usd_receives ^ match_price;
core_receives = usd_receives.multiply_and_round_up( match_price );
cull_taker = true;
}
}
@ -486,7 +571,7 @@ int database::match( const limit_order_object& usd, const limit_order_object& co
else
// The remaining amount in order `core` would be too small,
// so the order will be culled in fill_limit_order() below
usd_receives = core_receives ^ match_price;
usd_receives = core_receives.multiply_and_round_up( match_price );
}
core_pays = usd_receives;
@ -550,7 +635,7 @@ int database::match( const limit_order_object& bid, const call_order_object& ask
// so we should cull the order in fill_limit_order() below.
// The order would receive 0 even at `match_price`, so it would receive 0 at its own price,
// so calling maybe_cull_small() will always cull it.
call_receives = order_receives ^ match_price;
call_receives = order_receives.multiply_and_round_up( match_price );
cull_taker = true;
}
}
@ -565,7 +650,7 @@ int database::match( const limit_order_object& bid, const call_order_object& ask
return 1;
}
else // has hardfork core-342
order_receives = usd_to_buy ^ match_price; // round up here, in favor of limit order
order_receives = usd_to_buy.multiply_and_round_up( match_price ); // round up here, in favor of limit order
}
call_pays = order_receives;
@ -629,7 +714,7 @@ asset database::match( const call_order_object& call,
{
if( call_receives == call_debt ) // the call order is smaller than or equal to the settle order
{
call_pays = call_receives ^ match_price; // round up here, in favor of settle order
call_pays = call_receives.multiply_and_round_up( match_price ); // round up here, in favor of settle order
// be here, we should have: call_pays <= call_collateral
}
else
@ -642,7 +727,7 @@ asset database::match( const call_order_object& call,
cull_settle_order = true;
// else do nothing, since we can't cull the settle order
call_receives = call_pays ^ match_price; // round up here to mitigate rouding issue (core-342)
call_receives = call_pays.multiply_and_round_up( match_price ); // round up here to mitigate rouding issue (core-342)
if( call_receives == settle.balance ) // the settle order will be completely filled, no need to cull
cull_settle_order = false;
@ -955,7 +1040,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
// so we should cull the order in fill_limit_order() below.
// The order would receive 0 even at `match_price`, so it would receive 0 at its own price,
// so calling maybe_cull_small() will always cull it.
call_receives = order_receives ^ match_price;
call_receives = order_receives.multiply_and_round_up( match_price );
filled_limit = true;
} else { // fill call
call_receives = usd_to_buy;
@ -968,7 +1053,7 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
wlog( "Something for nothing issue (#184, variant D) occurred at block #${block}", ("block",head_block_num()) );
}
else
order_receives = usd_to_buy ^ match_price; // round up, in favor of limit order
order_receives = usd_to_buy.multiply_and_round_up( match_price ); // round up, in favor of limit order
filled_call = true; // this is safe, since BSIP38 (hard fork core-834) depends on BSIP31 (hard fork core-343)
if( usd_to_buy == usd_for_sale )

View file

@ -431,6 +431,12 @@ struct get_impacted_account_visitor
void operator()( const random_number_store_operation& op ) {
_impacted.insert( op.account );
}
void operator()( const bid_collateral_operation& op ) {
_impacted.insert( op.bidder );
}
void operator()( const execute_bid_operation& op ) {
_impacted.insert( op.bidder );
}
};
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {

View file

@ -409,6 +409,9 @@ namespace graphene { namespace chain {
void globally_settle_asset( const asset_object& bitasset, const price& settle_price );
void cancel_settle_order(const force_settlement_object& order, bool create_virtual_op = true);
void cancel_limit_order(const limit_order_object& order, bool create_virtual_op = true, bool skip_cancel_fee = false);
void revive_bitasset( const asset_object& bitasset );
void cancel_bid(const collateral_bid_object& bid, bool create_virtual_op = true);
void execute_bid( const collateral_bid_object& bid, share_type debt_covered, share_type collateral_from_fund, const price_feed& current_feed );
/**
* @brief Process a new limit order through the markets
@ -527,6 +530,7 @@ namespace graphene { namespace chain {
private:
void _apply_block( const signed_block& next_block );
processed_transaction _apply_transaction( const signed_transaction& trx );
void _cancel_bids_and_revive_mpa( const asset_object& bitasset, const asset_bitasset_data_object& bad );
///Steps involved in applying a new block
///@{
@ -579,6 +583,7 @@ namespace graphene { namespace chain {
void update_son_statuses( const vector<son_info>& cur_active_sons, const vector<son_info>& new_active_sons );
void update_son_wallet( const vector<son_info>& new_active_sons );
void update_worker_votes();
void process_bids( const asset_bitasset_data_object& bad );
public:
double calculate_vesting_factor(const account_object& stake_account);

View file

@ -31,6 +31,7 @@ namespace graphene { namespace chain {
class asset_object;
class asset_bitasset_data_object;
class call_order_object;
struct bid_collateral_operation;
struct call_order_update_operation;
struct limit_order_cancel_operation;
struct limit_order_create_operation;
@ -88,4 +89,18 @@ namespace graphene { namespace chain {
const asset_bitasset_data_object* _bitasset_data = nullptr;
};
class bid_collateral_evaluator : public evaluator<bid_collateral_evaluator>
{
public:
typedef bid_collateral_operation operation_type;
void_result do_evaluate( const bid_collateral_operation& o );
void_result do_apply( const bid_collateral_operation& o );
const asset_object* _debt_asset = nullptr;
const asset_bitasset_data_object* _bitasset_data = nullptr;
const account_object* _paying_account = nullptr;
const collateral_bid_object* _bid = nullptr;
};
} } // graphene::chain

View file

@ -157,6 +157,27 @@ class force_settlement_object : public abstract_object<force_settlement_object>
{ return balance.asset_id; }
};
/**
* @class collateral_bid_object
* @brief bids of collateral for debt after a black swan
*
* There should only be one collateral_bid_object per asset per account, and
* only for smartcoin assets that have a global settlement_price.
*/
class collateral_bid_object : public abstract_object<collateral_bid_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_collateral_bid_object_type;
asset get_additional_collateral()const { return inv_swan_price.base; }
asset get_debt_covered()const { return inv_swan_price.quote; }
asset_id_type debt_type()const { return inv_swan_price.quote.asset_id; }
account_id_type bidder;
price inv_swan_price; // Collateral / Debt
};
struct by_collateral;
struct by_account;
struct by_price;
@ -208,8 +229,31 @@ typedef multi_index_container<
>
> force_settlement_object_multi_index_type;
typedef multi_index_container<
collateral_bid_object,
indexed_by<
ordered_unique< tag<by_id>,
member< object, object_id_type, &object::id > >,
ordered_unique< tag<by_account>,
composite_key< collateral_bid_object,
const_mem_fun< collateral_bid_object, asset_id_type, &collateral_bid_object::debt_type>,
member< collateral_bid_object, account_id_type, &collateral_bid_object::bidder>
>
>,
ordered_unique< tag<by_price>,
composite_key< collateral_bid_object,
const_mem_fun< collateral_bid_object, asset_id_type, &collateral_bid_object::debt_type>,
member< collateral_bid_object, price, &collateral_bid_object::inv_swan_price >,
member< object, object_id_type, &object::id >
>,
composite_key_compare< std::less<asset_id_type>, std::greater<price>, std::less<object_id_type> >
>
>
> collateral_bid_object_multi_index_type;
typedef generic_index<call_order_object, call_order_multi_index_type> call_order_index;
typedef generic_index<force_settlement_object, force_settlement_object_multi_index_type> force_settlement_index;
typedef generic_index<collateral_bid_object, collateral_bid_object_multi_index_type> collateral_bid_index;
} } // graphene::chain
@ -225,7 +269,10 @@ FC_REFLECT_DERIVED( graphene::chain::force_settlement_object,
(graphene::db::object),
(owner)(balance)(settlement_date)
)
FC_REFLECT_DERIVED( graphene::chain::collateral_bid_object, (graphene::db::object),
(bidder)(inv_swan_price) )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_object )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_object )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::force_settlement_object )
GRAPHENE_DECLARE_EXTERNAL_SERIALIZATION( graphene::chain::collateral_bid_object )

View file

@ -29,6 +29,8 @@ namespace graphene { namespace chain {
extern const int64_t scaled_precision_lut[];
struct price;
struct asset
{
asset( share_type a = 0, asset_id_type id = asset_id_type() )
@ -94,6 +96,8 @@ namespace graphene { namespace chain {
FC_ASSERT( precision < 19 );
return scaled_precision_lut[ precision ];
}
asset multiply_and_round_up( const price& p )const; ///< Multiply and round up
};
/**

View file

@ -172,17 +172,69 @@ namespace graphene { namespace chain {
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
};
/**
* @ingroup operations
*
* This operation can be used after a black swan to bid collateral for
* taking over part of the debt and the settlement_fund (see BSIP-0018).
*/
struct bid_collateral_operation : public base_operation
{
/** should be equivalent to call_order_update fee */
struct fee_parameters_type { uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION; };
asset fee;
account_id_type bidder; ///< pays fee and additional collateral
asset additional_collateral; ///< the amount of collateral to bid for the debt
asset debt_covered; ///< the amount of debt to take over
extensions_type extensions;
account_id_type fee_payer()const { return bidder; }
void validate()const;
};
/**
* @ingroup operations
*
* @note This is a virtual operation that is created while reviving a
* bitasset from collateral bids.
*/
struct execute_bid_operation : public base_operation
{
struct fee_parameters_type {};
execute_bid_operation(){}
execute_bid_operation( account_id_type a, asset d, asset c )
: bidder(a), debt(d), collateral(c) {}
asset fee;
account_id_type bidder;
asset debt;
asset collateral;
account_id_type fee_payer()const { return bidder; }
void validate()const { FC_ASSERT( !"virtual operation" ); }
/// This is a virtual operation; there is no fee
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
};
} } // graphene::chain
FC_REFLECT( graphene::chain::limit_order_create_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::limit_order_cancel_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::call_order_update_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::bid_collateral_operation::fee_parameters_type, (fee) )
/// THIS IS THE ONLY VIRTUAL OPERATION THUS FAR...
FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::fill_order_operation::fee_parameters_type, ) // VIRTUAL
FC_REFLECT( graphene::chain::execute_bid_operation::fee_parameters_type, ) // VIRTUAL
FC_REFLECT( graphene::chain::limit_order_create_operation,(fee)(seller)(amount_to_sell)(min_to_receive)(expiration)(fill_or_kill)(extensions))
FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order)(extensions) )
FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(extensions) )
FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives)(fill_price)(is_maker) )
FC_REFLECT( graphene::chain::bid_collateral_operation, (fee)(bidder)(additional_collateral)(debt_covered)(extensions) )
FC_REFLECT( graphene::chain::execute_bid_operation, (fee)(bidder)(debt)(collateral) )
FC_REFLECT( graphene::chain::call_order_update_operation::options_type, (target_collateral_ratio) )
@ -194,3 +246,5 @@ GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_create_ope
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::limit_order_cancel_operation )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::call_order_update_operation )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::fill_order_operation )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::bid_collateral_operation )
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::execute_bid_operation )

View file

@ -190,7 +190,9 @@ namespace graphene { namespace chain {
nft_lottery_token_purchase_operation,
nft_lottery_reward_operation,
nft_lottery_end_operation,
random_number_store_operation
random_number_store_operation,
bid_collateral_operation,
execute_bid_operation
> operation;
/// @} // operations group

View file

@ -216,7 +216,8 @@ namespace graphene { namespace chain {
impl_offer_history_object_type,
impl_son_statistics_object_type,
impl_son_schedule_object_type,
impl_nft_lottery_balance_object_type
impl_nft_lottery_balance_object_type,
impl_collateral_bid_object_type
};
//typedef fc::unsigned_int object_id_type;
@ -328,6 +329,7 @@ namespace graphene { namespace chain {
class nft_lottery_balance_object;
class son_statistics_object;
class son_schedule_object;
class collateral_bid_object;
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
@ -360,6 +362,7 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids, impl_nft_lottery_balance_object_type, nft_lottery_balance_object> nft_lottery_balance_id_type;
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
typedef object_id< implementation_ids, impl_collateral_bid_object_type, collateral_bid_object > collateral_bid_id_type;
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
typedef fc::ripemd160 block_id_type;
@ -534,6 +537,7 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_son_statistics_object_type)
(impl_son_schedule_object_type)
(impl_nft_lottery_balance_object_type)
(impl_collateral_bid_object_type)
)
FC_REFLECT_TYPENAME( graphene::chain::share_type )
@ -592,6 +596,7 @@ FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
FC_REFLECT_TYPENAME( graphene::chain::random_number_id_type )
FC_REFLECT_TYPENAME( graphene::chain::collateral_bid_id_type )
FC_REFLECT( graphene::chain::void_t, )

View file

@ -314,4 +314,61 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result bid_collateral_evaluator::do_evaluate(const bid_collateral_operation& o)
{ try {
database& d = db();
FC_ASSERT( d.head_block_time() >= HARDFORK_CORE_216_TIME, "Not yet!" );
_paying_account = &o.bidder(d);
_debt_asset = &o.debt_covered.asset_id(d);
FC_ASSERT( _debt_asset->is_market_issued(), "Unable to cover ${sym} as it is not a collateralized asset.",
("sym", _debt_asset->symbol) );
_bitasset_data = &_debt_asset->bitasset_data(d);
FC_ASSERT( _bitasset_data->has_settlement() );
FC_ASSERT( o.additional_collateral.asset_id == _bitasset_data->options.short_backing_asset );
FC_ASSERT( !_bitasset_data->is_prediction_market, "Cannot bid on a prediction market!" );
if( o.additional_collateral.amount > 0 )
{
FC_ASSERT( d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)) >= o.additional_collateral,
"Cannot bid ${c} collateral when payer only has ${b}", ("c", o.additional_collateral.amount)
("b", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );
}
const collateral_bid_index& bids = d.get_index_type<collateral_bid_index>();
const auto& index = bids.indices().get<by_account>();
const auto& bid = index.find( boost::make_tuple( o.debt_covered.asset_id, o.bidder ) );
if( bid != index.end() )
_bid = &(*bid);
else
FC_ASSERT( o.debt_covered.amount > 0, "Can't find bid to cancel?!");
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
void_result bid_collateral_evaluator::do_apply(const bid_collateral_operation& o)
{ try {
database& d = db();
if( _bid )
d.cancel_bid( *_bid, false );
if( o.debt_covered.amount == 0 ) return void_result();
d.adjust_balance( o.bidder, -o.additional_collateral );
_bid = &d.create<collateral_bid_object>([&]( collateral_bid_object& bid ) {
bid.bidder = o.bidder;
bid.inv_swan_price = o.additional_collateral / o.debt_covered;
});
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
} } // graphene::chain

View file

@ -93,6 +93,18 @@ namespace graphene { namespace chain {
FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset * price", ("asset",a)("price",b) );
}
asset asset::multiply_and_round_up( const price& b )const
{
const asset& a = *this;
if( a.asset_id == b.base.asset_id )
{
FC_ASSERT( b.base.amount.value > 0 );
FC_ASSERT( result <= GRAPHENE_MAX_SHARE_SUPPLY );
return asset( result.convert_to<int64_t>(), b.base.asset_id );
}
FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset::multiply_and_round_up(price)", ("asset",a)("price",b) );
}
price operator / ( const asset& base, const asset& quote )
{ try {
FC_ASSERT( base.asset_id != quote.asset_id );

View file

@ -46,6 +46,12 @@ void call_order_update_operation::validate()const
FC_ASSERT( delta_collateral.amount != 0 || delta_debt.amount != 0 );
} FC_CAPTURE_AND_RETHROW((*this)) }
void bid_collateral_operation::validate()const
{ try {
FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( debt_covered.amount == 0 || (debt_covered.amount > 0 && additional_collateral.amount > 0) );
} FC_CAPTURE_AND_RETHROW((*this)) }
} } // graphene::chain
GRAPHENE_EXTERNAL_SERIALIZATION( /*not extern*/, graphene::chain::limit_order_create_operation::fee_parameters_type )

View file

@ -254,6 +254,7 @@ void database_fixture::verify_asset_supplies( const database& db )
const auto& settle_index = db.get_index_type<force_settlement_index>().indices();
const auto& tournaments_index = db.get_index_type<tournament_index>().indices();
const auto& asst_index = db.get_index_type<asset_index>().indices();
const auto& bids = db.get_index_type<collateral_bid_index>().indices();
map<asset_id_type,share_type> total_balances;
map<asset_id_type,share_type> total_debts;
@ -273,6 +274,8 @@ void database_fixture::verify_asset_supplies( const database& db )
total_balances[b.asset_type] += b.balance;
for( const force_settlement_object& s : settle_index )
total_balances[s.balance.asset_id] += s.balance.amount;
for( const collateral_bid_object& b : bids )
total_balances[b.inv_swan_price.base.asset_id] += b.inv_swan_price.base.amount;
for( const account_statistics_object& a : statistics_index )
{
reported_core_in_orders += a.total_core_in_orders;
@ -1044,6 +1047,22 @@ void database_fixture::cover(const account_object& who, asset what, asset collat
verify_asset_supplies(db);
} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral) ) }
void database_fixture::bid_collateral(const account_object& who, const asset& to_bid, const asset& to_cover)
{ try {
set_expiration( db, trx );
trx.operations.clear();
bid_collateral_operation bid;
bid.bidder = who.id;
bid.additional_collateral = to_bid;
bid.debt_covered = to_cover;
trx.operations.push_back(bid);
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
trx.validate();
db.push_transaction(trx, ~0);
trx.operations.clear();
verify_asset_supplies(db);
} FC_CAPTURE_AND_RETHROW( (who.name)(to_bid)(to_cover) ) }
void database_fixture::fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount )
{
asset_fund_fee_pool_operation fund;

View file

@ -235,6 +235,7 @@ struct database_fixture {
void cover(account_id_type who, asset what, asset collateral_freed)
{ cover(who(db), what, collateral_freed); }
void cover(const account_object& who, asset what, asset collateral_freed);
void bid_collateral(const account_object& who, const asset& to_bid, const asset& to_cover);
const asset_object& get_asset( const string& symbol )const;
const account_object& get_account( const string& name )const;