1465 1479 1573 1669 1692 460 hf fixes
This commit is contained in:
parent
14a3e7d9e9
commit
fce9f37e9c
20 changed files with 455 additions and 22 deletions
|
|
@ -827,6 +827,9 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
|
|||
FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "",
|
||||
("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration));
|
||||
FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) );
|
||||
FC_ASSERT( head_block_time() <= HARDFORK_CORE_1573_TIME
|
||||
|| trx.get_packed_size() <= chain_parameters.maximum_transaction_size,
|
||||
"Transaction exceeds maximum transaction size." );
|
||||
}
|
||||
|
||||
//Insert transaction into unique transactions database.
|
||||
|
|
|
|||
|
|
@ -2065,6 +2065,33 @@ void update_median_feeds(database& db)
|
|||
}
|
||||
}
|
||||
|
||||
/****
|
||||
* @brief a one-time data process to correct max_supply
|
||||
*/
|
||||
void process_hf_1465( database& db )
|
||||
{
|
||||
const auto head_num = db.head_block_num();
|
||||
wlog( "Processing hard fork core-1465 at block ${n}", ("n",head_num) );
|
||||
// for each market issued asset
|
||||
const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>();
|
||||
for( auto asset_itr = asset_idx.lower_bound(true); asset_itr != asset_idx.end(); ++asset_itr )
|
||||
{
|
||||
const auto& current_asset = *asset_itr;
|
||||
graphene::chain::share_type current_supply = current_asset.dynamic_data(db).current_supply;
|
||||
graphene::chain::share_type max_supply = current_asset.options.max_supply;
|
||||
if (current_supply > max_supply && max_supply != GRAPHENE_MAX_SHARE_SUPPLY)
|
||||
{
|
||||
wlog( "Adjusting max_supply of ${asset} because current_supply (${current_supply}) is greater than ${old}.",
|
||||
("asset", current_asset.symbol)
|
||||
("current_supply", current_supply.value)
|
||||
("old", max_supply));
|
||||
db.modify<asset_object>( current_asset, [current_supply](asset_object& obj) {
|
||||
obj.options.max_supply = graphene::chain::share_type(std::min(current_supply.value, GRAPHENE_MAX_SHARE_SUPPLY));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******
|
||||
* @brief one-time data process for hard fork core-868-890
|
||||
*
|
||||
|
|
@ -2534,6 +2561,10 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_1270_TIME) && (next_maintenance_time > HARDFORK_CORE_1270_TIME) )
|
||||
to_update_and_match_call_orders_for_hf_1270 = true;
|
||||
|
||||
// make sure current_supply is less than or equal to max_supply
|
||||
if ( dgpo.next_maintenance_time <= HARDFORK_CORE_1465_TIME && next_maintenance_time > HARDFORK_CORE_1465_TIME )
|
||||
process_hf_1465(*this);
|
||||
|
||||
modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) {
|
||||
d.next_maintenance_time = next_maintenance_time;
|
||||
d.accounts_registered_this_interval = 0;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,26 @@ namespace graphene { namespace chain {
|
|||
* No more asset updates may be issued.
|
||||
*/
|
||||
void database::globally_settle_asset( const asset_object& mia, const price& settlement_price )
|
||||
{
|
||||
auto maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
bool before_core_hardfork_1669 = ( maint_time <= HARDFORK_CORE_1669_TIME ); // whether to use call_price
|
||||
|
||||
if( before_core_hardfork_1669 )
|
||||
{
|
||||
globally_settle_asset_impl( mia, settlement_price,
|
||||
get_index_type<call_order_index>().indices().get<by_price>() );
|
||||
}
|
||||
else
|
||||
{
|
||||
globally_settle_asset_impl( mia, settlement_price,
|
||||
get_index_type<call_order_index>().indices().get<by_collateral>() );
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IndexType>
|
||||
void database::globally_settle_asset_impl( const asset_object& mia,
|
||||
const price& settlement_price,
|
||||
const IndexType& call_index )
|
||||
{ try {
|
||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||
FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" );
|
||||
|
|
@ -52,34 +72,33 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
const asset_dynamic_data_object& mia_dyn = mia.dynamic_asset_data_id(*this);
|
||||
auto original_mia_supply = mia_dyn.current_supply;
|
||||
|
||||
const auto& call_price_index = get_index_type<call_order_index>().indices().get<by_price>();
|
||||
|
||||
auto maint_time = get_dynamic_global_properties().next_maintenance_time;
|
||||
bool before_core_hardfork_342 = ( maint_time <= HARDFORK_CORE_342_TIME ); // better rounding
|
||||
|
||||
// cancel all call orders and accumulate it into collateral_gathered
|
||||
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_itr = call_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_end = call_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
||||
asset pays;
|
||||
while( call_itr != call_end )
|
||||
{
|
||||
const call_order_object& order = *call_itr;
|
||||
++call_itr;
|
||||
if( before_core_hardfork_342 )
|
||||
{
|
||||
pays = call_itr->get_debt() * settlement_price; // round down, in favor of call order
|
||||
pays = order.get_debt() * settlement_price; // round down, in favor of call order
|
||||
|
||||
// Be here, the call order can be paying nothing
|
||||
if( pays.amount == 0 && !bitasset.is_prediction_market ) // TODO remove this warning after hard fork core-342
|
||||
wlog( "Something for nothing issue (#184, variant E) occurred at block #${block}", ("block",head_block_num()) );
|
||||
wlog( "Something for nothing issue (#184, variant E) occurred at block #${block}",
|
||||
("block",head_block_num()) );
|
||||
}
|
||||
else
|
||||
pays = call_itr->get_debt().multiply_and_round_up( settlement_price ); // round up, in favor of global settlement fund
|
||||
pays = order.get_debt().multiply_and_round_up( settlement_price ); // round up in favor of global-settle fund
|
||||
|
||||
if( pays > call_itr->get_collateral() )
|
||||
pays = call_itr->get_collateral();
|
||||
if( pays > order.get_collateral() )
|
||||
pays = order.get_collateral();
|
||||
|
||||
collateral_gathered += pays;
|
||||
const auto& order = *call_itr;
|
||||
++call_itr;
|
||||
FC_ASSERT( fill_call_order( order, pays, order.get_debt(), settlement_price, true ) ); // call order is maker
|
||||
}
|
||||
|
||||
|
|
@ -962,6 +981,14 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
|||
|
||||
const asset_bitasset_data_object& bitasset = ( bitasset_ptr ? *bitasset_ptr : mia.bitasset_data(*this) );
|
||||
|
||||
// price feeds can cause black swans in prediction markets
|
||||
// The hardfork check may be able to be removed after the hardfork date
|
||||
// if check_for_blackswan never triggered a black swan on a prediction market.
|
||||
// NOTE: check_for_blackswan returning true does not always mean a black
|
||||
// swan was triggered.
|
||||
if ( maint_time >= HARDFORK_CORE_460_TIME && bitasset.is_prediction_market )
|
||||
return false;
|
||||
|
||||
if( check_for_blackswan( mia, enable_black_swan, &bitasset ) )
|
||||
return false;
|
||||
|
||||
|
|
|
|||
4
libraries/chain/hardfork.d/CORE_1465.hf
Normal file
4
libraries/chain/hardfork.d/CORE_1465.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #1465 check max_supply before processing call_order_update
|
||||
#ifndef HARDFORK_CORE_1465_TIME
|
||||
#define HARDFORK_CORE_1465_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_1479.hf
Normal file
4
libraries/chain/hardfork.d/CORE_1479.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #1479 nodes crashing on self-approving proposal
|
||||
#ifndef HARDFORK_CORE_1479_TIME
|
||||
#define HARDFORK_CORE_1479_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_1573.hf
Normal file
4
libraries/chain/hardfork.d/CORE_1573.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #1573 check transaction size
|
||||
#ifndef HARDFORK_CORE_1573_TIME
|
||||
#define HARDFORK_CORE_1573_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_1669.hf
Normal file
4
libraries/chain/hardfork.d/CORE_1669.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #1669 Stop using call_price when globally settling
|
||||
#ifndef HARDFORK_CORE_1669_TIME
|
||||
#define HARDFORK_CORE_1669_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_1692.hf
Normal file
4
libraries/chain/hardfork.d/CORE_1692.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #1692 validation check of bid_collateral
|
||||
#ifndef HARDFORK_CORE_1692_TIME
|
||||
#define HARDFORK_CORE_1692_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
4
libraries/chain/hardfork.d/CORE_460.hf
Normal file
4
libraries/chain/hardfork.d/CORE_460.hf
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// bitshares-core issue #460 Prediction Market price feed should not cause black swan
|
||||
#ifndef HARDFORK_CORE_460_TIME
|
||||
#define HARDFORK_CORE_460_TIME (fc::time_point_sec( 1615334400 )) // Wednesday, 10 March 2021 00:00:00 UTC
|
||||
#endif
|
||||
|
|
@ -413,6 +413,14 @@ namespace graphene { namespace chain {
|
|||
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 );
|
||||
|
||||
private:
|
||||
template<typename IndexType>
|
||||
void globally_settle_asset_impl( const asset_object& bitasset,
|
||||
const price& settle_price,
|
||||
const IndexType& call_index );
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Process a new limit order through the markets
|
||||
* @param order The new order to process
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ namespace graphene { namespace chain {
|
|||
const account_object* _paying_account = nullptr;
|
||||
const call_order_object* _order = nullptr;
|
||||
const asset_bitasset_data_object* _bitasset_data = nullptr;
|
||||
const asset_dynamic_data_object* _dynamic_data_obj = nullptr;
|
||||
};
|
||||
|
||||
class bid_collateral_evaluator : public evaluator<bid_collateral_evaluator>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,25 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class hardfork_visitor_1479
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
|
||||
uint64_t max_update_instance = 0;
|
||||
uint64_t nested_update_count = 0;
|
||||
|
||||
template<typename T>
|
||||
void operator()(const T &v) const {}
|
||||
|
||||
void operator()(const proposal_update_operation &v);
|
||||
|
||||
void operator()(const proposal_delete_operation &v);
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const graphene::chain::proposal_create_operation &v);
|
||||
};
|
||||
|
||||
class son_hardfork_visitor
|
||||
{
|
||||
public:
|
||||
|
|
@ -55,6 +74,7 @@ namespace graphene { namespace chain {
|
|||
object_id_type do_apply( const proposal_create_operation& o );
|
||||
|
||||
transaction _proposed_trx;
|
||||
hardfork_visitor_1479 vtor_1479;
|
||||
};
|
||||
|
||||
class proposal_update_evaluator : public evaluator<proposal_update_evaluator>
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ namespace graphene { namespace chain {
|
|||
flat_set<account_id_type>& owner,
|
||||
vector<authority>& other,
|
||||
bool ignore_custom_operation_required_auths )const;
|
||||
virtual uint64_t get_packed_size()const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -158,8 +158,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
|
|||
{ try {
|
||||
database& d = db();
|
||||
|
||||
auto next_maintenance_time = d.get_dynamic_global_properties().next_maintenance_time;
|
||||
|
||||
// TODO: remove this check and the assertion after hf_834
|
||||
if( d.get_dynamic_global_properties().next_maintenance_time <= HARDFORK_CORE_834_TIME )
|
||||
if( next_maintenance_time <= HARDFORK_CORE_834_TIME )
|
||||
FC_ASSERT( !o.extensions.value.target_collateral_ratio.valid(),
|
||||
"Can not set target_collateral_ratio in call_order_update_operation before hardfork 834." );
|
||||
|
||||
|
|
@ -168,6 +170,14 @@ 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) );
|
||||
|
||||
_dynamic_data_obj = &_debt_asset->dynamic_asset_data_id(d);
|
||||
FC_ASSERT( next_maintenance_time <= HARDFORK_CORE_1465_TIME
|
||||
|| _dynamic_data_obj->current_supply + o.delta_debt.amount <= _debt_asset->options.max_supply,
|
||||
"Borrowing this quantity would exceed MAX_SUPPLY" );
|
||||
|
||||
FC_ASSERT( _dynamic_data_obj->current_supply + o.delta_debt.amount >= 0,
|
||||
"This transaction would bring current supply below zero.");
|
||||
|
||||
_bitasset_data = &_debt_asset->bitasset_data(d);
|
||||
|
||||
/// if there is a settlement for this asset, then no further margin positions may be taken and
|
||||
|
|
@ -208,7 +218,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
d.adjust_balance( o.funding_account, o.delta_debt );
|
||||
|
||||
// Deduct the debt paid from the total supply of the debt asset.
|
||||
d.modify(_debt_asset->dynamic_asset_data_id(d), [&](asset_dynamic_data_object& dynamic_asset) {
|
||||
d.modify(*_dynamic_data_obj, [&](asset_dynamic_data_object& dynamic_asset) {
|
||||
dynamic_asset.current_supply += o.delta_debt.amount;
|
||||
assert(dynamic_asset.current_supply >= 0);
|
||||
});
|
||||
|
|
@ -366,13 +376,6 @@ void_result bid_collateral_evaluator::do_evaluate(const bid_collateral_operation
|
|||
|
||||
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 ) );
|
||||
|
|
@ -381,6 +384,22 @@ void_result bid_collateral_evaluator::do_evaluate(const bid_collateral_operation
|
|||
else
|
||||
FC_ASSERT( o.debt_covered.amount > 0, "Can't find bid to cancel?!");
|
||||
|
||||
if( o.additional_collateral.amount > 0 )
|
||||
{
|
||||
if( _bid && d.head_block_time() >= HARDFORK_CORE_1692_TIME ) // TODO: see if HF check can be removed after HF
|
||||
{
|
||||
asset delta = o.additional_collateral - _bid->get_additional_collateral();
|
||||
FC_ASSERT( d.get_balance(*_paying_account, _bitasset_data->options.short_backing_asset(d)) >= delta,
|
||||
"Cannot increase bid from ${oc} to ${nc} collateral when payer only has ${b}",
|
||||
("oc", _bid->get_additional_collateral().amount)("nc", o.additional_collateral.amount)
|
||||
("b", d.get_balance(*_paying_account, o.additional_collateral.asset_id(d)).amount) );
|
||||
} else
|
||||
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) );
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -280,6 +280,27 @@ void son_hardfork_visitor::operator()( const son_report_down_operation &v )
|
|||
});
|
||||
}
|
||||
|
||||
void hardfork_visitor_1479::operator()(const proposal_update_operation &v)
|
||||
{
|
||||
if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance )
|
||||
max_update_instance = v.proposal.instance.value;
|
||||
nested_update_count++;
|
||||
}
|
||||
|
||||
void hardfork_visitor_1479::operator()(const proposal_delete_operation &v)
|
||||
{
|
||||
if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance )
|
||||
max_update_instance = v.proposal.instance.value;
|
||||
nested_update_count++;
|
||||
}
|
||||
|
||||
// loop and self visit in proposals
|
||||
void hardfork_visitor_1479::operator()(const graphene::chain::proposal_create_operation &v)
|
||||
{
|
||||
for (const op_wrapper &op : v.proposed_ops)
|
||||
op.op.visit(*this);
|
||||
}
|
||||
|
||||
void_result proposal_create_evaluator::do_evaluate( const proposal_create_operation& o )
|
||||
{ try {
|
||||
const database& d = db();
|
||||
|
|
@ -287,6 +308,7 @@ void_result proposal_create_evaluator::do_evaluate( const proposal_create_operat
|
|||
|
||||
proposal_operation_hardfork_visitor vtor( block_time );
|
||||
vtor( o );
|
||||
vtor_1479( o );
|
||||
|
||||
const auto& global_parameters = d.get_global_properties().parameters;
|
||||
|
||||
|
|
@ -338,7 +360,7 @@ object_id_type proposal_create_evaluator::do_apply( const proposal_create_operat
|
|||
database& d = db();
|
||||
auto chain_time = d.head_block_time();
|
||||
|
||||
const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time](proposal_object& proposal) {
|
||||
const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time, &d](proposal_object& proposal) {
|
||||
_proposed_trx.expiration = o.expiration_time;
|
||||
proposal.proposed_transaction = _proposed_trx;
|
||||
proposal.proposer = o.fee_paying_account;
|
||||
|
|
@ -360,6 +382,20 @@ object_id_type proposal_create_evaluator::do_apply( const proposal_create_operat
|
|||
std::set_difference(required_active.begin(), required_active.end(),
|
||||
proposal.required_owner_approvals.begin(), proposal.required_owner_approvals.end(),
|
||||
std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin()));
|
||||
|
||||
if( d.head_block_time() > HARDFORK_CORE_1479_TIME )
|
||||
FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance,
|
||||
"Cannot update/delete a proposal with a future id!" );
|
||||
else if( vtor_1479.nested_update_count > 0 && proposal.id.instance() <= vtor_1479.max_update_instance )
|
||||
{
|
||||
// prevent approval
|
||||
transfer_operation top;
|
||||
top.from = GRAPHENE_NULL_ACCOUNT;
|
||||
top.to = GRAPHENE_RELAXED_COMMITTEE_ACCOUNT;
|
||||
top.amount = asset( GRAPHENE_MAX_SHARE_SUPPLY );
|
||||
proposal.proposed_transaction.operations.emplace_back( top );
|
||||
wlog( "Issue 1479: ${p}", ("p",proposal) );
|
||||
}
|
||||
});
|
||||
|
||||
son_hardfork_visitor son_vtor(d, proposal.id);
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@ void transaction::validate() const
|
|||
operation_validate(op);
|
||||
}
|
||||
|
||||
uint64_t transaction::get_packed_size() const
|
||||
{
|
||||
return fc::raw::pack_size(*this);
|
||||
}
|
||||
|
||||
graphene::chain::transaction_id_type graphene::chain::transaction::id() const
|
||||
{
|
||||
auto h = digest();
|
||||
|
|
|
|||
|
|
@ -610,7 +610,7 @@ const asset_object& database_fixture::create_bitasset(
|
|||
creator.issuer = issuer;
|
||||
creator.fee = asset();
|
||||
creator.symbol = name;
|
||||
creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY / 2;
|
||||
creator.precision = 2;
|
||||
creator.common_options.market_fee_percent = market_fee_percent;
|
||||
if( issuer == GRAPHENE_WITNESS_ACCOUNT )
|
||||
|
|
|
|||
|
|
@ -1381,4 +1381,70 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( self_approving_proposal )
|
||||
{ try {
|
||||
ACTORS( (alice) );
|
||||
fund( alice );
|
||||
|
||||
generate_blocks( HARDFORK_CORE_1479_TIME );
|
||||
trx.clear();
|
||||
set_expiration( db, trx );
|
||||
|
||||
proposal_update_operation pup;
|
||||
pup.fee_paying_account = alice_id;
|
||||
pup.proposal = proposal_id_type(0);
|
||||
pup.active_approvals_to_add.insert( alice_id );
|
||||
|
||||
proposal_create_operation pop;
|
||||
pop.proposed_ops.emplace_back(pup);
|
||||
pop.fee_paying_account = alice_id;
|
||||
pop.expiration_time = db.head_block_time() + fc::days(1);
|
||||
trx.operations.push_back(pop);
|
||||
const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get<object_id_type>();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL( 0, pid1.instance.value );
|
||||
db.get<proposal_object>(pid1);
|
||||
|
||||
trx.operations.push_back(pup);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
// Proposal failed and still exists
|
||||
db.get<proposal_object>(pid1);
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_CASE( self_deleting_proposal )
|
||||
{ try {
|
||||
ACTORS( (alice) );
|
||||
fund( alice );
|
||||
|
||||
generate_blocks( HARDFORK_CORE_1479_TIME );
|
||||
trx.clear();
|
||||
set_expiration( db, trx );
|
||||
|
||||
proposal_delete_operation pdo;
|
||||
pdo.fee_paying_account = alice_id;
|
||||
pdo.proposal = proposal_id_type(0);
|
||||
pdo.using_owner_authority = false;
|
||||
|
||||
proposal_create_operation pop;
|
||||
pop.proposed_ops.emplace_back( pdo );
|
||||
pop.fee_paying_account = alice_id;
|
||||
pop.expiration_time = db.head_block_time() + fc::days(1);
|
||||
trx.operations.push_back( pop );
|
||||
const proposal_id_type pid1 = PUSH_TX( db, trx, ~0 ).operation_results[0].get<object_id_type>();
|
||||
trx.clear();
|
||||
BOOST_REQUIRE_EQUAL( 0, pid1.instance.value );
|
||||
db.get<proposal_object>(pid1);
|
||||
|
||||
proposal_update_operation pup;
|
||||
pup.fee_paying_account = alice_id;
|
||||
pup.proposal = proposal_id_type(0);
|
||||
pup.active_approvals_to_add.insert( alice_id );
|
||||
trx.operations.push_back(pup);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
// Proposal failed and still exists
|
||||
db.get<proposal_object>(pid1);
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -462,4 +462,29 @@ BOOST_AUTO_TEST_CASE( broadcast_transaction_with_callback_test ) {
|
|||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( broadcast_transaction_too_large ) {
|
||||
try {
|
||||
|
||||
fc::ecc::private_key cid_key = fc::ecc::private_key::regenerate( fc::digest("key") );
|
||||
const account_id_type cid_id = create_account( "cid", cid_key.get_public_key() ).id;
|
||||
fund( cid_id(db) );
|
||||
|
||||
auto nb_api = std::make_shared< graphene::app::network_broadcast_api >( app );
|
||||
|
||||
generate_blocks( HARDFORK_CORE_1573_TIME + 10 );
|
||||
|
||||
set_expiration( db, trx );
|
||||
transfer_operation trans;
|
||||
trans.from = cid_id;
|
||||
trans.to = account_id_type();
|
||||
trans.amount = asset(1);
|
||||
for(int i = 0; i < 250; ++i )
|
||||
trx.operations.push_back( trans );
|
||||
sign( trx, cid_key );
|
||||
|
||||
BOOST_CHECK_THROW( nb_api->broadcast_transaction( trx ), fc::exception );
|
||||
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -438,6 +438,67 @@ BOOST_AUTO_TEST_CASE( prediction_market )
|
|||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Prediction markets should not suffer a black swan (Issue #460)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE( prediction_market_black_swan )
|
||||
{
|
||||
try {
|
||||
ACTORS((judge)(dan)(nathan));
|
||||
|
||||
// progress to recent hardfork
|
||||
generate_blocks( HARDFORK_CORE_1270_TIME );
|
||||
set_expiration( db, trx );
|
||||
|
||||
const auto& pmark = create_prediction_market("PMARK", judge_id);
|
||||
|
||||
int64_t init_balance(1000000);
|
||||
transfer(committee_account, judge_id, asset(init_balance));
|
||||
transfer(committee_account, dan_id, asset(init_balance));
|
||||
|
||||
update_feed_producers( pmark, { judge_id });
|
||||
price_feed feed;
|
||||
feed.settlement_price = asset( 1, pmark.id ) / asset( 1 );
|
||||
publish_feed( pmark, judge, feed );
|
||||
|
||||
borrow( dan, pmark.amount(1000), asset(1000) );
|
||||
|
||||
// feed a price that will cause a black swan
|
||||
feed.settlement_price = asset( 1, pmark.id ) / asset( 1000 );
|
||||
publish_feed( pmark, judge, feed );
|
||||
|
||||
// verify a black swan happened
|
||||
GRAPHENE_REQUIRE_THROW(borrow( dan, pmark.amount(1000), asset(1000) ), fc::exception);
|
||||
trx.clear();
|
||||
|
||||
// progress past hardfork
|
||||
generate_blocks( HARDFORK_CORE_460_TIME + db.get_global_properties().parameters.maintenance_interval );
|
||||
set_expiration( db, trx );
|
||||
|
||||
// create another prediction market to test the hardfork
|
||||
const auto& pmark2 = create_prediction_market("PMARKII", judge_id);
|
||||
update_feed_producers( pmark2, { judge_id });
|
||||
price_feed feed2;
|
||||
feed2.settlement_price = asset( 1, pmark2.id ) / asset( 1 );
|
||||
publish_feed( pmark2, judge, feed2 );
|
||||
|
||||
borrow( dan, pmark2.amount(1000), asset(1000) );
|
||||
|
||||
// feed a price that would have caused a black swan
|
||||
feed2.settlement_price = asset( 1, pmark2.id ) / asset( 1000 );
|
||||
publish_feed( pmark2, judge, feed2 );
|
||||
|
||||
// verify a black swan did not happen
|
||||
borrow( dan, pmark2.amount(1000), asset(1000) );
|
||||
|
||||
generate_block(~database::skip_transaction_dupe_check);
|
||||
generate_blocks( db.get_dynamic_global_properties().next_maintenance_time );
|
||||
generate_block();
|
||||
} catch( const fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( create_account_test )
|
||||
{
|
||||
|
|
@ -1464,6 +1525,111 @@ BOOST_AUTO_TEST_CASE( reserve_asset_test )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( call_order_update_evaluator_test )
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS( (alice) (bob) );
|
||||
transfer(committee_account, alice_id, asset(10000000 * GRAPHENE_BLOCKCHAIN_PRECISION));
|
||||
|
||||
const auto& core = asset_id_type()(db);
|
||||
|
||||
// attempt to increase current supply beyond max_supply
|
||||
const auto& bitjmj = create_bitasset( "JMJBIT", alice_id );
|
||||
auto bitjmj_id = bitjmj.get_id();
|
||||
share_type original_max_supply = bitjmj.options.max_supply;
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" );
|
||||
update_feed_producers( bitjmj, {alice_id} );
|
||||
price_feed current_feed;
|
||||
current_feed.settlement_price = bitjmj.amount( 100000 ) / core.amount(1);
|
||||
publish_feed( bitjmj, alice, current_feed );
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" );
|
||||
call_order_update_operation op;
|
||||
op.funding_account = alice_id;
|
||||
op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION );
|
||||
op.delta_debt = asset( bitjmj.options.max_supply + 1, bitjmj.id );
|
||||
transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );
|
||||
generate_block();
|
||||
}
|
||||
|
||||
// advance past hardfork
|
||||
generate_blocks( HARDFORK_CORE_1465_TIME );
|
||||
set_expiration( db, trx );
|
||||
|
||||
// bitjmj should have its problem corrected
|
||||
auto newbitjmj = bitjmj_id(db);
|
||||
BOOST_REQUIRE_GT(newbitjmj.options.max_supply.value, original_max_supply.value);
|
||||
|
||||
// now try with an asset after the hardfork
|
||||
const auto& bitusd = create_bitasset( "USDBIT", alice_id );
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Setting price feed to $100000 / 1" );
|
||||
update_feed_producers( bitusd, {alice_id} );
|
||||
price_feed current_feed;
|
||||
current_feed.settlement_price = bitusd.amount( 100000 ) / core.amount(1);
|
||||
publish_feed( bitusd, alice_id(db), current_feed );
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Attempting a call_order_update that exceeds max_supply" );
|
||||
call_order_update_operation op;
|
||||
op.funding_account = alice_id;
|
||||
op.delta_collateral = asset( 1000000 * GRAPHENE_BLOCKCHAIN_PRECISION );
|
||||
op.delta_debt = asset( bitusd.options.max_supply + 1, bitusd.id );
|
||||
transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception );
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Creating 2 bitusd and transferring to bob (increases current supply)" );
|
||||
call_order_update_operation op;
|
||||
op.funding_account = alice_id;
|
||||
op.delta_collateral = asset( 100 * GRAPHENE_BLOCKCHAIN_PRECISION );
|
||||
op.delta_debt = asset( 2, bitusd.id );
|
||||
transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );
|
||||
transfer( alice_id(db), bob_id(db), asset( 2, bitusd.id ) );
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Again attempting a call_order_update_operation that is max_supply - 1 (should throw)" );
|
||||
call_order_update_operation op;
|
||||
op.funding_account = alice_id;
|
||||
op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION );
|
||||
op.delta_debt = asset( bitusd.options.max_supply - 1, bitusd.id );
|
||||
transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures ), fc::exception);
|
||||
}
|
||||
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Again attempting a call_order_update_operation that equals max_supply (should work)" );
|
||||
call_order_update_operation op;
|
||||
op.funding_account = alice_id;
|
||||
op.delta_collateral = asset( 100000 * GRAPHENE_BLOCKCHAIN_PRECISION );
|
||||
op.delta_debt = asset( bitusd.options.max_supply - 2, bitusd.id );
|
||||
transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
PUSH_TX( db, tx, database::skip_tapos_check | database::skip_transaction_signatures );
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
/**
|
||||
* This test demonstrates how using the call_order_update_operation to
|
||||
* trigger a margin call is legal if there is a matching order.
|
||||
|
|
|
|||
Loading…
Reference in a new issue