Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement #583 percentage based transfer fee and #603 coin-days based free transaction #605

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e77c04b
Initial commit of percentage transfer fee
abitmore Jan 15, 2016
54d0d7b
Fix compiler errors
abitmore Jan 15, 2016
8449426
Refactor: use less visitors
abitmore Jan 15, 2016
f9f0a13
Fix get_required_fees(create_proposal_operation)
abitmore Jan 15, 2016
40fdd20
Implement transfer fee check and pay and split
abitmore Jan 16, 2016
bff6d5b
Fix more compiler errors
abitmore Jan 16, 2016
7c749b0
Add transfer_v2_operation
abitmore Jan 18, 2016
b75d8d6
Fix calculate_fee(transfer_op,...) issue
abitmore Jan 18, 2016
ee33985
Fix compiler errors again
abitmore Jan 18, 2016
2cbab26
Change attribute name 'fee_mode' to 'transfer_fee_mode'
abitmore Jan 18, 2016
05c5f01
Fix set_operation_fees
abitmore Jan 18, 2016
57965f8
Register transfer_v2_evaluator
abitmore Jan 18, 2016
a23bf43
Fix order of percentage calculation
abitmore Jan 18, 2016
b398c80
Add future extensions to transfer_v2_op::fee_params
abitmore Jan 25, 2016
b05c06c
Refactor: rename struct transfer_fee_options to transfer_fee_mode_opt…
abitmore Jan 25, 2016
9613720
Refactor: remove unnecessarily CER price flipping
abitmore Jan 26, 2016
52e14cb
Add hard fork code for BSIP10(#583); refactor: change some of fee par…
abitmore Feb 1, 2016
11f0262
Fix set_fees_on_builder_transaction
abitmore Feb 1, 2016
ecd3b25
Bugfix for transfer_v2_evaluator::pay_fee(op) : if fee is not defined…
abitmore Feb 2, 2016
78f4c6e
Make percentage not scale
abitmore Feb 2, 2016
4f45f5c
Fix compiler errors
abitmore Feb 2, 2016
a300500
Transfer_v2_evaluator::pay_fee : scale network cut
abitmore Feb 2, 2016
19b8393
Refactor: rename pending_fees_to_network and etc
abitmore Feb 2, 2016
b93639f
Implement asset_object::set_transfer_fee_mode(mode)
abitmore Feb 10, 2016
05aeeb1
Fix transfer_v2_operation::calculate_fee(,CORE_ASSET,) issue
abitmore Feb 10, 2016
5da0bf0
Real fix for transfer_v2_operation::calculate_fee(,CORE_ASSET,) issue
abitmore Feb 10, 2016
83ebf42
Fix error log issue in database::push_proposal
abitmore Feb 11, 2016
7e3db39
pay_fee_pre_split_network(): allow total_fee < network_fee, in this c…
abitmore Feb 14, 2016
24b13d8
Fix compiler errors and warnings
abitmore Feb 23, 2016
3d6b988
Add programs/build_helpers/cat-parts to .gitignore
abitmore Feb 23, 2016
aabda83
Compile fixes for win32 and out-of-source builds
emfrias Feb 17, 2016
04d3936
Fix gcc/clang builds broken by d3c6cbf4de72a8d8003ffdb178e6ec32060620…
emfrias Feb 23, 2016
f2ee75a
Merge branch 'graphene/develop' into dev-transfer-fee
abitmore Feb 25, 2016
d90b563
Refactor: remove transfer_op etc from generic_evaluator
abitmore Feb 25, 2016
240947f
Refactor: use template to extend fee calculation, call extended funct…
abitmore Feb 28, 2016
e9ed5ca
Code clean up
abitmore Feb 28, 2016
30c07da
Update asset_update_operation: new_options can now have a null CER, i…
abitmore Jan 26, 2016
c2cf914
Implement committee_member_update_core_asset_operation
abitmore Feb 10, 2016
09edab6
Initial commit of limited free transactions feature
abitmore Feb 14, 2016
522f6a8
Fix compiler errors, and refactor
abitmore Feb 14, 2016
a2de076
Update coin_seconds_earned and etc when creating new balance object
abitmore Feb 14, 2016
2ee1c74
Expand insufficient fee error message.
abitmore Feb 14, 2016
1261bec
Fix compiler errors
abitmore Mar 1, 2016
72edfd2
Add hard fork logic for #603 rate limited free trx
abitmore Mar 1, 2016
e7e26b9
Refactor: move call of pay_fee_with_coin_seconds() out of pay_fee(); …
abitmore Mar 1, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ libraries/wallet/Doxyfile
libraries/wallet/api_documentation.cpp
libraries/wallet/doxygen

programs/build_helpers/cat-parts
programs/cli_wallet/cli_wallet
programs/js_operation_serializer/js_operation_serializer
programs/witness_node/witness_node
Expand All @@ -39,3 +40,8 @@ object_database/*

*.pyc
*.pyo

libraries/egenesis/egenesis_brief.cpp
libraries/egenesis/egenesis_full.cpp
libraries/egenesis/embed_genesis

27 changes: 25 additions & 2 deletions libraries/app/database_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
bool verify_account_authority( const string& name_or_id, const flat_set<public_key_type>& signers )const;
processed_transaction validate_transaction( const signed_transaction& trx )const;
vector< fc::variant > get_required_fees( const vector<operation>& ops, asset_id_type id )const;
asset get_operation_fee( const operation& op, const asset_id_type id = asset_id_type(0) )const;

// Proposed transactions
vector<proposal_object> get_proposed_transactions( account_id_type id )const;
Expand Down Expand Up @@ -1626,18 +1627,25 @@ vector< fc::variant > database_api::get_required_fees( const vector<operation>&
return my->get_required_fees( ops, id );
}

asset database_api::get_operation_fee( const operation& op, const asset_id_type id )const
{
return my->get_operation_fee( op, id );
}

/**
* Container method for mutually recursive functions used to
* implement get_required_fees() with potentially nested proposals.
*/
struct get_required_fees_helper
{
get_required_fees_helper(
graphene::chain::database& _db,
const fee_schedule& _current_fee_schedule,
const price& _core_exchange_rate,
uint32_t _max_recursion
)
: current_fee_schedule(_current_fee_schedule),
: db(_db),
current_fee_schedule(_current_fee_schedule),
core_exchange_rate(_core_exchange_rate),
max_recursion(_max_recursion)
{}
Expand All @@ -1650,7 +1658,9 @@ struct get_required_fees_helper
}
else
{
asset fee = current_fee_schedule.set_fee( op, core_exchange_rate );
fc::variant extended_fee_parameters;
db.build_extended_fee_parameters( op, extended_fee_parameters );
asset fee = current_fee_schedule.set_fee_extended( op, extended_fee_parameters, core_exchange_rate );
fc::variant result;
fc::to_variant( fee, result );
return result;
Expand All @@ -1676,6 +1686,7 @@ struct get_required_fees_helper
return vresult;
}

graphene::chain::database& db;
const fee_schedule& current_fee_schedule;
const price& core_exchange_rate;
uint32_t max_recursion;
Expand All @@ -1694,6 +1705,7 @@ vector< fc::variant > database_api_impl::get_required_fees( const vector<operati
result.reserve(ops.size());
const asset_object& a = id(_db);
get_required_fees_helper helper(
_db,
_db.current_fee_schedule(),
a.options.core_exchange_rate,
GET_REQUIRED_FEES_MAX_RECURSION );
Expand All @@ -1704,6 +1716,17 @@ vector< fc::variant > database_api_impl::get_required_fees( const vector<operati
return result;
}

asset database_api_impl::get_operation_fee( const operation& op, const asset_id_type id )const
{
// we copy the op because we need to mutate an operation to reliably
// determine its fee
operation _op = op;
fc::variant extended_fee_parameters;
_db.build_extended_fee_parameters( _op, extended_fee_parameters );
const asset_object& a = id(_db);
return _db.current_fee_schedule().set_fee_extended( _op, extended_fee_parameters, a.options.core_exchange_rate );
}

//////////////////////////////////////////////////////////////////////
// //
// Proposed transactions //
Expand Down
6 changes: 6 additions & 0 deletions libraries/app/impacted.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ struct get_impacted_account_visitor
_impacted.insert( op.to );
}

void operator()( const transfer_v2_operation& op )
{
_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 )
Expand Down Expand Up @@ -152,6 +157,7 @@ struct get_impacted_account_visitor
_impacted.insert( op.committee_member_account );
}
void operator()( const committee_member_update_global_parameters_operation& op ) {}
void operator()( const committee_member_update_core_asset_operation& op ) {}

