Merge branch 'newmaster' into newbitshares
Conflicts: libraries/chain/asset_evaluator.cpp libraries/chain/include/graphene/chain/hardfork.hpp libraries/chain/market_evaluator.cpp
This commit is contained in:
commit
545fbfe63c
35 changed files with 711 additions and 119 deletions
|
|
@ -39,6 +39,7 @@ struct get_impacted_account_visitor
|
|||
_impacted.insert( op.to );
|
||||
}
|
||||
|
||||
void operator()( const asset_claim_fees_operation& op ){}
|
||||
void operator()( const limit_order_create_operation& op ) {}
|
||||
void operator()( const limit_order_cancel_operation& op )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <fc/uint128.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -38,12 +39,32 @@ share_type cut_fee(share_type a, uint16_t p)
|
|||
return r.to_uint64();
|
||||
}
|
||||
|
||||
bool account_object::is_authorized_asset(const asset_object& asset_obj) const {
|
||||
bool account_object::is_authorized_asset(const asset_object& asset_obj, const database& d) const
|
||||
{
|
||||
if( d.head_block_time() > HARDFORK_416_TIME )
|
||||
{
|
||||
if( !(asset_obj.options.flags & white_list) )
|
||||
return true;
|
||||
}
|
||||
|
||||
for( const auto id : blacklisting_accounts )
|
||||
if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() ) return false;
|
||||
{
|
||||
if( asset_obj.options.blacklist_authorities.find(id) != asset_obj.options.blacklist_authorities.end() )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( d.head_block_time() > HARDFORK_415_TIME )
|
||||
{
|
||||
if( asset_obj.options.whitelist_authorities.size() == 0 )
|
||||
return true;
|
||||
}
|
||||
|
||||
for( const auto id : whitelisting_accounts )
|
||||
if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() ) return true;
|
||||
{
|
||||
if( asset_obj.options.whitelist_authorities.find(id) != asset_obj.options.whitelist_authorities.end() )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +74,6 @@ void account_balance_object::adjust_balance(const asset& delta)
|
|||
balance += delta.amount;
|
||||
}
|
||||
|
||||
|
||||
void account_statistics_object::process_fees(const account_object& a, database& d) const
|
||||
{
|
||||
if( pending_fees > 0 || pending_vested_fees > 0 )
|
||||
|
|
|
|||
|
|
@ -45,20 +45,36 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
|
|||
for( auto id : op.common_options.blacklist_authorities )
|
||||
d.get_object(id);
|
||||
|
||||
auto& asset_indx = db().get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto& asset_indx = d.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
auto asset_symbol_itr = asset_indx.find( op.symbol );
|
||||
FC_ASSERT( asset_symbol_itr == asset_indx.end() );
|
||||
|
||||
auto dotpos = op.symbol.find( '.' );
|
||||
if( dotpos != std::string::npos && d.head_block_time() > HARDFORK_385_TIME ) {
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( op.symbol );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
if( d.head_block_time() <= HARDFORK_409_TIME )
|
||||
{
|
||||
auto dotpos = op.symbol.find( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
{
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( op.symbol );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dotpos = op.symbol.rfind( '.' );
|
||||
if( dotpos != std::string::npos )
|
||||
{
|
||||
auto prefix = op.symbol.substr( 0, dotpos );
|
||||
auto asset_symbol_itr = asset_indx.find( prefix );
|
||||
FC_ASSERT( asset_symbol_itr != asset_indx.end(), "Asset ${s} may only be created by issuer of ${p}, but ${p} has not been registered",
|
||||
("s",op.symbol)("p",prefix) );
|
||||
FC_ASSERT( asset_symbol_itr->issuer == op.issuer, "Asset ${s} may only be created by issuer of ${p}, ${i}",
|
||||
("s",op.symbol)("p",prefix)("i", op.issuer(d).name) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
core_fee_paid -= core_fee_paid.value/2;
|
||||
|
||||
|
|
@ -126,7 +142,7 @@ object_id_type asset_create_evaluator::do_apply( const asset_create_operation& o
|
|||
|
||||
void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
|
||||
{ try {
|
||||
database& d = db();
|
||||
const database& d = db();
|
||||
|
||||
const asset_object& a = o.asset_to_issue.asset_id(d);
|
||||
FC_ASSERT( o.issuer == a.issuer );
|
||||
|
|
@ -136,7 +152,7 @@ void_result asset_issue_evaluator::do_evaluate( const asset_issue_operation& o )
|
|||
|
||||
if( a.options.flags & white_list )
|
||||
{
|
||||
FC_ASSERT( to_account->is_authorized_asset( a ) );
|
||||
FC_ASSERT( to_account->is_authorized_asset( a, d ) );
|
||||
}
|
||||
|
||||
asset_dyn_data = &a.dynamic_asset_data_id(d);
|
||||
|
|
@ -158,7 +174,7 @@ void_result asset_issue_evaluator::do_apply( const asset_issue_operation& o )
|
|||
|
||||
void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation& o )
|
||||
{ try {
|
||||
database& d = db();
|
||||
const database& d = db();
|
||||
|
||||
const asset_object& a = o.amount_to_reserve.asset_id(d);
|
||||
GRAPHENE_ASSERT(
|
||||
|
|
@ -172,7 +188,7 @@ void_result asset_reserve_evaluator::do_evaluate( const asset_reserve_operation&
|
|||
|
||||
if( a.options.flags & white_list )
|
||||
{
|
||||
FC_ASSERT( from_account->is_authorized_asset( a ) );
|
||||
FC_ASSERT( from_account->is_authorized_asset( a, d ) );
|
||||
}
|
||||
|
||||
asset_dyn_data = &a.dynamic_asset_data_id(d);
|
||||
|
|
@ -514,4 +530,32 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
|
|||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW((o)) }
|
||||
|
||||
|
||||
|
||||
void_result asset_claim_fees_evaluator::do_evaluate( const asset_claim_fees_operation& o )
|
||||
{ try {
|
||||
FC_ASSERT( db().head_block_time() > HARDFORK_413_TIME );
|
||||
FC_ASSERT( o.amount_to_claim.asset_id(db()).issuer == o.issuer, "Asset fees may only be claimed by the issuer" );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
void_result asset_claim_fees_evaluator::do_apply( const asset_claim_fees_operation& o )
|
||||
{ try {
|
||||
database& d = db();
|
||||
|
||||
const asset_object& a = o.amount_to_claim.asset_id(d);
|
||||
const asset_dynamic_data_object& addo = a.dynamic_asset_data_id(d);
|
||||
FC_ASSERT( o.amount_to_claim.amount <= addo.accumulated_fees, "Attempt to claim more fees than have accumulated", ("addo",addo) );
|
||||
|
||||
d.modify( addo, [&]( asset_dynamic_data_object& _addo ) {
|
||||
_addo.accumulated_fees -= o.amount_to_claim.amount;
|
||||
});
|
||||
|
||||
d.adjust_balance( o.issuer, o.amount_to_claim );
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ void_result transfer_to_blind_evaluator::do_evaluate( const transfer_to_blind_op
|
|||
const auto& atype = o.amount.asset_id(db());
|
||||
FC_ASSERT( atype.allow_confidential() );
|
||||
FC_ASSERT( !atype.is_transfer_restricted() );
|
||||
FC_ASSERT( !atype.enforce_white_list() );
|
||||
FC_ASSERT( !(atype.options.flags & white_list) );
|
||||
|
||||
for( const auto& out : o.outputs )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ void database::initialize_evaluators()
|
|||
register_evaluator<transfer_to_blind_evaluator>();
|
||||
register_evaluator<transfer_from_blind_evaluator>();
|
||||
register_evaluator<blind_transfer_evaluator>();
|
||||
register_evaluator<asset_claim_fees_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
|
|||
|
|
@ -144,7 +144,9 @@ void database::close(bool rewind)
|
|||
{
|
||||
try
|
||||
{
|
||||
while( true )
|
||||
uint32_t cutoff = get_dynamic_global_properties().last_irreversible_block_num;
|
||||
|
||||
while( head_block_num() > cutoff )
|
||||
{
|
||||
// elog("pop");
|
||||
block_id_type popped_block_id = head_block_id();
|
||||
|
|
|
|||
|
|
@ -366,11 +366,13 @@ void database::clear_expired_orders()
|
|||
|
||||
void database::update_expired_feeds()
|
||||
{
|
||||
auto& asset_idx = get_index_type<asset_index>().indices();
|
||||
for( const asset_object& a : asset_idx )
|
||||
auto& asset_idx = get_index_type<asset_index>().indices().get<by_type>();
|
||||
auto itr = asset_idx.lower_bound( true /** market issued */ );
|
||||
while( itr != asset_idx.end() )
|
||||
{
|
||||
if( !a.is_market_issued() )
|
||||
continue;
|
||||
const asset_object& a = *itr;
|
||||
++itr;
|
||||
assert( a.is_market_issued() );
|
||||
|
||||
const asset_bitasset_data_object& b = a.bitasset_data(*this);
|
||||
if( b.feed_is_expired(head_block_time()) )
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/transaction_evaluation_state.hpp>
|
||||
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
|
|
@ -46,17 +47,25 @@ database& generic_evaluator::db()const { return trx_state->db(); }
|
|||
|
||||
void generic_evaluator::prepare_fee(account_id_type account_id, asset fee)
|
||||
{
|
||||
const database& d = db();
|
||||
fee_from_account = fee;
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
fee_paying_account = &account_id(db());
|
||||
fee_paying_account_statistics = &fee_paying_account->statistics(db());
|
||||
fee_paying_account = &account_id(d);
|
||||
fee_paying_account_statistics = &fee_paying_account->statistics(d);
|
||||
|
||||
fee_asset = &fee.asset_id(db());
|
||||
fee_asset_dyn_data = &fee_asset->dynamic_asset_data_id(db());
|
||||
fee_asset = &fee.asset_id(d);
|
||||
fee_asset_dyn_data = &fee_asset->dynamic_asset_data_id(d);
|
||||
|
||||
if( d.head_block_time() > HARDFORK_419_TIME )
|
||||
{
|
||||
FC_ASSERT( fee_paying_account->is_authorized_asset( *fee_asset, d ), "Account ${acct} '${name}' attempted to pay fee by using asset ${a} '${sym}', which is unauthorized due to whitelist / blacklist",
|
||||
("acct", fee_paying_account->id)("name", fee_paying_account->name)("a", fee_asset->id)("sym", fee_asset->symbol) );
|
||||
}
|
||||
|
||||
if( fee_from_account.asset_id == asset_id_type() )
|
||||
core_fee_paid = fee_from_account.amount;
|
||||
else {
|
||||
else
|
||||
{
|
||||
asset fee_from_pool = fee_from_account * fee_asset->options.core_exchange_rate;
|
||||
FC_ASSERT( fee_from_pool.asset_id == asset_id_type() );
|
||||
core_fee_paid = fee_from_pool.amount;
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ namespace graphene { namespace chain {
|
|||
* @return true if this account is whitelisted and not blacklisted to transact in the provided asset; false
|
||||
* otherwise.
|
||||
*/
|
||||
bool is_authorized_asset(const asset_object& asset_obj)const;
|
||||
bool is_authorized_asset(const asset_object& asset_obj, const database& d)const;
|
||||
|
||||
account_id_type get_id()const { return id; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -133,4 +133,13 @@ namespace graphene { namespace chain {
|
|||
std::map<std::pair<asset_id_type,asset_id_type>,price_feed> median_feed_values;
|
||||
};
|
||||
|
||||
class asset_claim_fees_evaluator : public evaluator<asset_claim_fees_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef asset_claim_fees_operation operation_type;
|
||||
|
||||
void_result do_evaluate( const asset_claim_fees_operation& o );
|
||||
void_result do_apply( const asset_claim_fees_operation& o );
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
|
|||
|
|
@ -82,8 +82,6 @@ namespace graphene { namespace chain {
|
|||
/// @return true if symbol is a valid ticker symbol; false otherwise.
|
||||
static bool is_valid_symbol( const string& symbol );
|
||||
|
||||
/// @return true if accounts must be on a whitelist in order to hold this asset; false otherwise.
|
||||
bool enforce_white_list()const { return options.flags & white_list; }
|
||||
/// @return true if this is a market-issued asset; false otherwise.
|
||||
bool is_market_issued()const { return bitasset_data_id.valid(); }
|
||||
/// @return true if users may request force-settlement of this market-issued asset; false otherwise
|
||||
|
|
@ -213,7 +211,6 @@ namespace graphene { namespace chain {
|
|||
void update_median_feeds(time_point_sec current_time);
|
||||
};
|
||||
|
||||
|
||||
struct by_feed_expiration;
|
||||
typedef multi_index_container<
|
||||
asset_bitasset_data_object,
|
||||
|
|
@ -227,17 +224,19 @@ namespace graphene { namespace chain {
|
|||
typedef flat_index<asset_bitasset_data_object> asset_bitasset_data_index;
|
||||
|
||||
struct by_symbol;
|
||||
struct by_type;
|
||||
typedef multi_index_container<
|
||||
asset_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >
|
||||
ordered_unique< tag<by_symbol>, member<asset_object, string, &asset_object::symbol> >,
|
||||
ordered_non_unique< tag<by_type>, const_mem_fun<asset_object, bool, &asset_object::is_market_issued> >
|
||||
>
|
||||
> asset_object_multi_index_type;
|
||||
typedef generic_index<asset_object, asset_object_multi_index_type> asset_index;
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::asset_dynamic_data_object, (graphene::db::object),
|
||||
(current_supply)(confidential_supply)(accumulated_fees)(fee_pool) )
|
||||
|
||||
|
|
@ -260,4 +259,3 @@ FC_REFLECT_DERIVED( graphene::chain::asset_object, (graphene::db::object),
|
|||
(dynamic_asset_data_id)
|
||||
(bitasset_data_id)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
|
|||
(time)
|
||||
(current_witness)
|
||||
(next_maintenance_time)
|
||||
(last_budget_time)
|
||||
(witness_budget)
|
||||
(accounts_registered_this_interval)
|
||||
(recently_missed_count)
|
||||
|
|
|
|||
|
|
@ -24,3 +24,8 @@
|
|||
#define HARDFORK_359_TIME (fc::time_point_sec( 1444416300 ))
|
||||
#define HARDFORK_385_TIME (fc::time_point_sec( 1445558400 )) // October 23 enforce PARENT.CHILD and allow short names
|
||||
#define HARDFORK_393_TIME (fc::time_point_sec( 2445558400 )) // Refund order creation fee on cancel
|
||||
#define HARDFORK_409_TIME (fc::time_point_sec( 1446652800 ))
|
||||
#define HARDFORK_413_TIME (fc::time_point_sec( 1446652800 ))
|
||||
#define HARDFORK_415_TIME (fc::time_point_sec( 1446652800 ))
|
||||
#define HARDFORK_416_TIME (fc::time_point_sec( 1446652800 ))
|
||||
#define HARDFORK_419_TIME (fc::time_point_sec( 1446652800 ))
|
||||
|
|
|
|||
|
|
@ -53,12 +53,11 @@ namespace graphene { namespace chain {
|
|||
/// the core exchange rate.
|
||||
price core_exchange_rate;
|
||||
|
||||
/// A set of accounts which maintain whitelists to consult for this asset. If enforce_white_list() returns
|
||||
/// true, an account may only send, receive, trade, etc. in this asset if one of these accounts appears in
|
||||
/// its account_object::whitelisting_accounts field.
|
||||
/// A set of accounts which maintain whitelists to consult for this asset. If whitelist_authorities
|
||||
/// is non-empty, then only accounts in whitelist_authorities are allowed to hold, use, or transfer the asset.
|
||||
flat_set<account_id_type> whitelist_authorities;
|
||||
/// A set of accounts which maintain blacklists to consult for this asset. If enforce_white_list() returns
|
||||
/// true, an account may only send, receive, trade, etc. in this asset if none of these accounts appears in
|
||||
/// A set of accounts which maintain blacklists to consult for this asset. If flags & white_list is set,
|
||||
/// an account may only send, receive, trade, etc. in this asset if none of these accounts appears in
|
||||
/// its account_object::blacklisting_accounts field. If the account is blacklisted, it may not transact in
|
||||
/// this asset even if it is also whitelisted.
|
||||
flat_set<account_id_type> blacklist_authorities;
|
||||
|
|
@ -420,8 +419,30 @@ namespace graphene { namespace chain {
|
|||
void validate()const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief used to transfer accumulated fees back to the issuer's balance.
|
||||
*/
|
||||
struct asset_claim_fees_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type {
|
||||
uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;
|
||||
};
|
||||
|
||||
asset fee;
|
||||
account_id_type issuer;
|
||||
asset amount_to_claim; /// amount_to_claim.asset_id->issuer must == issuer
|
||||
extensions_type extensions;
|
||||
|
||||
account_id_type fee_payer()const { return issuer; }
|
||||
void validate()const;
|
||||
};
|
||||
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_claim_fees_operation, (fee)(issuer)(amount_to_claim)(extensions) )
|
||||
FC_REFLECT( graphene::chain::asset_claim_fees_operation::fee_parameters_type, (fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_options,
|
||||
(max_supply)
|
||||
(market_fee_percent)
|
||||
|
|
|
|||
|
|
@ -117,4 +117,5 @@ namespace graphene { namespace chain {
|
|||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::operation_result )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::future_extensions )
|
||||
FC_REFLECT( graphene::chain::void_result, )
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@ namespace graphene { namespace chain {
|
|||
transfer_to_blind_operation,
|
||||
blind_transfer_operation,
|
||||
transfer_from_blind_operation,
|
||||
|
||||
asset_settle_cancel_operation // VIRTUAL
|
||||
asset_settle_cancel_operation, // VIRTUAL
|
||||
asset_claim_fees_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ FC_REFLECT_TYPENAME( graphene::chain::operation_history_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::withdraw_permission_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vesting_balance_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::worker_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::balance_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::global_property_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::dynamic_global_property_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::asset_dynamic_data_id_type )
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
namespace graphene { namespace chain {
|
||||
void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_operation& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
const database& d = db();
|
||||
|
||||
FC_ASSERT( op.expiration >= d.head_block_time() );
|
||||
|
||||
|
|
@ -42,8 +42,16 @@ void_result limit_order_create_evaluator::do_evaluate(const limit_order_create_o
|
|||
if( _sell_asset->options.blacklist_markets.size() )
|
||||
FC_ASSERT( _sell_asset->options.blacklist_markets.find(_receive_asset->id) == _sell_asset->options.blacklist_markets.end() );
|
||||
|
||||
if( _sell_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_sell_asset ) );
|
||||
if( _receive_asset->enforce_white_list() ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset ) );
|
||||
if( d.head_block_time() <= HARDFORK_416_TIME )
|
||||
{
|
||||
if( _sell_asset->options.flags & white_list ) FC_ASSERT( _seller->is_authorized_asset( *_sell_asset, d ) );
|
||||
if( _receive_asset->options.flags & white_list ) FC_ASSERT( _seller->is_authorized_asset( *_receive_asset, d ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
FC_ASSERT( _seller->is_authorized_asset( *_sell_asset, d ) );
|
||||
FC_ASSERT( _seller->is_authorized_asset( *_receive_asset, d ) );
|
||||
}
|
||||
|
||||
FC_ASSERT( d.get_balance( *_seller, *_sell_asset ) >= op.amount_to_sell, "insufficient balance",
|
||||
("balance",d.get_balance(*_seller,*_sell_asset))("amount_to_sell",op.amount_to_sell) );
|
||||
|
|
|
|||
|
|
@ -227,4 +227,9 @@ void asset_options::validate()const
|
|||
}
|
||||
}
|
||||
|
||||
void asset_claim_fees_operation::validate()const {
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
FC_ASSERT( amount_to_claim.amount > 0 );
|
||||
}
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@
|
|||
#include <graphene/chain/transfer_evaluator.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
|
||||
{ try {
|
||||
|
||||
database& d = db();
|
||||
const database& d = db();
|
||||
|
||||
const account_object& from_account = op.from(d);
|
||||
const account_object& to_account = op.to(d);
|
||||
|
|
@ -38,14 +39,14 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
|
|||
if( asset_type.options.flags & white_list )
|
||||
{
|
||||
GRAPHENE_ASSERT(
|
||||
from_account.is_authorized_asset( asset_type ),
|
||||
from_account.is_authorized_asset( asset_type, d ),
|
||||
transfer_from_account_not_whitelisted,
|
||||
"'from' account ${from} is not whitelisted for asset ${asset}",
|
||||
("from",op.from)
|
||||
("asset",op.amount.asset_id)
|
||||
);
|
||||
GRAPHENE_ASSERT(
|
||||
to_account.is_authorized_asset( asset_type ),
|
||||
to_account.is_authorized_asset( asset_type, d ),
|
||||
transfer_to_account_not_whitelisted,
|
||||
"'to' account ${to} is not whitelisted for asset ${asset}",
|
||||
("to",op.to)
|
||||
|
|
@ -53,8 +54,12 @@ void_result transfer_evaluator::do_evaluate( const transfer_operation& op )
|
|||
);
|
||||
}
|
||||
|
||||
if( fee_asset_type.options.flags & white_list )
|
||||
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
|
||||
if( d.head_block_time() <= HARDFORK_419_TIME )
|
||||
{
|
||||
if( fee_asset_type.options.flags & white_list )
|
||||
FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) );
|
||||
}
|
||||
// the above becomes no-op after hardfork because this check will then be performed in evaluator
|
||||
|
||||
if( asset_type.is_transfer_restricted() )
|
||||
{
|
||||
|
|
@ -87,7 +92,7 @@ void_result transfer_evaluator::do_apply( const transfer_operation& o )
|
|||
|
||||
void_result override_transfer_evaluator::do_evaluate( const override_transfer_operation& op )
|
||||
{ try {
|
||||
database& d = db();
|
||||
const database& d = db();
|
||||
|
||||
const asset_object& asset_type = op.amount.asset_id(d);
|
||||
GRAPHENE_ASSERT(
|
||||
|
|
@ -104,12 +109,16 @@ void_result override_transfer_evaluator::do_evaluate( const override_transfer_op
|
|||
|
||||
if( asset_type.options.flags & white_list )
|
||||
{
|
||||
FC_ASSERT( to_account.is_authorized_asset( asset_type ) );
|
||||
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
|
||||
FC_ASSERT( to_account.is_authorized_asset( asset_type, d ) );
|
||||
FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) );
|
||||
}
|
||||
|
||||
if( fee_asset_type.options.flags & white_list )
|
||||
FC_ASSERT( from_account.is_authorized_asset( asset_type ) );
|
||||
if( d.head_block_time() <= HARDFORK_419_TIME )
|
||||
{
|
||||
if( fee_asset_type.options.flags & white_list )
|
||||
FC_ASSERT( from_account.is_authorized_asset( asset_type, d ) );
|
||||
}
|
||||
// the above becomes no-op after hardfork because this check will then be performed in evaluator
|
||||
|
||||
FC_ASSERT( d.get_balance( from_account, asset_type ).amount >= op.amount.amount,
|
||||
"", ("total_transfer",op.amount)("balance",d.get_balance(from_account, asset_type).amount) );
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned(const vesting_poli
|
|||
delta_coin_seconds *= delta_seconds;
|
||||
|
||||
fc::uint128_t coin_seconds_earned_cap = ctx.balance.amount.value;
|
||||
coin_seconds_earned_cap *= vesting_seconds;
|
||||
coin_seconds_earned_cap *= std::max(vesting_seconds, 1u);
|
||||
|
||||
return std::min(coin_seconds_earned + delta_coin_seconds, coin_seconds_earned_cap);
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ asset cdd_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx
|
|||
if(ctx.now <= start_claim)
|
||||
return asset(0, ctx.balance.asset_id);
|
||||
fc::uint128_t cs_earned = compute_coin_seconds_earned(ctx);
|
||||
fc::uint128_t withdraw_available = cs_earned / vesting_seconds;
|
||||
fc::uint128_t withdraw_available = cs_earned / std::max(vesting_seconds, 1u);
|
||||
assert(withdraw_available <= ctx.balance.amount.value);
|
||||
return asset(withdraw_available.to_uint64(), ctx.balance.asset_id);
|
||||
}
|
||||
|
|
@ -123,14 +123,14 @@ void cdd_vesting_policy::on_deposit(const vesting_policy_context& ctx)
|
|||
void cdd_vesting_policy::on_deposit_vested(const vesting_policy_context& ctx)
|
||||
{
|
||||
on_deposit(ctx);
|
||||
coin_seconds_earned += ctx.amount.amount.value * vesting_seconds;
|
||||
coin_seconds_earned += ctx.amount.amount.value * std::max(vesting_seconds, 1u);
|
||||
}
|
||||
|
||||
void cdd_vesting_policy::on_withdraw(const vesting_policy_context& ctx)
|
||||
{
|
||||
update_coin_seconds_earned(ctx);
|
||||
fc::uint128_t coin_seconds_needed = ctx.amount.amount.value;
|
||||
coin_seconds_needed *= vesting_seconds;
|
||||
coin_seconds_needed *= std::max(vesting_seconds, 1u);
|
||||
// is_withdraw_allowed should forbid any withdrawal that
|
||||
// would trigger this assert
|
||||
assert(coin_seconds_needed <= coin_seconds_earned);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -53,7 +54,7 @@ object_id_type withdraw_permission_create_evaluator::do_apply(const operation_ty
|
|||
|
||||
void_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_permission_claim_evaluator::operation_type& op)
|
||||
{ try {
|
||||
database& d = db();
|
||||
const database& d = db();
|
||||
|
||||
const withdraw_permission_object& permit = op.withdraw_permission(d);
|
||||
FC_ASSERT(permit.expiration > d.head_block_time() );
|
||||
|
|
@ -65,12 +66,22 @@ void_result withdraw_permission_claim_evaluator::do_evaluate(const withdraw_perm
|
|||
const asset_object& _asset = op.amount_to_withdraw.asset_id(d);
|
||||
if( _asset.is_transfer_restricted() ) FC_ASSERT( _asset.issuer == permit.authorized_account || _asset.issuer == permit.withdraw_from_account );
|
||||
|
||||
if( _asset.enforce_white_list() )
|
||||
if( d.head_block_time() <= HARDFORK_416_TIME )
|
||||
{
|
||||
if( _asset.options.flags & white_list )
|
||||
{
|
||||
const account_object& from = op.withdraw_to_account(d);
|
||||
const account_object& to = permit.authorized_account(d);
|
||||
FC_ASSERT( to.is_authorized_asset( _asset, d ) );
|
||||
FC_ASSERT( from.is_authorized_asset( _asset, d ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const account_object& from = op.withdraw_to_account(d);
|
||||
const account_object& to = permit.authorized_account(d);
|
||||
FC_ASSERT( to.is_authorized_asset( _asset ) );
|
||||
FC_ASSERT( from.is_authorized_asset( _asset ) );
|
||||
FC_ASSERT( to.is_authorized_asset( _asset, d ) );
|
||||
FC_ASSERT( from.is_authorized_asset( _asset, d ) );
|
||||
}
|
||||
|
||||
return void_result();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/mem_fun.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -117,7 +116,7 @@ namespace graphene { namespace chain {
|
|||
struct sparse_index : public generic_index<T, boost::multi_index_container<
|
||||
T,
|
||||
indexed_by<
|
||||
hashed_unique<
|
||||
ordered_unique<
|
||||
tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>
|
||||
|
|
|
|||
|
|
@ -146,12 +146,18 @@ void witness_plugin::plugin_shutdown()
|
|||
void witness_plugin::schedule_production_loop()
|
||||
{
|
||||
//Schedule for the next second's tick regardless of chain state
|
||||
// If we would wait less than 200ms, wait for the whole second.
|
||||
fc::time_point now = graphene::time::now();
|
||||
fc::time_point_sec next_second( now + fc::microseconds( 1200000 ) );
|
||||
//wdump( (now.time_since_epoch().count())(next_second) );
|
||||
// If we would wait less than 50ms, wait for the whole second.
|
||||
fc::time_point ntp_now = graphene::time::now();
|
||||
fc::time_point fc_now = fc::time_point::now();
|
||||
int64_t time_to_next_second = 1000000 - (ntp_now.time_since_epoch().count() % 1000000);
|
||||
if( time_to_next_second < 50000 ) // we must sleep for at least 50ms
|
||||
time_to_next_second += 1000000;
|
||||
|
||||
fc::time_point next_wakeup( fc_now + fc::microseconds( time_to_next_second ) );
|
||||
|
||||
//wdump( (now.time_since_epoch().count())(next_wakeup.time_since_epoch().count()) );
|
||||
_block_production_task = fc::schedule([this]{block_production_loop();},
|
||||
next_second, "Witness Block Production");
|
||||
next_wakeup, "Witness Block Production");
|
||||
}
|
||||
|
||||
block_production_condition::block_production_condition_enum witness_plugin::block_production_loop()
|
||||
|
|
|
|||
|
|
@ -2955,7 +2955,7 @@ signed_transaction wallet_api::reserve_asset(string from,
|
|||
string symbol,
|
||||
bool broadcast /* = false */)
|
||||
{
|
||||
return my->fund_asset_fee_pool(from, amount, symbol, broadcast);
|
||||
return my->reserve_asset(from, amount, symbol, broadcast);
|
||||
}
|
||||
|
||||
signed_transaction wallet_api::global_settle_asset(string symbol,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ def main():
|
|||
with open(filename, "r") as f:
|
||||
patch = json.load(f)
|
||||
for k, v in patch.get("append", {}).items():
|
||||
if k not in genesis:
|
||||
genesis[k] = []
|
||||
sys.stderr.write("[WARN] item {k} was created\n".format(k=k))
|
||||
genesis[k].extend(v)
|
||||
sys.stderr.write("appended {n} items to {k}\n".format(n=len(v), k=k))
|
||||
for k, v in patch.get("replace", {}).items():
|
||||
|
|
|
|||
54
programs/genesis_util/generate_init_config.py
Executable file
54
programs/genesis_util/generate_init_config.py
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def dump_json(obj, out, pretty):
|
||||
if pretty:
|
||||
json.dump(obj, out, indent=2, sort_keys=True)
|
||||
else:
|
||||
json.dump(obj, out, separators=(",", ":"), sort_keys=True)
|
||||
return
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate a patch file that adds init accounts")
|
||||
parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)")
|
||||
parser.add_argument("-n", "--num", metavar="N", default=11, type=int, help="number of init witnesses")
|
||||
parser.add_argument("-w", "--witness", metavar="N", default=1, type=int, help="starting witness ID")
|
||||
parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output")
|
||||
parser.add_argument("-m", "--mname", metavar="HOSTNAME", default="", help="machine name of target machine")
|
||||
parser.add_argument("-s", "--secret", metavar="SECRET", default=None, help="private key generation secret")
|
||||
opts = parser.parse_args()
|
||||
|
||||
if opts.secret is None:
|
||||
sys.stderr.write("missing required parameter --secret\n")
|
||||
sys.stderr.flush()
|
||||
sys.exit(1)
|
||||
|
||||
out_wits = []
|
||||
out_keys = []
|
||||
|
||||
for i in range(opts.num):
|
||||
if opts.mname != "":
|
||||
istr = "wit-block-signing-"+opts.mname+"-"+str(i)
|
||||
else:
|
||||
istr = "wit-block-signing-"+str(i)
|
||||
prod_str = subprocess.check_output(["programs/genesis_util/get_dev_key", opts.secret, istr]).decode("utf-8")
|
||||
prod = json.loads(prod_str)
|
||||
out_wits.append('witness-id = "1.6.'+str(opts.witness+i)+'"\n')
|
||||
out_keys.append("private-key = "+json.dumps([prod[0]["public_key"], prod[0]["private_key"]])+"\n")
|
||||
|
||||
out_data = "".join(out_wits + ["\n"] + out_keys)
|
||||
|
||||
if opts.output == "-":
|
||||
sys.stdout.write(out_data)
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
with open(opts.output, "w") as f:
|
||||
f.write(out_data)
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
using namespace graphene::chain::test;
|
||||
|
||||
uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP = 1431700000;
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
using std::cout;
|
||||
|
|
@ -499,7 +501,9 @@ const asset_object& database_fixture::create_user_issued_asset( const string& na
|
|||
creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
creator.common_options.flags = flags;
|
||||
creator.common_options.issuer_permissions = flags;
|
||||
trx.operations.clear();
|
||||
trx.operations.push_back(std::move(creator));
|
||||
set_expiration( db, trx );
|
||||
trx.validate();
|
||||
processed_transaction ptx = db.push_transaction(trx, ~0);
|
||||
trx.operations.clear();
|
||||
|
|
@ -733,6 +737,8 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obje
|
|||
op.publisher = by.id;
|
||||
op.asset_id = mia.id;
|
||||
op.feed = f;
|
||||
if( op.feed.core_exchange_rate.is_null() )
|
||||
op.feed.core_exchange_rate = op.feed.settlement_price;
|
||||
trx.operations.emplace_back( std::move(op) );
|
||||
|
||||
for( auto& op : trx.operations ) db.current_fee_schedule().set_fee(op);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
using namespace graphene::db;
|
||||
|
||||
#define GRAPHENE_TESTING_GENESIS_TIMESTAMP (1431700000)
|
||||
extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;
|
||||
|
||||
#define PUSH_TX \
|
||||
graphene::chain::test::_push_transaction
|
||||
|
|
|
|||
|
|
@ -131,14 +131,15 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
|
||||
// TODO: Don't generate this here
|
||||
auto init_account_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
|
||||
signed_block b200;
|
||||
signed_block cutoff_block;
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), make_genesis );
|
||||
b = db.generate_block(db.get_slot_time(1), db.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing);
|
||||
|
||||
// TODO: Change this test when we correct #406
|
||||
// n.b. we generate GRAPHENE_MIN_UNDO_HISTORY+1 extra blocks which will be discarded on save
|
||||
for( uint32_t i = 1; i < 200+GRAPHENE_MIN_UNDO_HISTORY+1; ++i )
|
||||
for( uint32_t i = 1; ; ++i )
|
||||
{
|
||||
BOOST_CHECK( db.head_block_id() == b.id() );
|
||||
//witness_id_type prev_witness = b.witness;
|
||||
|
|
@ -146,16 +147,20 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
//BOOST_CHECK( cur_witness != prev_witness );
|
||||
b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing);
|
||||
BOOST_CHECK( b.witness == cur_witness );
|
||||
if( i == 199 )
|
||||
b200 = b;
|
||||
uint32_t cutoff_height = db.get_dynamic_global_properties().last_irreversible_block_num;
|
||||
if( cutoff_height >= 200 )
|
||||
{
|
||||
cutoff_block = *(db.fetch_block_by_number( cutoff_height ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
db.close();
|
||||
}
|
||||
{
|
||||
database db;
|
||||
db.open(data_dir.path(), []{return genesis_state_type();});
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), 200 );
|
||||
b = b200;
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num() );
|
||||
b = cutoff_block;
|
||||
for( uint32_t i = 0; i < 200; ++i )
|
||||
{
|
||||
BOOST_CHECK( db.head_block_id() == b.id() );
|
||||
|
|
@ -164,7 +169,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
|
|||
//BOOST_CHECK( cur_witness != prev_witness );
|
||||
b = db.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing);
|
||||
}
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), 400 );
|
||||
BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num()+200 );
|
||||
}
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
#include <fc/uint128.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
|
@ -67,6 +68,138 @@ BOOST_AUTO_TEST_CASE( nonzero_fee_test )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(asset_claim_fees_test)
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS((alice)(bob)(izzy)(jill));
|
||||
// Izzy issues asset to Alice
|
||||
// Jill issues asset to Bob
|
||||
// Alice and Bob trade in the market and pay fees
|
||||
// Verify that Izzy and Jill can claim the fees
|
||||
|
||||
const share_type core_prec = asset::scaled_precision( asset_id_type()(db).precision );
|
||||
|
||||
// Return number of core shares (times precision)
|
||||
auto _core = [&]( int64_t x ) -> asset
|
||||
{ return asset( x*core_prec ); };
|
||||
|
||||
transfer( committee_account, alice_id, _core(1000000) );
|
||||
transfer( committee_account, bob_id, _core(1000000) );
|
||||
transfer( committee_account, izzy_id, _core(1000000) );
|
||||
transfer( committee_account, jill_id, _core(1000000) );
|
||||
|
||||
asset_id_type izzycoin_id = create_bitasset( "IZZYCOIN", izzy_id, GRAPHENE_1_PERCENT, charge_market_fee ).id;
|
||||
asset_id_type jillcoin_id = create_bitasset( "JILLCOIN", jill_id, 2*GRAPHENE_1_PERCENT, charge_market_fee ).id;
|
||||
|
||||
const share_type izzy_prec = asset::scaled_precision( asset_id_type(izzycoin_id)(db).precision );
|
||||
const share_type jill_prec = asset::scaled_precision( asset_id_type(jillcoin_id)(db).precision );
|
||||
|
||||
auto _izzy = [&]( int64_t x ) -> asset
|
||||
{ return asset( x*izzy_prec, izzycoin_id ); };
|
||||
auto _jill = [&]( int64_t x ) -> asset
|
||||
{ return asset( x*jill_prec, jillcoin_id ); };
|
||||
|
||||
update_feed_producers( izzycoin_id(db), { izzy_id } );
|
||||
update_feed_producers( jillcoin_id(db), { jill_id } );
|
||||
|
||||
const asset izzy_satoshi = asset(1, izzycoin_id);
|
||||
const asset jill_satoshi = asset(1, jillcoin_id);
|
||||
|
||||
// Izzycoin is worth 100 BTS
|
||||
price_feed feed;
|
||||
feed.settlement_price = price( _izzy(1), _core(100) );
|
||||
feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
|
||||
feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
|
||||
publish_feed( izzycoin_id(db), izzy, feed );
|
||||
|
||||
// Jillcoin is worth 30 BTS
|
||||
feed.settlement_price = price( _jill(1), _core(30) );
|
||||
feed.maintenance_collateral_ratio = 175 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
|
||||
feed.maximum_short_squeeze_ratio = 150 * GRAPHENE_COLLATERAL_RATIO_DENOM / 100;
|
||||
publish_feed( jillcoin_id(db), jill, feed );
|
||||
|
||||
enable_fees();
|
||||
|
||||
// Alice and Bob create some coins
|
||||
borrow( alice_id, _izzy( 200), _core( 60000) );
|
||||
borrow( bob_id, _jill(2000), _core(180000) );
|
||||
|
||||
// Alice and Bob place orders which match
|
||||
create_sell_order( alice_id, _izzy(100), _jill(300) ); // Alice is willing to sell her Izzy's for 3 Jill
|
||||
create_sell_order( bob_id, _jill(700), _izzy(200) ); // Bob is buying up to 200 Izzy's for up to 3.5 Jill
|
||||
|
||||
// 100 Izzys and 300 Jills are matched, so the fees should be
|
||||
// 1 Izzy (1%) and 6 Jill (2%).
|
||||
|
||||
auto claim_fees = [&]( account_id_type issuer, asset amount_to_claim )
|
||||
{
|
||||
asset_claim_fees_operation claim_op;
|
||||
claim_op.issuer = issuer;
|
||||
claim_op.amount_to_claim = amount_to_claim;
|
||||
signed_transaction tx;
|
||||
tx.operations.push_back( claim_op );
|
||||
db.current_fee_schedule().set_fee( tx.operations.back() );
|
||||
set_expiration( db, tx );
|
||||
fc::ecc::private_key my_pk = (issuer == izzy_id) ? izzy_private_key : jill_private_key;
|
||||
fc::ecc::private_key your_pk = (issuer == izzy_id) ? jill_private_key : izzy_private_key;
|
||||
sign( tx, your_pk );
|
||||
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx ), fc::exception );
|
||||
tx.signatures.clear();
|
||||
sign( tx, my_pk );
|
||||
PUSH_TX( db, tx );
|
||||
};
|
||||
|
||||
{
|
||||
const asset_object& izzycoin = izzycoin_id(db);
|
||||
const asset_object& jillcoin = jillcoin_id(db);
|
||||
|
||||
//wdump( (izzycoin)(izzycoin.dynamic_asset_data_id(db))((*izzycoin.bitasset_data_id)(db)) );
|
||||
//wdump( (jillcoin)(jillcoin.dynamic_asset_data_id(db))((*jillcoin.bitasset_data_id)(db)) );
|
||||
|
||||
// check the correct amount of fees has been awarded
|
||||
BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(1).amount );
|
||||
BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(6).amount );
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
// can't claim before hardfork
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) ), fc::exception );
|
||||
generate_blocks( HARDFORK_413_TIME );
|
||||
while( db.head_block_time() <= HARDFORK_413_TIME )
|
||||
{
|
||||
generate_block();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const asset_object& izzycoin = izzycoin_id(db);
|
||||
const asset_object& jillcoin = jillcoin_id(db);
|
||||
|
||||
// can't claim more than balance
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, _izzy(1) + izzy_satoshi ), fc::exception );
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(6) + jill_satoshi ), fc::exception );
|
||||
|
||||
// can't claim asset that doesn't belong to you
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, izzy_satoshi ), fc::exception );
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, jill_satoshi ), fc::exception );
|
||||
|
||||
// can claim asset in one go
|
||||
claim_fees( izzy_id, _izzy(1) );
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( izzy_id, izzy_satoshi ), fc::exception );
|
||||
BOOST_CHECK( izzycoin.dynamic_asset_data_id(db).accumulated_fees == _izzy(0).amount );
|
||||
|
||||
// can claim in multiple goes
|
||||
claim_fees( jill_id, _jill(4) );
|
||||
BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(2).amount );
|
||||
GRAPHENE_REQUIRE_THROW( claim_fees( jill_id, _jill(2) + jill_satoshi ), fc::exception );
|
||||
claim_fees( jill_id, _jill(2) );
|
||||
BOOST_CHECK( jillcoin.dynamic_asset_data_id(db).accumulated_fees == _jill(0).amount );
|
||||
}
|
||||
}
|
||||
FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// cashback_test infrastructure //
|
||||
|
|
|
|||
|
|
@ -23,8 +23,16 @@
|
|||
#include <iostream>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
extern uint32_t GRAPHENE_TESTING_GENESIS_TIMESTAMP;
|
||||
|
||||
boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
|
||||
std::srand(time(NULL));
|
||||
std::cout << "Random number generator seeded to " << time(NULL) << std::endl;
|
||||
const char* genesis_timestamp_str = getenv("GRAPHENE_TESTING_GENESIS_TIMESTAMP");
|
||||
if( genesis_timestamp_str != nullptr )
|
||||
{
|
||||
GRAPHENE_TESTING_GENESIS_TIMESTAMP = std::stoul( genesis_timestamp_str );
|
||||
}
|
||||
std::cout << "GRAPHENE_TESTING_GENESIS_TIMESTAMP is " << GRAPHENE_TESTING_GENESIS_TIMESTAMP << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -636,11 +636,11 @@ BOOST_AUTO_TEST_CASE( update_mia )
|
|||
pop.asset_id = bit_usd.get_id();
|
||||
pop.publisher = get_account("init0").get_id();
|
||||
price_feed feed;
|
||||
feed.settlement_price = price(bit_usd.amount(5), bit_usd.amount(5));
|
||||
feed.settlement_price = feed.core_exchange_rate = price(bit_usd.amount(5), bit_usd.amount(5));
|
||||
REQUIRE_THROW_WITH_VALUE(pop, feed, feed);
|
||||
feed.settlement_price = ~price(bit_usd.amount(5), asset(5));
|
||||
feed.settlement_price = feed.core_exchange_rate = ~price(bit_usd.amount(5), asset(5));
|
||||
REQUIRE_THROW_WITH_VALUE(pop, feed, feed);
|
||||
feed.settlement_price = price(bit_usd.amount(5), asset(5));
|
||||
feed.settlement_price = feed.core_exchange_rate = price(bit_usd.amount(5), asset(5));
|
||||
pop.feed = feed;
|
||||
REQUIRE_THROW_WITH_VALUE(pop, feed.maintenance_collateral_ratio, 0);
|
||||
trx.operations.back() = pop;
|
||||
|
|
@ -687,7 +687,7 @@ BOOST_AUTO_TEST_CASE( create_uia )
|
|||
const asset_object& test_asset = test_asset_id(db);
|
||||
BOOST_CHECK(test_asset.symbol == "TEST");
|
||||
BOOST_CHECK(asset(1, test_asset_id) * test_asset.options.core_exchange_rate == asset(2));
|
||||
BOOST_CHECK(!test_asset.enforce_white_list());
|
||||
BOOST_CHECK((test_asset.options.flags & white_list) == 0);
|
||||
BOOST_CHECK(test_asset.options.max_supply == 100000000);
|
||||
BOOST_CHECK(!test_asset.bitasset_data_id.valid());
|
||||
BOOST_CHECK(test_asset.options.market_fee_percent == GRAPHENE_MAX_MARKET_FEE_PERCENT/100);
|
||||
|
|
@ -1144,7 +1144,7 @@ BOOST_AUTO_TEST_CASE( witness_feeds )
|
|||
asset_publish_feed_operation op;
|
||||
op.publisher = active_witnesses[0];
|
||||
op.asset_id = bit_usd.get_id();
|
||||
op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30));
|
||||
op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30));
|
||||
// Accept defaults for required collateral
|
||||
trx.operations.emplace_back(op);
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
|
@ -1154,7 +1154,7 @@ BOOST_AUTO_TEST_CASE( witness_feeds )
|
|||
BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);
|
||||
|
||||
op.publisher = active_witnesses[1];
|
||||
op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25));
|
||||
op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25));
|
||||
trx.operations.back() = op;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
|
|
@ -1162,7 +1162,7 @@ BOOST_AUTO_TEST_CASE( witness_feeds )
|
|||
BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);
|
||||
|
||||
op.publisher = active_witnesses[2];
|
||||
op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40));
|
||||
op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40));
|
||||
// But this witness is an idiot.
|
||||
op.feed.maintenance_collateral_ratio = 1001;
|
||||
trx.operations.back() = op;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <graphene/chain/asset_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/balance_object.hpp>
|
||||
#include <graphene/chain/budget_record_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/committee_member_object.hpp>
|
||||
#include <graphene/chain/market_evaluator.hpp>
|
||||
|
|
@ -348,7 +349,8 @@ BOOST_AUTO_TEST_CASE( mia_feeds )
|
|||
asset_publish_feed_operation op;
|
||||
op.publisher = vikram_id;
|
||||
op.asset_id = bit_usd_id;
|
||||
op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30));
|
||||
op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(30));
|
||||
|
||||
// We'll expire margins after a month
|
||||
// Accept defaults for required collateral
|
||||
trx.operations.emplace_back(op);
|
||||
|
|
@ -359,7 +361,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds )
|
|||
BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);
|
||||
|
||||
op.publisher = ben_id;
|
||||
op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25));
|
||||
op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(25));
|
||||
trx.operations.back() = op;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
|
|
@ -367,7 +369,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds )
|
|||
BOOST_CHECK(bitasset.current_feed.maintenance_collateral_ratio == GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO);
|
||||
|
||||
op.publisher = dan_id;
|
||||
op.feed.settlement_price = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40));
|
||||
op.feed.settlement_price = op.feed.core_exchange_rate = ~price(asset(GRAPHENE_BLOCKCHAIN_PRECISION),bit_usd.amount(40));
|
||||
op.feed.maximum_short_squeeze_ratio = 1001;
|
||||
op.feed.maintenance_collateral_ratio = 1001;
|
||||
trx.operations.back() = op;
|
||||
|
|
@ -452,15 +454,16 @@ BOOST_AUTO_TEST_CASE( witness_create )
|
|||
generate_block();
|
||||
|
||||
int produced = 0;
|
||||
// Make sure we get scheduled exactly once in witnesses.size() blocks
|
||||
// Make sure we get scheduled at least once in witnesses.size()*2 blocks
|
||||
// may take this many unless we measure where in the scheduling round we are
|
||||
// TODO: intense_test that repeats this loop many times
|
||||
for( size_t i=0; i<witnesses.size(); i++ )
|
||||
for( size_t i=0, n=witnesses.size()*2; i<n; i++ )
|
||||
{
|
||||
signed_block block = generate_block();
|
||||
if( block.witness == nathan_witness_id )
|
||||
produced++;
|
||||
}
|
||||
BOOST_CHECK_EQUAL( produced, 2 );
|
||||
BOOST_CHECK_GE( produced, 1 );
|
||||
} FC_LOG_AND_RETHROW() }
|
||||
|
||||
/**
|
||||
|
|
@ -1035,8 +1038,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
|
|||
fc::temp_directory td( graphene::utilities::temp_directory_path() );
|
||||
genesis_state.initial_balances.push_back({generate_private_key("n").get_public_key(), GRAPHENE_SYMBOL, 1});
|
||||
genesis_state.initial_balances.push_back({generate_private_key("x").get_public_key(), GRAPHENE_SYMBOL, 1});
|
||||
auto starting_time = time_point_sec((time_point::now().sec_since_epoch() / GRAPHENE_DEFAULT_BLOCK_INTERVAL + 1) *
|
||||
GRAPHENE_DEFAULT_BLOCK_INTERVAL);
|
||||
fc::time_point_sec starting_time = genesis_state.initial_timestamp + 3000;
|
||||
|
||||
auto n_key = generate_private_key("n");
|
||||
auto x_key = generate_private_key("x");
|
||||
|
|
@ -1206,6 +1208,121 @@ BOOST_AUTO_TEST_CASE(transfer_with_memo) {
|
|||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(zero_second_vbo)
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTOR(alice);
|
||||
// don't pay witnesses so we have some worker budget to work with
|
||||
|
||||
transfer(account_id_type(), alice_id, asset(int64_t(100000) * 1100 * 1000 * 1000));
|
||||
{
|
||||
asset_reserve_operation op;
|
||||
op.payer = alice_id;
|
||||
op.amount_to_reserve = asset(int64_t(100000) * 1000 * 1000 * 1000);
|
||||
transaction tx;
|
||||
tx.operations.push_back( op );
|
||||
set_expiration( db, tx );
|
||||
db.push_transaction( tx, database::skip_authority_check | database::skip_tapos_check | database::skip_transaction_signatures );
|
||||
}
|
||||
enable_fees();
|
||||
upgrade_to_lifetime_member(alice_id);
|
||||
generate_block();
|
||||
|
||||
auto check_vesting_1b = [&](vesting_balance_id_type vbid)
|
||||
{
|
||||
// this function checks that Alice can't draw any right now,
|
||||
// but one block later, she can withdraw it all.
|
||||
|
||||
vesting_balance_withdraw_operation withdraw_op;
|
||||
withdraw_op.vesting_balance = vbid;
|
||||
withdraw_op.owner = alice_id;
|
||||
withdraw_op.amount = asset(1);
|
||||
|
||||
signed_transaction withdraw_tx;
|
||||
withdraw_tx.operations.push_back( withdraw_op );
|
||||
sign(withdraw_tx, alice_private_key);
|
||||
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, withdraw_tx ), fc::exception );
|
||||
|
||||
generate_block();
|
||||
withdraw_tx = signed_transaction();
|
||||
withdraw_op.amount = asset(500);
|
||||
withdraw_tx.operations.push_back( withdraw_op );
|
||||
set_expiration( db, withdraw_tx );
|
||||
sign(withdraw_tx, alice_private_key);
|
||||
PUSH_TX( db, withdraw_tx );
|
||||
};
|
||||
|
||||
// This block creates a zero-second VBO with a vesting_balance_create_operation.
|
||||
{
|
||||
cdd_vesting_policy_initializer pinit;
|
||||
pinit.vesting_seconds = 0;
|
||||
|
||||
vesting_balance_create_operation create_op;
|
||||
create_op.creator = alice_id;
|
||||
create_op.owner = alice_id;
|
||||
create_op.amount = asset(500);
|
||||
create_op.policy = pinit;
|
||||
|
||||
signed_transaction create_tx;
|
||||
create_tx.operations.push_back( create_op );
|
||||
set_expiration( db, create_tx );
|
||||
sign(create_tx, alice_private_key);
|
||||
|
||||
processed_transaction ptx = PUSH_TX( db, create_tx );
|
||||
vesting_balance_id_type vbid = ptx.operation_results[0].get<object_id_type>();
|
||||
check_vesting_1b( vbid );
|
||||
}
|
||||
|
||||
// This block creates a zero-second VBO with a worker_create_operation.
|
||||
{
|
||||
worker_create_operation create_op;
|
||||
create_op.owner = alice_id;
|
||||
create_op.work_begin_date = db.head_block_time();
|
||||
create_op.work_end_date = db.head_block_time() + fc::days(1000);
|
||||
create_op.daily_pay = share_type( 10000 );
|
||||
create_op.name = "alice";
|
||||
create_op.url = "";
|
||||
create_op.initializer = vesting_balance_worker_initializer(0);
|
||||
signed_transaction create_tx;
|
||||
create_tx.operations.push_back(create_op);
|
||||
set_expiration( db, create_tx );
|
||||
sign(create_tx, alice_private_key);
|
||||
processed_transaction ptx = PUSH_TX( db, create_tx );
|
||||
worker_id_type wid = ptx.operation_results[0].get<object_id_type>();
|
||||
|
||||
// vote it in
|
||||
account_update_operation vote_op;
|
||||
vote_op.account = alice_id;
|
||||
vote_op.new_options = alice_id(db).options;
|
||||
vote_op.new_options->votes.insert(wid(db).vote_for);
|
||||
signed_transaction vote_tx;
|
||||
vote_tx.operations.push_back(vote_op);
|
||||
set_expiration( db, vote_tx );
|
||||
sign( vote_tx, alice_private_key );
|
||||
PUSH_TX( db, vote_tx );
|
||||
|
||||
// vote it in, wait for one maint. for vote to take effect
|
||||
vesting_balance_id_type vbid = wid(db).worker.get<vesting_balance_worker_type>().balance;
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
generate_block();
|
||||
// wait for another maint. for worker to be paid
|
||||
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
|
||||
BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(0) );
|
||||
generate_block();
|
||||
BOOST_CHECK( vbid(db).get_allowed_withdraw(db.head_block_time()) == asset(10000) );
|
||||
|
||||
/*
|
||||
db.get_index_type< simple_index<budget_record_object> >().inspect_all_objects(
|
||||
[&](const object& o)
|
||||
{
|
||||
ilog( "budget: ${brec}", ("brec", static_cast<const budget_record_object&>(o)) );
|
||||
});
|
||||
*/
|
||||
}
|
||||
} FC_LOG_AND_RETHROW()
|
||||
}
|
||||
|
||||
// TODO: Write linear VBO tests
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/exceptions.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/asset_object.hpp>
|
||||
|
|
@ -57,7 +58,7 @@ BOOST_AUTO_TEST_CASE( create_advanced_uia )
|
|||
const asset_object& test_asset = test_asset_id(db);
|
||||
BOOST_CHECK(test_asset.symbol == "ADVANCED");
|
||||
BOOST_CHECK(asset(1, test_asset_id) * test_asset.options.core_exchange_rate == asset(2));
|
||||
BOOST_CHECK(test_asset.enforce_white_list());
|
||||
BOOST_CHECK(test_asset.options.flags & white_list);
|
||||
BOOST_CHECK(test_asset.options.max_supply == 100000000);
|
||||
BOOST_CHECK(!test_asset.bitasset_data_id.valid());
|
||||
BOOST_CHECK(test_asset.options.market_fee_percent == GRAPHENE_MAX_MARKET_FEE_PERCENT/100);
|
||||
|
|
@ -134,33 +135,75 @@ BOOST_AUTO_TEST_CASE( override_transfer_test2 )
|
|||
BOOST_AUTO_TEST_CASE( issue_whitelist_uia )
|
||||
{
|
||||
try {
|
||||
INVOKE(create_advanced_uia);
|
||||
const asset_object& advanced = get_asset("ADVANCED");
|
||||
const account_object& nathan = create_account("nathan");
|
||||
upgrade_to_lifetime_member(nathan);
|
||||
account_id_type izzy_id = create_account("izzy").id;
|
||||
const asset_id_type uia_id = create_user_issued_asset(
|
||||
"ADVANCED", izzy_id(db), white_list ).id;
|
||||
account_id_type nathan_id = create_account("nathan").id;
|
||||
account_id_type vikram_id = create_account("vikram").id;
|
||||
trx.clear();
|
||||
|
||||
asset_issue_operation op;
|
||||
op.issuer = advanced.issuer;
|
||||
op.asset_to_issue = advanced.amount(1000);
|
||||
op.issue_to_account = nathan.id; //({asset(), advanced.issuer, advanced.amount(1000), nathan.id});
|
||||
op.issuer = uia_id(db).issuer;
|
||||
op.asset_to_issue = asset(1000, uia_id);
|
||||
op.issue_to_account = nathan_id;
|
||||
trx.operations.emplace_back(op);
|
||||
//Fail because nathan is not whitelisted.
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
|
||||
set_expiration( db, trx );
|
||||
//Fail because nathan is not whitelisted, but only before hardfork time
|
||||
if( db.head_block_time() <= HARDFORK_415_TIME )
|
||||
{
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
|
||||
generate_blocks( HARDFORK_415_TIME );
|
||||
generate_block();
|
||||
set_expiration( db, trx );
|
||||
}
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
BOOST_CHECK(nathan_id(db).is_authorized_asset(uia_id(db), db));
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000);
|
||||
|
||||
// Make a whitelist, now it should fail
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Changing the whitelist authority" );
|
||||
asset_update_operation uop;
|
||||
uop.issuer = izzy_id;
|
||||
uop.asset_to_update = uia_id;
|
||||
uop.new_options = uia_id(db).options;
|
||||
uop.new_options.whitelist_authorities.insert(izzy_id);
|
||||
trx.operations.back() = uop;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
BOOST_CHECK( uia_id(db).options.whitelist_authorities.find(izzy_id) != uia_id(db).options.whitelist_authorities.end() );
|
||||
}
|
||||
|
||||
// Fail because there is a whitelist authority and I'm not whitelisted
|
||||
trx.operations.back() = op;
|
||||
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );
|
||||
|
||||
account_whitelist_operation wop;
|
||||
wop.authorizing_account = account_id_type();
|
||||
wop.account_to_list = nathan.id;
|
||||
wop.authorizing_account = izzy_id;
|
||||
wop.account_to_list = vikram_id;
|
||||
wop.new_listing = account_whitelist_operation::white_listed;
|
||||
|
||||
trx.operations.back() = wop;
|
||||
// Fail because whitelist function is restricted to members only
|
||||
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );
|
||||
upgrade_to_lifetime_member( izzy_id );
|
||||
trx.operations.clear();
|
||||
trx.operations.push_back( wop );
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
BOOST_CHECK(nathan.is_authorized_asset(advanced));
|
||||
// Still fail after an irrelevant account was added
|
||||
trx.operations.back() = op;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, ~0 ), fc::exception );
|
||||
|
||||
wop.account_to_list = nathan_id;
|
||||
trx.operations.back() = wop;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
trx.operations.back() = op;
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 1000);
|
||||
// Finally succeed when we were whitelisted
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan_id, uia_id), 2000);
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1000);
|
||||
} catch(fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
|
|
@ -174,6 +217,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
|
|||
const asset_object& advanced = get_asset("ADVANCED");
|
||||
const account_object& nathan = get_account("nathan");
|
||||
const account_object& dan = create_account("dan");
|
||||
account_id_type izzy_id = get_account("izzy").id;
|
||||
upgrade_to_lifetime_member(dan);
|
||||
trx.clear();
|
||||
|
||||
|
|
@ -189,30 +233,51 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
|
|||
|
||||
BOOST_TEST_MESSAGE( "Adding dan to whitelist for asset ADVANCED" );
|
||||
account_whitelist_operation wop;
|
||||
wop.authorizing_account = account_id_type();
|
||||
wop.authorizing_account = izzy_id;
|
||||
wop.account_to_list = dan.id;
|
||||
wop.new_listing = account_whitelist_operation::white_listed;
|
||||
trx.operations.back() = wop;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
BOOST_TEST_MESSAGE( "Attempting to trnsfer from nathan to dan after whitelisting dan, should succeed" );
|
||||
BOOST_TEST_MESSAGE( "Attempting to transfer from nathan to dan after whitelisting dan, should succeed" );
|
||||
trx.operations.back() = op;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 900);
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1900);
|
||||
BOOST_CHECK_EQUAL(get_balance(dan, advanced), 100);
|
||||
|
||||
BOOST_TEST_MESSAGE( "Attempting to blacklist nathan" );
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Changing the blacklist authority" );
|
||||
asset_update_operation uop;
|
||||
uop.issuer = izzy_id;
|
||||
uop.asset_to_update = advanced.id;
|
||||
uop.new_options = advanced.options;
|
||||
uop.new_options.blacklist_authorities.insert(izzy_id);
|
||||
trx.operations.back() = uop;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
BOOST_CHECK( advanced.options.blacklist_authorities.find(izzy_id) != advanced.options.blacklist_authorities.end() );
|
||||
}
|
||||
|
||||
wop.new_listing |= account_whitelist_operation::black_listed;
|
||||
wop.account_to_list = nathan.id;
|
||||
trx.operations.back() = wop;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
BOOST_CHECK( !(nathan.is_authorized_asset(advanced, db)) );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Attempting to transfer from nathan after blacklisting, should fail" );
|
||||
op.amount = advanced.amount(50);
|
||||
trx.operations.back() = op;
|
||||
//Fail because nathan is blacklisted
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), transfer_from_account_not_whitelisted );
|
||||
|
||||
if( db.head_block_time() <= HARDFORK_419_TIME )
|
||||
{
|
||||
// before the hardfork time, it fails because the whitelist check fails
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), transfer_from_account_not_whitelisted );
|
||||
}
|
||||
else
|
||||
{
|
||||
// after the hardfork time, it fails because the fees are not in a whitelisted asset
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception );
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE( "Attempting to burn from nathan after blacklisting, should fail" );
|
||||
asset_reserve_operation burn;
|
||||
|
|
@ -230,6 +295,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
|
|||
{
|
||||
BOOST_TEST_MESSAGE( "Changing the blacklist authority to dan" );
|
||||
asset_update_operation op;
|
||||
op.issuer = izzy_id;
|
||||
op.asset_to_update = advanced.id;
|
||||
op.new_options = advanced.options;
|
||||
op.new_options.blacklist_authorities.clear();
|
||||
|
|
@ -242,7 +308,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
|
|||
BOOST_TEST_MESSAGE( "Attempting to transfer from dan back to nathan" );
|
||||
trx.operations.back() = op;
|
||||
PUSH_TX( db, trx, ~0 );
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 950);
|
||||
BOOST_CHECK_EQUAL(get_balance(nathan, advanced), 1950);
|
||||
BOOST_CHECK_EQUAL(get_balance(dan, advanced), 50);
|
||||
|
||||
BOOST_TEST_MESSAGE( "Blacklisting nathan by dan" );
|
||||
|
|
@ -254,11 +320,11 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
|
|||
|
||||
trx.operations.back() = op;
|
||||
//Fail because nathan is blacklisted
|
||||
BOOST_CHECK(!nathan.is_authorized_asset(advanced));
|
||||
BOOST_CHECK(!nathan.is_authorized_asset(advanced, db));
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
|
||||
|
||||
//Remove nathan from committee's whitelist, add him to dan's. This should not authorize him to hold ADVANCED.
|
||||
wop.authorizing_account = account_id_type();
|
||||
wop.authorizing_account = izzy_id;
|
||||
wop.account_to_list = nathan.id;
|
||||
wop.new_listing = account_whitelist_operation::no_listing;
|
||||
trx.operations.back() = wop;
|
||||
|
|
@ -271,7 +337,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
|
|||
|
||||
trx.operations.back() = op;
|
||||
//Fail because nathan is not whitelisted
|
||||
BOOST_CHECK(!nathan.is_authorized_asset(advanced));
|
||||
BOOST_CHECK(!nathan.is_authorized_asset(advanced, db));
|
||||
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx, ~0 ), fc::exception);
|
||||
|
||||
burn.payer = dan.id;
|
||||
|
|
@ -356,5 +422,52 @@ BOOST_AUTO_TEST_CASE( transfer_restricted_test )
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( asset_name_test )
|
||||
{
|
||||
try
|
||||
{
|
||||
ACTORS( (alice)(bob) );
|
||||
|
||||
auto has_asset = [&]( std::string symbol ) -> bool
|
||||
{
|
||||
const auto& assets_by_symbol = db.get_index_type<asset_index>().indices().get<by_symbol>();
|
||||
return assets_by_symbol.find( symbol ) != assets_by_symbol.end();
|
||||
};
|
||||
|
||||
// Alice creates asset "ALPHA"
|
||||
BOOST_CHECK( !has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
create_user_issued_asset( "ALPHA", alice_id(db), 0 );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
|
||||
// Nobody can create another asset named ALPHA
|
||||
GRAPHENE_REQUIRE_THROW( create_user_issued_asset( "ALPHA", bob_id(db), 0 ), fc::exception );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
GRAPHENE_REQUIRE_THROW( create_user_issued_asset( "ALPHA", alice_id(db), 0 ), fc::exception );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
|
||||
// Bob can't create ALPHA.ONE
|
||||
GRAPHENE_REQUIRE_THROW( create_user_issued_asset( "ALPHA.ONE", bob_id(db), 0 ), fc::exception );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
if( db.head_block_time() <= HARDFORK_409_TIME )
|
||||
{
|
||||
// Alice can't create ALPHA.ONE before hardfork
|
||||
GRAPHENE_REQUIRE_THROW( create_user_issued_asset( "ALPHA.ONE", alice_id(db), 0 ), fc::exception );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
generate_blocks( HARDFORK_409_TIME );
|
||||
generate_block();
|
||||
// Bob can't create ALPHA.ONE after hardfork
|
||||
GRAPHENE_REQUIRE_THROW( create_user_issued_asset( "ALPHA.ONE", bob_id(db), 0 ), fc::exception );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( !has_asset("ALPHA.ONE") );
|
||||
}
|
||||
// Alice can create it
|
||||
create_user_issued_asset( "ALPHA.ONE", alice_id(db), 0 );
|
||||
BOOST_CHECK( has_asset("ALPHA") ); BOOST_CHECK( has_asset("ALPHA.ONE") );
|
||||
}
|
||||
catch(fc::exception& e)
|
||||
{
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
|||
Loading…
Reference in a new issue