void operator()( const vesting_balance_create_operation& op )
{
Expand Down
8 changes: 8 additions & 0 deletions libraries/app/include/graphene/app/database_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,13 @@ class database_api
*/
vector< fc::variant > get_required_fees( const vector<operation>& ops, asset_id_type id )const;

/**
* Get minimum fee required by the given operation. Won't recursively check if the operation
* is a proposal.
* @return the fee
*/
asset get_operation_fee( const operation& op, const asset_id_type id = asset_id_type(0) )const;

///////////////////////////
// Proposed transactions //
///////////////////////////
Expand Down Expand Up @@ -642,6 +649,7 @@ FC_API(graphene::app::database_api,
(verify_account_authority)
(validate_transaction)
(get_required_fees)
(get_operation_fee)

// Proposed transactions
(get_proposed_transactions)
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if( GRAPHENE_DISABLE_UNITY_BUILD )
db_balance.cpp
db_block.cpp
db_debug.cpp
db_fee.cpp
db_getter.cpp
db_init.cpp
db_maint.cpp
Expand Down
117 changes: 115 additions & 2 deletions libraries/chain/account_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

namespace graphene { namespace chain {

/// @return a * p% / 100%
share_type cut_fee(share_type a, uint16_t p)
{
if( a == 0 || p == 0 )
Expand All @@ -42,6 +43,20 @@ share_type cut_fee(share_type a, uint16_t p)
return r.to_uint64();
}

/// @return a * p% / t%
share_type cut_fee(share_type a, uint16_t p, uint16_t t)
{
if( a == 0 || p == 0 )
return 0;
if( p >= t )
return a;

fc::uint128 r(a.value);
r *= p;
r /= t;
return r.to_uint64();
}

void account_balance_object::adjust_balance(const asset& delta)
{
assert(delta.asset_id == asset_type);
Expand All @@ -50,8 +65,11 @@ void account_balance_object::adjust_balance(const asset& delta)

void account_statistics_object::process_fees(const account_object& a, database& d) const
{
if( pending_fees > 0 || pending_vested_fees > 0 )
if( pending_fees > 0 || pending_vested_fees > 0
|| pending_fees_to_network > 0 || pending_fees_to_non_network > 0
|| pending_vested_fees_to_non_network > 0)
{
// split pending fees among network, lifetime referrer, registrar, referrer
auto pay_out_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting)
{
// Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead.
Expand Down Expand Up @@ -94,10 +112,65 @@ void account_statistics_object::process_fees(const account_object& a, database&
pay_out_fees(a, pending_fees, true);
pay_out_fees(a, pending_vested_fees, false);


// pay pending network fees to network
auto pay_out_network_fees = [&]( share_type network_fee_total )
{

#ifndef NDEBUG
const auto& props = d.get_global_properties();

share_type reserved = cut_fee(network_fee_total, props.parameters.reserve_percent_of_fee);
share_type accumulated = network_fee_total - reserved;
assert( accumulated + reserved == network_fee_total );
#endif
d.modify(asset_dynamic_data_id_type()(d), [network_fee_total](asset_dynamic_data_object& d) {
d.accumulated_fees += network_fee_total;
});
};

pay_out_network_fees(pending_fees_to_network);


// split pending non-network fees among lifetime referrer, registrar, referrer
auto pay_out_non_network_fees = [&](const account_object& account, share_type core_fee_total, bool require_vesting)
{
// Check the referrer -- if he's no longer a member, pay to the lifetime referrer instead.
// No need to check the registrar; registrars are required to be lifetime members.
if( account.referrer(d).is_basic_account(d.head_block_time()) )
d.modify(account, [](account_object& a) {
a.referrer = a.lifetime_referrer;
});

share_type lifetime_cut = cut_fee(core_fee_total,
account.lifetime_referrer_fee_percentage,
GRAPHENE_100_PERCENT - account.network_fee_percentage);
share_type referral = core_fee_total - lifetime_cut;

// Potential optimization: Skip some of this math and object lookups by special casing on the account type.
// For example, if the account is a lifetime member, we can skip all this and just deposit the referral to
// it directly.
share_type referrer_cut = cut_fee(referral, account.referrer_rewards_percentage);
share_type registrar_cut = referral - referrer_cut;

d.deposit_cashback(d.get(account.lifetime_referrer), lifetime_cut, require_vesting);
d.deposit_cashback(d.get(account.referrer), referrer_cut, require_vesting);
d.deposit_cashback(d.get(account.registrar), registrar_cut, require_vesting);

assert( referrer_cut + registrar_cut + lifetime_cut == core_fee_total );
};

pay_out_non_network_fees(a, pending_fees_to_non_network, true);
pay_out_non_network_fees(a, pending_vested_fees_to_non_network, false);

d.modify(*this, [&](account_statistics_object& s) {
s.lifetime_fees_paid += pending_fees + pending_vested_fees;
s.lifetime_fees_paid += ( pending_fees + pending_vested_fees + pending_fees_to_network
+ pending_fees_to_non_network + pending_vested_fees_to_non_network );
s.pending_fees = 0;
s.pending_vested_fees = 0;
s.pending_fees_to_network = 0;
s.pending_fees_to_non_network = 0;
s.pending_vested_fees_to_non_network = 0;
});
}
}
Expand All @@ -110,6 +183,46 @@ void account_statistics_object::pay_fee( share_type core_fee, share_type cashbac
pending_vested_fees += core_fee;
}

void account_statistics_object::pay_fee_pre_split_network( share_type core_fee,
share_type cashback_vesting_threshold,
share_type network_fee )
{
if( core_fee <= 0 ) return;
share_type new_network_fee = network_fee;
if( core_fee < network_fee ) new_network_fee = core_fee;
pending_fees_to_network += new_network_fee;
if( core_fee > cashback_vesting_threshold )
pending_fees_to_non_network += ( core_fee - new_network_fee );
else
pending_vested_fees_to_non_network += ( core_fee - new_network_fee );
}

fc::uint128_t account_statistics_object::compute_coin_seconds_earned(const asset& balance, fc::time_point_sec now)const
{
if( now <= coin_seconds_earned_last_update )
return coin_seconds_earned;
int64_t delta_seconds = (now - coin_seconds_earned_last_update).to_seconds();

fc::uint128_t delta_coin_seconds = balance.amount.value;
delta_coin_seconds *= delta_seconds;

return (coin_seconds_earned + delta_coin_seconds);
}

void account_statistics_object::update_coin_seconds_earned(const asset& balance, fc::time_point_sec now)
{
if( now <= coin_seconds_earned_last_update )
return;
coin_seconds_earned = compute_coin_seconds_earned(balance, now);
coin_seconds_earned_last_update = now;
}

void account_statistics_object::set_coin_seconds_earned(const fc::uint128_t new_coin_seconds, fc::time_point_sec now)
{
coin_seconds_earned = new_coin_seconds;
coin_seconds_earned_last_update = now;
}

set<account_id_type> account_member_index::get_account_members(const account_object& a)const
{
set<account_id_type> result;
Expand Down
33 changes: 32 additions & 1 deletion libraries/chain/asset_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
FC_ASSERT( op.precision == op.bitasset_opts->short_backing_asset(d).precision );
}

// #583 BSIP10 hard fork check
if( d.head_block_time() <= HARDFORK_583_TIME )
{
for( const auto& e : op.common_options.extensions )
{
FC_ASSERT( e.which() != asset_options::future_extensions::tag<asset_options::ext::transfer_fee_mode_options>::value,
"Asset ${s} has an extension which requires hardfork #583.", ("s",op.symbol) );
}
}

return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }

Expand Down Expand Up @@ -233,6 +243,13 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
const asset_object& a = o.asset_to_update(d);
auto a_copy = a;
a_copy.options = o.new_options;
// check whether hard fork time passed
if( d.head_block_time() > HARDFORK_583_TIME )
{
// if new options contain a null CER, replace it with current CER before validate
if( o.new_options.core_exchange_rate.is_null() )
a_copy.options.core_exchange_rate = a.options.core_exchange_rate;
}
a_copy.validate();

if( o.new_issuer )
Expand Down Expand Up @@ -275,6 +292,16 @@ void_result asset_update_evaluator::do_evaluate(const asset_update_operation& o)
for( auto id : o.new_options.blacklist_authorities )
d.get_object(id);

// #583 BSIP10 hard fork check
if( d.head_block_time() <= HARDFORK_583_TIME )
{
for( const auto& e : o.new_options.extensions )
{
FC_ASSERT( e.which() != asset_options::future_extensions::tag<asset_options::ext::transfer_fee_mode_options>::value,
"Asset ${s} has an extension which requires hardfork #583.", ("s",a.symbol) );
}
}

return void_result();
} FC_CAPTURE_AND_RETHROW((o)) }

Expand All @@ -297,7 +324,11 @@ void_result asset_update_evaluator::do_apply(const asset_update_operation& o)
d.modify(*asset_to_update, [&](asset_object& a) {
if( o.new_issuer )
a.issuer = *o.new_issuer;
a.options = o.new_options;
// make a copy of new_options; if CER is null, replace it with current CER
auto copy_new_options = o.new_options;
if( copy_new_options.core_exchange_rate.is_null() )
copy_new_options.core_exchange_rate = a.options.core_exchange_rate;
a.options = copy_new_options;
});

return void_result();
Expand Down
Loading