Progress #31: Initial work on new cashback system

Lots still to do. There's no longer a way to upgrade an account, genesis
can't evaluate, and who knows how many tests are failing... But it
builds!
This commit is contained in:
Nathan Hourt 2015-06-09 16:46:00 -04:00
parent fa26e39e10
commit ed7d485df3
21 changed files with 337 additions and 278 deletions

View file

@ -24,22 +24,13 @@ namespace graphene { namespace chain {
void_result account_create_evaluator::do_evaluate( const account_create_operation& op ) void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try { { try {
FC_ASSERT( db().find_object(op.voting_account) ); database& d = db();
FC_ASSERT( is_relative(op.memo_key) || db().find_object(op.memo_key) ); FC_ASSERT( d.find_object(op.voting_account) );
FC_ASSERT( is_relative(op.memo_key) || d.find_object(op.memo_key) );
FC_ASSERT( fee_paying_account->is_lifetime_member() );
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()) );
if( fee_paying_account->is_prime() ) const auto& global_props = d.get_global_properties();
{
FC_ASSERT( op.referrer(db()).is_prime() );
}
else
{
FC_ASSERT( op.referrer == fee_paying_account->referrer );
FC_ASSERT( op.referrer_percent == fee_paying_account->referrer_percent, "",
("op",op)
("fee_paying_account->referral_percent",fee_paying_account->referrer_percent) );
}
const auto& global_props = db().get_global_properties();
uint32_t max_vote_id = global_props.next_available_vote_id; uint32_t max_vote_id = global_props.next_available_vote_id;
const auto& chain_params = global_props.parameters; const auto& chain_params = global_props.parameters;
FC_ASSERT( op.num_witness <= chain_params.maximum_witness_count ); FC_ASSERT( op.num_witness <= chain_params.maximum_witness_count );
@ -48,11 +39,11 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
FC_ASSERT( op.active.auths.size() <= chain_params.maximum_authority_membership ); FC_ASSERT( op.active.auths.size() <= chain_params.maximum_authority_membership );
for( auto id : op.owner.auths ) for( auto id : op.owner.auths )
{ {
FC_ASSERT( is_relative(id.first) || db().find<object>(id.first) ); FC_ASSERT( is_relative(id.first) || d.find_object(id.first) );
} }
for( auto id : op.active.auths ) for( auto id : op.active.auths )
{ {
FC_ASSERT( is_relative(id.first) || db().find<object>(id.first) ); FC_ASSERT( is_relative(id.first) || d.find_object(id.first) );
} }
safe<uint32_t> counts[vote_id_type::VOTE_TYPE_COUNT]; safe<uint32_t> counts[vote_id_type::VOTE_TYPE_COUNT];
for( auto id : op.vote ) for( auto id : op.vote )
@ -67,7 +58,7 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
"", "",
("count", counts[vote_id_type::committee])("num", op.num_committee)); ("count", counts[vote_id_type::committee])("num", op.num_committee));
auto& acnt_indx = db().get_index_type<account_index>(); auto& acnt_indx = d.get_index_type<account_index>();
if( op.name.size() ) if( op.name.size() )
{ {
auto current_account_itr = acnt_indx.indices().get<by_name>().find( op.name ); auto current_account_itr = acnt_indx.indices().get<by_name>().find( op.name );
@ -76,7 +67,7 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
// TODO: this check can be removed after GRAPHENE_LEGACY_NAME_IMPORT_PERIOD // TODO: this check can be removed after GRAPHENE_LEGACY_NAME_IMPORT_PERIOD
// legacy account check // legacy account check
if( db().get_dynamic_global_properties().head_block_number < GRAPHENE_LEGACY_NAME_IMPORT_PERIOD ) if( d.get_dynamic_global_properties().head_block_number < GRAPHENE_LEGACY_NAME_IMPORT_PERIOD )
{ {
auto legacy_account_itr = acnt_indx.indices().get<by_name>().find( "bts-"+op.name ); auto legacy_account_itr = acnt_indx.indices().get<by_name>().find( "bts-"+op.name );
if( legacy_account_itr != acnt_indx.indices().get<by_name>().end() ) if( legacy_account_itr != acnt_indx.indices().get<by_name>().end() )
@ -109,18 +100,15 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
}); });
const auto& new_acnt_object = db().create<account_object>( [&]( account_object& obj ){ const auto& new_acnt_object = db().create<account_object>( [&]( account_object& obj ){
if( fee_paying_account->is_prime() ) obj.registrar = o.registrar;
{ obj.referrer = o.referrer;
obj.registrar = o.registrar; obj.lifetime_referrer = o.referrer(db()).lifetime_referrer;
obj.referrer = o.referrer;
obj.referrer_percent = o.referrer_percent; auto& params = db().get_global_properties().parameters;
} obj.network_fee_percentage = params.network_percent_of_fee;
else obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee;
{ obj.referrer_rewards_percentage = o.referrer_percent;
obj.registrar = fee_paying_account->registrar;
obj.referrer = fee_paying_account->referrer;
obj.referrer_percent = fee_paying_account->referrer_percent;
}
obj.name = o.name; obj.name = o.name;
obj.owner = owner; obj.owner = owner;
obj.active = active; obj.active = active;
@ -138,7 +126,7 @@ object_id_type account_create_evaluator::do_apply( const account_create_operatio
void_result account_update_evaluator::do_evaluate( const account_update_operation& o ) void_result account_update_evaluator::do_evaluate( const account_update_operation& o )
{ {
database& d = db(); database& d = db();
FC_ASSERT( !o.memo_key || is_relative(*o.memo_key) || db().find_object(*o.memo_key) ); FC_ASSERT( !o.memo_key || is_relative(*o.memo_key) || db().find_object(*o.memo_key) );
@ -163,7 +151,6 @@ void_result account_update_evaluator::do_evaluate( const account_update_operatio
} }
acnt = &o.account(d); acnt = &o.account(d);
if( o.upgrade_to_prime ) FC_ASSERT( !acnt->is_prime() );
if( o.vote ) if( o.vote )
{ {
@ -184,11 +171,6 @@ void_result account_update_evaluator::do_apply( const account_update_operation&
if( o.voting_account ) a.voting_account = *o.voting_account; if( o.voting_account ) a.voting_account = *o.voting_account;
if( o.memo_key ) a.memo_key = *o.memo_key; if( o.memo_key ) a.memo_key = *o.memo_key;
if( o.vote ) a.votes = *o.vote; if( o.vote ) a.votes = *o.vote;
if( o.upgrade_to_prime )
{
a.referrer_percent = 100;
a.referrer = a.id;
}
a.num_witness = o.num_witness; a.num_witness = o.num_witness;
a.num_committee = o.num_committee; a.num_committee = o.num_committee;
}); });
@ -200,8 +182,8 @@ void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_ope
database& d = db(); database& d = db();
listed_account = &o.account_to_list(d); listed_account = &o.account_to_list(d);
if( !d.get_global_properties().parameters.allow_non_prime_whitelists ) if( !d.get_global_properties().parameters.allow_non_member_whitelists )
FC_ASSERT(listed_account->is_prime()); FC_ASSERT(o.authorizing_account(d).is_lifetime_member());
return void_result(); return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) } } FC_CAPTURE_AND_RETHROW( (o) ) }

View file

@ -174,74 +174,6 @@ void database::update_active_delegates()
}); });
} FC_CAPTURE_AND_RETHROW() } } FC_CAPTURE_AND_RETHROW() }
void database::update_vote_totals(const global_property_object& props)
{ try {
_vote_tally_buffer.resize(props.next_available_vote_id);
_witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
_committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
const account_index& account_idx = get_index_type<account_index>();
_total_voting_stake = 0;
bool count_non_prime_votes = props.parameters.count_non_prime_votes;
auto timestamp = fc::time_point::now();
for( const account_object& stake_account : account_idx.indices() )
{
if( count_non_prime_votes || stake_account.is_prime() )
{
// There may be a difference between the account whose stake is voting and the one specifying opinions.
// Usually they're the same, but if the stake account has specified a voting_account, that account is the one
// specifying the opinions.
const account_object& opinion_account =
(stake_account.voting_account == account_id_type())? stake_account
: get(stake_account.voting_account);
const auto& stats = stake_account.statistics(*this);
uint64_t voting_stake = stats.total_core_in_orders.value
+ (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(*this).balance.amount.value: 0)
+ get_balance(stake_account.get_id(), asset_id_type()).amount.value;
for( vote_id_type id : opinion_account.votes )
{
uint32_t offset = id.instance();
// if they somehow managed to specify an illegal offset, ignore it.
if( offset < _vote_tally_buffer.size() )
_vote_tally_buffer[ offset ] += voting_stake;
}
if( opinion_account.num_witness <= props.parameters.maximum_witness_count )
{
uint16_t offset = std::min(size_t(opinion_account.num_witness/2),
_witness_count_histogram_buffer.size() - 1);
//
// votes for a number greater than maximum_witness_count
// are turned into votes for maximum_witness_count.
//
// in particular, this takes care of the case where a
// member was voting for a high number, then the
// parameter was lowered.
//
_witness_count_histogram_buffer[ offset ] += voting_stake;
}
if( opinion_account.num_committee <= props.parameters.maximum_committee_count )
{
uint16_t offset = std::min(size_t(opinion_account.num_committee/2),
_committee_count_histogram_buffer.size() - 1);
//
// votes for a number greater than maximum_committee_count
// are turned into votes for maximum_committee_count.
//
// same rationale as for witnesses
//
_committee_count_histogram_buffer[ offset ] += voting_stake;
}
_total_voting_stake += voting_stake;
}
}
ilog("Tallied votes in ${time} milliseconds.", ("time", (fc::time_point::now() - timestamp).count() / 1000.0));
} FC_CAPTURE_AND_RETHROW() }
share_type database::get_max_budget( fc::time_point_sec now )const share_type database::get_max_budget( fc::time_point_sec now )const
{ {
const dynamic_global_property_object& dpo = get_dynamic_global_properties(); const dynamic_global_property_object& dpo = get_dynamic_global_properties();
@ -352,7 +284,154 @@ void database::process_budget()
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
{ {
update_vote_totals(global_props); const auto& gpo = get_global_properties();
struct vote_tally_helper {
database& d;
const global_property_object& props;
vote_tally_helper(database& d, const global_property_object& gpo)
: d(d), props(gpo)
{
d._vote_tally_buffer.resize(props.next_available_vote_id);
d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
d._total_voting_stake = 0;
}
void operator()(const account_object& stake_account) {
if( props.parameters.count_non_member_votes || stake_account.is_member(d.head_block_time()) )
{
// There may be a difference between the account whose stake is voting and the one specifying opinions.
// Usually they're the same, but if the stake account has specified a voting_account, that account is the one
// specifying the opinions.
const account_object& opinion_account =
(stake_account.voting_account == account_id_type())? stake_account
: d.get(stake_account.voting_account);
const auto& stats = stake_account.statistics(d);
uint64_t voting_stake = stats.total_core_in_orders.value
+ (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)
+ d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;
for( vote_id_type id : opinion_account.votes )
{
uint32_t offset = id.instance();
// if they somehow managed to specify an illegal offset, ignore it.
if( offset < d._vote_tally_buffer.size() )
d._vote_tally_buffer[ offset ] += voting_stake;
}
if( opinion_account.num_witness <= props.parameters.maximum_witness_count )
{
uint16_t offset = std::min(size_t(opinion_account.num_witness/2),
d._witness_count_histogram_buffer.size() - 1);
// votes for a number greater than maximum_witness_count
// are turned into votes for maximum_witness_count.
//
// in particular, this takes care of the case where a
// member was voting for a high number, then the
// parameter was lowered.
d._witness_count_histogram_buffer[ offset ] += voting_stake;
}
if( opinion_account.num_committee <= props.parameters.maximum_committee_count )
{
uint16_t offset = std::min(size_t(opinion_account.num_committee/2),
d._committee_count_histogram_buffer.size() - 1);
// votes for a number greater than maximum_committee_count
// are turned into votes for maximum_committee_count.
//
// same rationale as for witnesses
d._committee_count_histogram_buffer[ offset ] += voting_stake;
}
d._total_voting_stake += voting_stake;
}
}
} tally_helper(*this, gpo);
struct process_fees_helper {
database& d;
const global_property_object& props;
process_fees_helper(database& d, const global_property_object& gpo)
: d(d), props(gpo) {}
void operator()(const account_object& a) {
const account_statistics_object& stats = a.statistics(d);
auto cut_fee = [](share_type a, uint16_t p) -> share_type
{
fc::uint128 r(a.value);
r *= p;
r /= GRAPHENE_100_PERCENT;
return r.to_uint64();
};
if( stats.pending_fees > 0 )
{
share_type core_fee_subtotal(stats.pending_fees);
share_type bulk_cashback = share_type(0);
if( stats.lifetime_fees_paid > props.parameters.bulk_discount_threshold_min &&
a.is_member(d.head_block_time()) )
{
uint64_t bulk_discount_percent = 0;
if( stats.lifetime_fees_paid >= props.parameters.bulk_discount_threshold_max )
bulk_discount_percent = props.parameters.max_bulk_discount_percent_of_fee;
else if(props.parameters.bulk_discount_threshold_max.value !=
props.parameters.bulk_discount_threshold_min.value)
{
bulk_discount_percent =
(props.parameters.max_bulk_discount_percent_of_fee *
(stats.lifetime_fees_paid.value -
props.parameters.bulk_discount_threshold_min.value)) /
(props.parameters.bulk_discount_threshold_max.value -
props.parameters.bulk_discount_threshold_min.value);
}
assert( bulk_discount_percent <= GRAPHENE_100_PERCENT );
assert( bulk_discount_percent >= 0 );
bulk_cashback = cut_fee(core_fee_subtotal, bulk_discount_percent);
assert( bulk_cashback <= core_fee_subtotal );
}
share_type core_fee_total = core_fee_subtotal - bulk_cashback;
share_type network_cut = cut_fee(core_fee_total, a.network_fee_percentage);
assert( network_cut <= core_fee_total );
share_type burned = cut_fee(network_cut, props.parameters.burn_percent_of_fee);
share_type accumulated = network_cut - burned;
assert( accumulated + burned == network_cut );
share_type referral = core_fee_total - network_cut;
d.modify(dynamic_asset_data_id_type()(d), [burned,accumulated](asset_dynamic_data_object& d) {
d.accumulated_fees += accumulated + burned;
});
d.modify(a.statistics(d), [core_fee_total](account_statistics_object& s) {
s.lifetime_fees_paid += core_fee_total;
s.pending_fees = 0;
});
d.deposit_cashback( a, bulk_cashback );
assert( referral + bulk_cashback + accumulated + burned == core_fee_subtotal );
// 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 lifetime_cut = cut_fee(referral, a.lifetime_referrer_fee_percentage);
share_type referrer_cut = cut_fee(referral, a.referrer_rewards_percentage);
share_type registrar_cut = referral - lifetime_cut = referrer_cut;
d.deposit_cashback(d.get(a.lifetime_referrer), lifetime_cut);
d.deposit_cashback(d.get(a.referrer), referrer_cut);
d.deposit_cashback(d.get(a.registrar), registrar_cut);
assert( lifetime_cut + referrer_cut + registrar_cut == referral );
}
}
} fees_helper(*this, gpo);
perform_account_maintenance(std::tie(tally_helper, fees_helper));
struct clear_canary { struct clear_canary {
clear_canary(vector<uint64_t>& target): target(target){} clear_canary(vector<uint64_t>& target): target(target){}

View file

@ -24,7 +24,7 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
object_id_type delegate_create_evaluator::do_evaluate( const delegate_create_operation& op ) object_id_type delegate_create_evaluator::do_evaluate( const delegate_create_operation& op )
{ {
FC_ASSERT(db().get(op.delegate_account).is_prime()); FC_ASSERT(db().get(op.delegate_account).is_lifetime_member());
return object_id_type(); return object_id_type();
} }

View file

@ -61,55 +61,14 @@ namespace graphene { namespace chain {
void generic_evaluator::pay_fee() void generic_evaluator::pay_fee()
{ try { { try {
asset core_fee_subtotal(core_fee_paid);
const auto& gp = db().get_global_properties();
share_type bulk_cashback = share_type(0);
if( fee_paying_account_statistics->lifetime_fees_paid > gp.parameters.bulk_discount_threshold_min &&
fee_paying_account->is_prime() )
{
uint64_t bulk_discount_percent = 0;
if( fee_paying_account_statistics->lifetime_fees_paid > gp.parameters.bulk_discount_threshold_max )
bulk_discount_percent = gp.parameters.max_bulk_discount_percent_of_fee;
else if(gp.parameters.bulk_discount_threshold_max.value - gp.parameters.bulk_discount_threshold_min.value != 0)
{
bulk_discount_percent =
(gp.parameters.max_bulk_discount_percent_of_fee *
(fee_paying_account_statistics->lifetime_fees_paid.value -
gp.parameters.bulk_discount_threshold_min.value)) /
(gp.parameters.bulk_discount_threshold_max.value - gp.parameters.bulk_discount_threshold_min.value);
}
assert( bulk_discount_percent <= GRAPHENE_100_PERCENT );
assert( bulk_discount_percent >= 0 );
bulk_cashback = (core_fee_subtotal.amount.value * bulk_discount_percent) / GRAPHENE_100_PERCENT;
assert( bulk_cashback <= core_fee_subtotal.amount );
}
share_type core_fee_total = core_fee_subtotal.amount - bulk_cashback;
share_type accumulated = (core_fee_total.value * gp.parameters.witness_percent_of_fee)/GRAPHENE_100_PERCENT;
share_type burned = (core_fee_total.value * gp.parameters.burn_percent_of_fee)/GRAPHENE_100_PERCENT;
share_type referral = core_fee_total.value - accumulated - burned;
auto& d = db();
assert( accumulated + burned <= core_fee_total );
if( fee_asset->get_id() != asset_id_type() ) if( fee_asset->get_id() != asset_id_type() )
d.modify(*fee_asset_dyn_data, [this](asset_dynamic_data_object& d) { db().modify(*fee_asset_dyn_data, [this](asset_dynamic_data_object& d) {
d.accumulated_fees += fee_from_account.amount; d.accumulated_fees += fee_from_account.amount;
d.fee_pool -= core_fee_paid; d.fee_pool -= core_fee_paid;
}); });
d.modify(dynamic_asset_data_id_type()(d), [burned,accumulated](asset_dynamic_data_object& d) { db().modify(*fee_paying_account_statistics, [&](account_statistics_object& s) {
d.accumulated_fees += accumulated + burned; s.pending_fees += core_fee_paid;
}); });
d.modify(fee_paying_account->statistics(d), [core_fee_total](account_statistics_object& s) {
s.lifetime_fees_paid += core_fee_total;
});
d.deposit_cashback( fee_paying_account->referrer(d), referral );
d.deposit_cashback( *fee_paying_account, bulk_cashback );
assert( referral + bulk_cashback + accumulated + burned == core_fee_subtotal.amount );
} FC_CAPTURE_AND_RETHROW() } } FC_CAPTURE_AND_RETHROW() }
bool generic_evaluator::verify_authority( const account_object& a, authority::classification c ) bool generic_evaluator::verify_authority( const account_object& a, authority::classification c )

View file

@ -39,32 +39,34 @@ namespace graphene { namespace chain {
static const uint8_t type_id = impl_account_statistics_object_type; static const uint8_t type_id = impl_account_statistics_object_type;
/** /**
* Keep the most recent operation as a root pointer to * Keep the most recent operation as a root pointer to a linked list of the transaction history. This field is
* a linked list of the transaction history. This field is * not required by core validation and could in theory be made an annotation on the account object, but
* not required by core validation and could in theory be * because transaction history is so common and this object is already cached in the undo buffer (because it
* made an annotation on the account object, but because * likely affected the balances of this account) it is convienent to simply track this data here. Account
* transaction history is so common and this object is already * balance objects don't currenty inherit from annotated object.
* cached in the undo buffer (because it likely affected the
* balances of this account) it is convienent to simply
* track this data here. Account balance objects don't currenty
* inherit from annotated object.
*/ */
account_transaction_history_id_type most_recent_op; account_transaction_history_id_type most_recent_op;
/** /**
* When calculating votes it is necessary to know how much is * When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for
* stored in orders (and thus unavailable for transfers). Rather * transfers). Rather than maintaining an index of [asset,owner,order_id] we will simply maintain the running
* than maintaining an index of [asset,owner,order_id] we will * total here and update it every time an order is created or modified.
* simply maintain the running total here and update it every
* time an order is created or modified.
*/ */
share_type total_core_in_orders; share_type total_core_in_orders;
/** /**
* Tracks the total fees paid by this account for the purpose * Tracks the total fees paid by this account for the purpose of calculating bulk discounts.
* of calculating bulk discounts.
*/ */
share_type lifetime_fees_paid; share_type lifetime_fees_paid;
/**
* Tracks the fees paid by this account which have not been disseminated to the various parties that receive
* them yet (registrar, referrer, lifetime referrer, network, etc). This is used as an optimization to avoid
* doing massive amounts of uint128 math on each and every operation.
*
* These fees will be paid out and this counter will reset during the maintenance interval.
*/
share_type pending_fees;
}; };
/** /**
@ -102,53 +104,54 @@ namespace graphene { namespace chain {
public: public:
static const uint8_t space_id = protocol_ids; static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = account_object_type; static const uint8_t type_id = account_object_type;
/**
* The account that paid the fee to register this account, this account is
* known as the primary referrer and is entitled to a percent of transaction
* fees.
*/
account_id_type registrar;
/** /**
* The registrar may be a faucet with its own revenue sharing model that allows * The time at which this account's membership expires.
* users to refer each other. * If set to any time in the past, the account is a basic account.
* If set to time_point_sec::maximum(), the account is a lifetime member.
* If set to any time not in the past less than time_point_sec::maximum(), the account is an annual member.
*
* See @ref is_lifetime_member, @ref is_basic_account, @ref is_annual_member, and @ref is_member
*/ */
account_id_type referrer; time_point_sec membership_expiration_date;
/** ///The account that paid the fee to register this account. Receives a percentage of referral rewards.
* Any referral fees not paid to referrer are paid to registrar account_id_type registrar;
*/ /// The account credited as referring this account. Receives a percentage of referral rewards.
uint8_t referrer_percent = 0; account_id_type referrer;
/// The lifetime member at the top of the referral tree. Receives a percentage of referral rewards.
account_id_type lifetime_referrer;
/// Percentage of fee which should go to network.
uint16_t network_fee_percentage;
/// Percentage of fee which should go to lifetime referrer.
uint16_t lifetime_referrer_fee_percentage = 0;
/// Percentage of referral rewards (leftover fee after paying network and lifetime referrer) which should go
/// to referrer. The remainder of referral rewards goes to the registrar.
uint16_t referrer_rewards_percentage = 0;
/// The account's name. This name must be unique among all account names on the graph. The name may be empty. /// The account's name. This name must be unique among all account names on the graph. May not be empty.
string name; string name;
/** /**
* The owner authority represents absolute control over the account. Usually the keys in this authority will * The owner authority represents absolute control over the account. Usually the keys in this authority will
* be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes * be kept in cold storage, as they should not be needed very often and compromise of these keys constitutes
* complete and irrevocable loss of the account. Generally the only time the owner authority is required is to * complete and irrevocable loss of the account. Generally the only time the owner authority is required is to
* update the active authority. * update the active authority.
*/ */
authority owner; authority owner;
/// The owner authority contains the hot keys of the account. This authority has control over nearly all /// The owner authority contains the hot keys of the account. This authority has control over nearly all
/// operations the account may perform. /// operations the account may perform.
authority active; authority active;
/// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non- /// The memo key is the key this account will typically use to encrypt/sign transaction memos and other non-
/// validated account activities. This field is here to prevent confusion if the active authority has zero or /// validated account activities. This field is here to prevent confusion if the active authority has zero or
/// multiple keys in it. /// multiple keys in it.
key_id_type memo_key; key_id_type memo_key;
/// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake /// If this field is set to an account ID other than 0, this account's votes will be ignored and its stake
/// will be counted as voting for the referenced account's selected votes instead. /// will be counted as voting for the referenced account's selected votes instead.
account_id_type voting_account; account_id_type voting_account;
uint16_t num_witness = 0;
uint16_t num_committee = 0;
uint16_t num_witness = 0;
uint16_t num_committee = 0;
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this /// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
/// account's balance of core asset. /// account's balance of core asset.
flat_set<vote_id_type> votes; flat_set<vote_id_type> votes;
@ -163,7 +166,7 @@ namespace graphene { namespace chain {
* account cannot update this set, except by transferring ownership of the account, which will clear it. Other * account cannot update this set, except by transferring ownership of the account, which will clear it. Other
* accounts may add or remove their IDs from this set. * accounts may add or remove their IDs from this set.
*/ */
flat_set<account_id_type> whitelisting_accounts; flat_set<account_id_type> whitelisting_accounts;
/** /**
* This is a set of all accounts which have 'blacklisted' this account. Blacklisting is only used in core * This is a set of all accounts which have 'blacklisted' this account. Blacklisting is only used in core
@ -171,20 +174,34 @@ namespace graphene { namespace chain {
* account cannot update this set, and it will be preserved even if the account is transferred. Other accounts * account cannot update this set, and it will be preserved even if the account is transferred. Other accounts
* may add or remove their IDs from this set. * may add or remove their IDs from this set.
*/ */
flat_set<account_id_type> blacklisting_accounts; flat_set<account_id_type> blacklisting_accounts;
/** /**
* Vesting balance which receives cashback_reward deposits. * Vesting balance which receives cashback_reward deposits.
*/ */
optional<vesting_balance_id_type> cashback_vb; optional<vesting_balance_id_type> cashback_vb;
/** /// @return true if this is a lifetime member account; false otherwise.
* @return true if this is a prime account, false otherwise. bool is_lifetime_member()const
*/
bool is_prime()const
{ {
return get_id() == referrer; return get_id() == referrer;
} }
/// @return true if this is a basic account; false otherwise.
bool is_basic_account(time_point_sec now)const
{
return now > membership_expiration_date;
}
/// @return true if the account is an unexpired annual member; false otherwise.
/// @note This method will return false for lifetime members.
bool is_annual_member(time_point_sec now)const
{
return !is_lifetime_member() && !is_basic_account(now);
}
/// @return true if the account is an annual or lifetime member; false otherwise.
bool is_member(time_point_sec now)const
{
return !is_basic_account(now);
}
/** /**
* @return true if this account is whitelisted and not blacklisted to transact in the provided asset; false * @return true if this account is whitelisted and not blacklisted to transact in the provided asset; false
@ -253,9 +270,12 @@ namespace graphene { namespace chain {
typedef generic_index<account_object, account_object_multi_index_type> account_index; typedef generic_index<account_object, account_object_multi_index_type> account_index;
}} }}
FC_REFLECT_DERIVED( graphene::chain::account_object, FC_REFLECT_DERIVED( graphene::chain::account_object,
(graphene::db::annotated_object<graphene::chain::account_object>), (graphene::db::annotated_object<graphene::chain::account_object>),
(registrar)(referrer)(referrer_percent)(name)(owner)(active)(memo_key)(voting_account)(num_witness)(num_committee)(votes) (membership_expiration_date)(registrar)(referrer)(lifetime_referrer)
(network_fee_percentage)(lifetime_referrer_fee_percentage)(referrer_rewards_percentage)
(name)(owner)(active)(memo_key)(voting_account)(num_witness)(num_committee)(votes)
(statistics)(whitelisting_accounts)(blacklisting_accounts)(cashback_vb) ) (statistics)(whitelisting_accounts)(blacklisting_accounts)(cashback_vb) )
FC_REFLECT_DERIVED( graphene::chain::account_balance_object, FC_REFLECT_DERIVED( graphene::chain::account_balance_object,

View file

@ -49,6 +49,7 @@
#define GRAPHENE_DEFAULT_TRANSFER_FEE (1*GRAPHENE_BLOCKCHAIN_PRECISION) #define GRAPHENE_DEFAULT_TRANSFER_FEE (1*GRAPHENE_BLOCKCHAIN_PRECISION)
#define GRAPHENE_MAX_INSTANCE_ID (uint64_t(-1)>>16) #define GRAPHENE_MAX_INSTANCE_ID (uint64_t(-1)>>16)
#define GRAPHENE_100_PERCENT 10000 #define GRAPHENE_100_PERCENT 10000
#define GRAPHENE_1_PERCENT (GRAPHENE_100_PERCENT/100)
/** NOTE: making this a power of 2 (say 2^15) would greatly accelerate fee calcs */ /** NOTE: making this a power of 2 (say 2^15) would greatly accelerate fee calcs */
#define GRAPHENE_MAX_MARKET_FEE_PERCENT GRAPHENE_100_PERCENT #define GRAPHENE_MAX_MARKET_FEE_PERCENT GRAPHENE_100_PERCENT
#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY (60*60*24) ///< 1 day #define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY (60*60*24) ///< 1 day
@ -72,12 +73,14 @@
#define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD #define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD
#define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks #define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks
#define GRAPHENE_DEFAULT_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks #define GRAPHENE_DEFAULT_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks
#define GRAPHENE_DEFAULT_WITNESS_PERCENT (10000/100) // 1% #define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
#define GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT (10000/2) // 50% #define GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE (30*GRAPHENE_1_PERCENT)
#define GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT (50*GRAPHENE_1_PERCENT)
#define GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN ( GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(1000) ) #define GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN ( GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(1000) )
#define GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX ( GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN*int64_t(100) ) #define GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX ( GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN*int64_t(100) )
#define GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC (60*60*24*365) ///< 1 year #define GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC (60*60*24*365) ///< 1 year
#define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE (10000/5) // 20% #define GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD (GRAPHENE_BLOCKCHAIN_PRECISION*int64_t(100))
#define GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
#define GRAPHENE_DEFAULT_WITNESS_PAY_PERCENT_OF_ACCUMULATED ( 1728000) /// gives a half life of 1 year assuming 1 second blocks #define GRAPHENE_DEFAULT_WITNESS_PAY_PERCENT_OF_ACCUMULATED ( 1728000) /// gives a half life of 1 year assuming 1 second blocks
#define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000) #define GRAPHENE_WITNESS_PAY_PERCENT_PRECISION (1000000000)
#define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL #define GRAPHENE_GENESIS_TIMESTAMP (1431700000) /// Should be divisible by GRAPHENE_DEFAULT_BLOCK_INTERVAL

View file

@ -20,6 +20,7 @@
#include <graphene/chain/block.hpp> #include <graphene/chain/block.hpp>
#include <graphene/chain/asset.hpp> #include <graphene/chain/asset.hpp>
#include <graphene/chain/global_property_object.hpp> #include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp> #include <graphene/chain/asset_object.hpp>
#include <graphene/chain/fork_database.hpp> #include <graphene/chain/fork_database.hpp>
@ -357,7 +358,9 @@ namespace graphene { namespace chain {
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props); void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
void update_active_witnesses(); void update_active_witnesses();
void update_active_delegates(); void update_active_delegates();
void update_vote_totals(const global_property_object& props);
template<class... Types>
void perform_account_maintenance(std::tuple<Types...> helpers);
///@} ///@}
///@} ///@}
@ -394,4 +397,29 @@ namespace graphene { namespace chain {
uint64_t _total_voting_stake; uint64_t _total_voting_stake;
}; };
namespace detail
{
template<int... Is>
struct seq { };
template<int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };
template<int... Is>
struct gen_seq<0, Is...> : seq<Is...> { };
template<typename T, int... Is>
void for_each(T&& t, const account_object& a, seq<Is...>)
{
auto l = { (std::get<Is>(t)(a), 0)... };
}
}
template<class... Types>
void database::perform_account_maintenance(std::tuple<Types...> helpers)
{
const auto& idx = get_index_type<account_index>().indices();
for( const account_object& a : idx )
detail::for_each(helpers, a, detail::gen_seq<sizeof...(Types)>());
}
} } } }

View file

@ -15,7 +15,7 @@
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/* Copyright (C) Cryptonomex, Inc - All Rights Reserved /* Copyright (C) Cryptonomex, Inc - All Rights Reserved
* *
* All modifications become property of Cryptonomex, Inc. * All modifications become property of Cryptonomex, Inc.
* *
@ -125,14 +125,13 @@ namespace graphene { namespace chain {
struct account_create_operation struct account_create_operation
{ {
asset fee; asset fee;
/// This account pays the fee. Must be a lifetime member.
account_id_type registrar; account_id_type registrar;
/** /// This account receives a portion of the fee split between registrar and referrer. Must be a member.
* If fee_paying_account->is_prime then referrer can be
* any other account that is also prime. Otherwise referrer must
* equal fee_paying_account->referrer.
*/
account_id_type referrer; account_id_type referrer;
/// Of the fee split between registrar and referrer, this percentage goes to the referrer. The rest goes to the
/// registrar.
uint8_t referrer_percent = 0; uint8_t referrer_percent = 0;
string name; string name;
@ -214,12 +213,6 @@ namespace graphene { namespace chain {
uint16_t num_witness = 0; uint16_t num_witness = 0;
uint16_t num_committee = 0; uint16_t num_committee = 0;
/**
* If set to true, upgrades the account to a prime account by setting the account's referrer to itself. This may
* only be set to true when the account being modified is not already a prime account.
*/
bool upgrade_to_prime = false;
account_id_type fee_payer()const { return account; } account_id_type fee_payer()const { return account; }
void get_required_auth(flat_set<account_id_type>& active_auth_set , flat_set<account_id_type>& owner_auth_set)const; void get_required_auth(flat_set<account_id_type>& active_auth_set , flat_set<account_id_type>& owner_auth_set)const;
void validate()const; void validate()const;
@ -1089,13 +1082,13 @@ namespace graphene { namespace chain {
* authorizing account. This operation is primarily useful for scheduling recurring payments. * authorizing account. This operation is primarily useful for scheduling recurring payments.
* *
* Withdrawal permissions define withdrawal periods, which is a span of time during which the authorized account may * Withdrawal permissions define withdrawal periods, which is a span of time during which the authorized account may
* make a withdrawal. Any number of withdraws may be made so long as the total amount withdrawn is less than the limit * make a withdrawal. Any number of withdrawals may be made so long as the total amount withdrawn per period does
* for any given period. * not exceed the limit for any given period.
* *
* Withdrawal permissions authorize only a specific pairing, i.e. a permission only authorizes one specified * Withdrawal permissions authorize only a specific pairing, i.e. a permission only authorizes one specified
* authorized account to withdraw from one specified authorizing account. Withdrawals are limited and may not exceet * authorized account to withdraw from one specified authorizing account. Withdrawals are limited and may not exceet
* the withdrawal limit. The withdrawal must be made in the same asset as the limit; attempts with withdraw any other * the withdrawal limit. The withdrawal must be made in the same asset as the limit; attempts with withdraw any
* asset type will be rejected. * other asset type will be rejected.
* *
* The fee for this operation is paid by withdraw_from_account, and this account is required to authorize this * The fee for this operation is paid by withdraw_from_account, and this account is required to authorize this
* operation. * operation.
@ -1748,7 +1741,7 @@ FC_REFLECT( graphene::chain::account_create_operation,
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> ) FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
FC_REFLECT( graphene::chain::account_update_operation, FC_REFLECT( graphene::chain::account_update_operation,
(fee)(account)(owner)(active)(voting_account)(memo_key)(num_witness)(num_committee)(vote)(upgrade_to_prime) (fee)(account)(owner)(active)(voting_account)(memo_key)(num_witness)(num_committee)(vote)
) )
FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing) FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)

View file

@ -460,23 +460,26 @@ namespace graphene { namespace chain {
uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses
uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active delegates uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active delegates
uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have
uint16_t burn_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of every fee that is taken out of circulation uint16_t burn_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation
uint16_t witness_percent_of_fee = GRAPHENE_DEFAULT_WITNESS_PERCENT; ///< percent of revenue paid to witnesses uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint16_t lifetime_referrer_percent_of_fee = GRAPHENE_DEFAULT_LIFETIME_REFERRER_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
uint32_t cashback_vesting_period_seconds = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC; ///< time after cashback rewards are accrued before they become liquid uint32_t cashback_vesting_period_seconds = GRAPHENE_DEFAULT_CASHBACK_VESTING_PERIOD_SEC; ///< time after cashback rewards are accrued before they become liquid
share_type cashback_vesting_threshold = GRAPHENE_DEFAULT_CASHBACK_VESTING_THRESHOLD; ///< the maximum cashback that can be received without vesting
uint16_t max_bulk_discount_percent_of_fee = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT; ///< the maximum percentage discount for bulk discounts uint16_t max_bulk_discount_percent_of_fee = GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT; ///< the maximum percentage discount for bulk discounts
share_type bulk_discount_threshold_min = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN; ///< the minimum amount of fees paid to qualify for bulk discounts share_type bulk_discount_threshold_min = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MIN; ///< the minimum amount of fees paid to qualify for bulk discounts
share_type bulk_discount_threshold_max = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX; ///< the amount of fees paid to qualify for the max bulk discount percent share_type bulk_discount_threshold_max = GRAPHENE_DEFAULT_BULK_DISCOUNT_THRESHOLD_MAX; ///< the amount of fees paid to qualify for the max bulk discount percent
bool count_non_prime_votes = true; ///< set to false to restrict voting privlegages to prime accounts bool count_non_member_votes = true; ///< set to false to restrict voting privlegages to member accounts
bool allow_non_prime_whitelists = false; ///< true if non-prime accounts may set whitelists and blacklists; false otherwise bool allow_non_member_whitelists = false; ///< true if non-member accounts may set whitelists and blacklists; false otherwise
share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block) share_type witness_pay_per_block = GRAPHENE_DEFAULT_WITNESS_PAY_PER_BLOCK; ///< CORE to be allocated to witnesses (per block)
share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day) share_type worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)
void validate()const void validate()const
{ {
FC_ASSERT( witness_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( burn_percent_of_fee <= GRAPHENE_100_PERCENT ); FC_ASSERT( burn_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( max_bulk_discount_percent_of_fee <= GRAPHENE_100_PERCENT ); FC_ASSERT( max_bulk_discount_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( burn_percent_of_fee + witness_percent_of_fee <= GRAPHENE_100_PERCENT ); FC_ASSERT( lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( network_percent_of_fee + lifetime_referrer_percent_of_fee <= GRAPHENE_100_PERCENT );
FC_ASSERT( bulk_discount_threshold_min <= bulk_discount_threshold_max ); FC_ASSERT( bulk_discount_threshold_min <= bulk_discount_threshold_max );
FC_ASSERT( bulk_discount_threshold_min > 0 ); FC_ASSERT( bulk_discount_threshold_min > 0 );
@ -656,13 +659,15 @@ FC_REFLECT( graphene::chain::chain_parameters,
(maximum_asset_feed_publishers) (maximum_asset_feed_publishers)
(maximum_authority_membership) (maximum_authority_membership)
(burn_percent_of_fee) (burn_percent_of_fee)
(witness_percent_of_fee) (network_percent_of_fee)
(lifetime_referrer_percent_of_fee)
(max_bulk_discount_percent_of_fee) (max_bulk_discount_percent_of_fee)
(cashback_vesting_period_seconds) (cashback_vesting_period_seconds)
(cashback_vesting_threshold)
(bulk_discount_threshold_min) (bulk_discount_threshold_min)
(bulk_discount_threshold_max) (bulk_discount_threshold_max)
(count_non_prime_votes) (count_non_member_votes)
(allow_non_prime_whitelists) (allow_non_member_whitelists)
(witness_pay_per_block) (witness_pay_per_block)
(worker_budget_per_day) (worker_budget_per_day)
) )

View file

@ -28,9 +28,9 @@ namespace graphene { namespace chain {
* *
* The primary purpose of this object is to enable recurring payments on the blockchain. An account which wishes to * The primary purpose of this object is to enable recurring payments on the blockchain. An account which wishes to
* process a recurring payment may use a @ref withdraw_permission_claim_operation to reference an object of this type * process a recurring payment may use a @ref withdraw_permission_claim_operation to reference an object of this type
* and withdraw up to @ref withdrawal_limit from @ref withdraw_from_account. Only @ref authorized_account may do this. * and withdraw up to @ref withdrawal_limit from @ref withdraw_from_account. Only @ref authorized_account may do
* Any number of withdraws may be made so long as the total amount withdrawn is less than the limit for any given * this. Any number of withdrawals may be made so long as the total amount withdrawn per period does not exceed the
* period. * limit for any given period.
*/ */
class withdraw_permission_object : public graphene::db::abstract_object<withdraw_permission_object> class withdraw_permission_object : public graphene::db::abstract_object<withdraw_permission_object>
{ {
@ -54,14 +54,14 @@ namespace graphene { namespace chain {
/// tracks the total amount /// tracks the total amount
share_type claimed_this_period; share_type claimed_this_period;
/// True if the permission may still be claimed for this period; false if it has already been used /// True if the permission may still be claimed for this period; false if it has already been used
asset available_this_period( fc::time_point_sec current_time )const asset available_this_period( fc::time_point_sec current_time )const
{ {
if( current_time >= period_start_time + withdrawal_period_sec ) if( current_time >= period_start_time + withdrawal_period_sec )
return withdrawal_limit; return withdrawal_limit;
return asset( return asset(
( withdrawal_limit.amount > claimed_this_period ) ( withdrawal_limit.amount > claimed_this_period )
? withdrawal_limit.amount - claimed_this_period ? withdrawal_limit.amount - claimed_this_period
: 0, withdrawal_limit.asset_id ); : 0, withdrawal_limit.asset_id );
} }
}; };

View file

@ -122,7 +122,6 @@ share_type account_create_operation::calculate_fee( const fee_schedule_type& sch
} }
share_type account_update_operation::calculate_fee( const fee_schedule_type& schedule )const share_type account_update_operation::calculate_fee( const fee_schedule_type& schedule )const
{ {
if( upgrade_to_prime ) return schedule.at(prime_upgrade_fee_type);
return schedule.at(account_create_fee_type); return schedule.at(account_create_fee_type);
} }
void account_update_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, void account_update_operation::get_required_auth(flat_set<account_id_type>& active_auth_set,
@ -138,7 +137,7 @@ void account_update_operation::validate()const
{ {
FC_ASSERT( fee.amount >= 0 ); FC_ASSERT( fee.amount >= 0 );
FC_ASSERT( account != account_id_type() ); FC_ASSERT( account != account_id_type() );
FC_ASSERT( owner || active || voting_account || memo_key || vote || upgrade_to_prime ); FC_ASSERT( owner || active || voting_account || memo_key || vote );
} }

View file

@ -25,7 +25,7 @@
namespace graphene { namespace chain { namespace graphene { namespace chain {
object_id_type witness_create_evaluator::do_evaluate( const witness_create_operation& op ) object_id_type witness_create_evaluator::do_evaluate( const witness_create_operation& op )
{ {
FC_ASSERT(db().get(op.witness_account).is_prime()); FC_ASSERT(db().get(op.witness_account).is_lifetime_member());
return object_id_type(); return object_id_type();
} }

View file

@ -26,7 +26,7 @@ object_id_type worker_create_evaluator::do_evaluate(const worker_create_evaluato
{ try { { try {
database& d = db(); database& d = db();
FC_ASSERT(d.get(o.owner).is_prime()); FC_ASSERT(d.get(o.owner).is_lifetime_member());
FC_ASSERT(o.work_begin_date >= d.head_block_time()); FC_ASSERT(o.work_begin_date >= d.head_block_time());
return object_id_type(); return object_id_type();

View file

@ -695,7 +695,7 @@ public:
account_object registrar_account_object = account_object registrar_account_object =
this->get_account( registrar_account ); this->get_account( registrar_account );
FC_ASSERT( registrar_account_object.is_prime() ); FC_ASSERT( registrar_account_object.is_lifetime_member() );
account_id_type registrar_account_id = registrar_account_object.id; account_id_type registrar_account_id = registrar_account_object.id;
@ -761,17 +761,11 @@ public:
{ try { { try {
FC_ASSERT( !self.is_locked() ); FC_ASSERT( !self.is_locked() );
account_object account_obj = get_account(name); account_object account_obj = get_account(name);
FC_ASSERT( !account_obj.is_prime() ); FC_ASSERT( !account_obj.is_lifetime_member() );
account_update_operation update_op;
update_op.account = account_obj.id;
update_op.num_witness = account_obj.num_witness;
update_op.num_committee = account_obj.num_committee;
update_op.upgrade_to_prime = true;
// TODO
signed_transaction tx; signed_transaction tx;
tx.operations.push_back( update_op );
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) ); tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
tx.validate(); tx.validate();
@ -802,7 +796,7 @@ public:
account_object referrer_account_object = account_object referrer_account_object =
this->get_account( referrer_account ); this->get_account( referrer_account );
account_create_op.referrer = referrer_account_object.id; account_create_op.referrer = referrer_account_object.id;
account_create_op.referrer_percent = referrer_account_object.referrer_percent; account_create_op.referrer_percent = referrer_account_object.referrer_rewards_percentage;
// get pay_from_account_id // get pay_from_account_id
key_create_operation owner_key_create_op; key_create_operation owner_key_create_op;

View file

@ -676,21 +676,18 @@ void database_fixture::enable_fees(
} ); } );
} }
void database_fixture::upgrade_to_prime(account_id_type account) void database_fixture::upgrade_to_lifetime_member(account_id_type account)
{ {
upgrade_to_prime(account(db)); upgrade_to_lifetime_member(account(db));
} }
void database_fixture::upgrade_to_prime( const account_object& account ) void database_fixture::upgrade_to_lifetime_member( const account_object& account )
{ {
try try
{ {
account_update_operation op; // TODO
op.account = account.id;
op.upgrade_to_prime = true;
trx.operations.emplace_back(operation(op));
db.push_transaction( trx, ~0 ); db.push_transaction( trx, ~0 );
FC_ASSERT( account.is_prime() ); FC_ASSERT( account.is_lifetime_member() );
trx.clear(); trx.clear();
} }
FC_CAPTURE_AND_RETHROW((account)) FC_CAPTURE_AND_RETHROW((account))

View file

@ -202,8 +202,8 @@ struct database_fixture {
void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() ); void transfer( const account_object& from, const account_object& to, const asset& amount, const asset& fee = asset() );
void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount ); void fund_fee_pool( const account_object& from, const asset_object& asset_to_fund, const share_type amount );
void enable_fees( share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION ); void enable_fees( share_type fee = GRAPHENE_BLOCKCHAIN_PRECISION );
void upgrade_to_prime( account_id_type account ); void upgrade_to_lifetime_member( account_id_type account );
void upgrade_to_prime( const account_object& account ); void upgrade_to_lifetime_member( const account_object& account );
void print_market( const string& syma, const string& symb )const; void print_market( const string& syma, const string& symb )const;
string pretty( const asset& a )const; string pretty( const asset& a )const;
void print_short_order( const short_order_object& cur )const; void print_short_order( const short_order_object& cur )const;

View file

@ -434,7 +434,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture )
for( int i = 0; i < 15; ++i ) for( int i = 0; i < 15; ++i )
{ {
const auto& account = create_account("delegate" + fc::to_string(i+1), delegate_key_object.id); const auto& account = create_account("delegate" + fc::to_string(i+1), delegate_key_object.id);
upgrade_to_prime(account); upgrade_to_lifetime_member(account);
delegates.insert(create_delegate(account).vote_id); delegates.insert(create_delegate(account).vote_id);
} }
@ -980,8 +980,8 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture ) BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture )
{ try { { try {
ACTORS((nathan)(vikram)); ACTORS((nathan)(vikram));
upgrade_to_prime(nathan_id); upgrade_to_lifetime_member(nathan_id);
upgrade_to_prime(vikram_id); upgrade_to_lifetime_member(vikram_id);
delegate_id_type nathan_delegate = create_delegate(nathan_id(db)).id; delegate_id_type nathan_delegate = create_delegate(nathan_id(db)).id;
delegate_id_type vikram_delegate = create_delegate(vikram_id(db)).id; delegate_id_type vikram_delegate = create_delegate(vikram_id(db)).id;

View file

@ -424,7 +424,7 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture )
BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch()); BOOST_CHECK_GT(maintenence_time.sec_since_epoch(), db.head_block_time().sec_since_epoch());
auto initial_properties = db.get_global_properties(); auto initial_properties = db.get_global_properties();
const account_object& nathan = create_account("nathan"); const account_object& nathan = create_account("nathan");
upgrade_to_prime(nathan); upgrade_to_lifetime_member(nathan);
const delegate_object nathans_delegate = create_delegate(nathan); const delegate_object nathans_delegate = create_delegate(nathan);
{ {
account_update_operation op; account_update_operation op;

View file

@ -186,13 +186,13 @@ BOOST_AUTO_TEST_CASE( update_account )
transfer(account_id_type()(db), nathan, asset(3000000)); transfer(account_id_type()(db), nathan, asset(3000000));
enable_fees(); enable_fees();
op.upgrade_to_prime = true; // TODO: op.upgrade_to_prime = true;
op.fee = op.calculate_fee( db.get_global_properties().parameters.current_fees ); op.fee = op.calculate_fee( db.get_global_properties().parameters.current_fees );
trx.operations.push_back(op); trx.operations.push_back(op);
db.push_transaction(trx, ~0); db.push_transaction(trx, ~0);
BOOST_CHECK( nathan.referrer == nathan.id ); BOOST_CHECK( nathan.referrer == nathan.id );
BOOST_CHECK( nathan.referrer_percent == 100 ); BOOST_CHECK( nathan.referrer_rewards_percentage == 100 );
} catch (fc::exception& e) { } catch (fc::exception& e) {
edump((e.to_detail_string())); edump((e.to_detail_string()));
throw; throw;
@ -1872,7 +1872,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0); BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0);
account_update_operation uop; account_update_operation uop;
uop.account = nathan->get_id(); uop.account = nathan->get_id();
uop.upgrade_to_prime = true; // TODO: uop.upgrade_to_prime = true;
trx.set_expiration(db.head_block_id()); trx.set_expiration(db.head_block_id());
trx.operations.push_back(uop); trx.operations.push_back(uop);
trx.visit(operation_set_fee(db.current_fee_schedule())); trx.visit(operation_set_fee(db.current_fee_schedule()));
@ -2124,7 +2124,7 @@ BOOST_AUTO_TEST_CASE( unimp_transfer_cashback_test )
const account_object& sam = create_account( "sam" ); const account_object& sam = create_account( "sam" );
transfer(account_id_type()(db), sam, asset(30000)); transfer(account_id_type()(db), sam, asset(30000));
upgrade_to_prime(sam); upgrade_to_lifetime_member(sam);
ilog( "Creating alice" ); ilog( "Creating alice" );
const account_object& alice = create_account( "alice", sam, sam, 0 ); const account_object& alice = create_account( "alice", sam, sam, 0 );

View file

@ -388,7 +388,7 @@ BOOST_AUTO_TEST_CASE( mia_feeds )
BOOST_AUTO_TEST_CASE( witness_create ) BOOST_AUTO_TEST_CASE( witness_create )
{ try { { try {
ACTOR(nathan); ACTOR(nathan);
upgrade_to_prime(nathan_id); upgrade_to_lifetime_member(nathan_id);
trx.clear(); trx.clear();
witness_id_type nathan_witness_id = create_witness(nathan_id, nathan_key_id, nathan_private_key).id; witness_id_type nathan_witness_id = create_witness(nathan_id, nathan_key_id, nathan_private_key).id;
// Give nathan some voting stake // Give nathan some voting stake
@ -494,7 +494,7 @@ BOOST_AUTO_TEST_CASE( global_settle_test )
BOOST_AUTO_TEST_CASE( worker_create_test ) BOOST_AUTO_TEST_CASE( worker_create_test )
{ try { { try {
ACTOR(nathan); ACTOR(nathan);
upgrade_to_prime(nathan_id); upgrade_to_lifetime_member(nathan_id);
generate_block(); generate_block();
{ {
@ -615,7 +615,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test )
BOOST_AUTO_TEST_CASE( refund_worker_test ) BOOST_AUTO_TEST_CASE( refund_worker_test )
{try{ {try{
ACTOR(nathan); ACTOR(nathan);
upgrade_to_prime(nathan_id); upgrade_to_lifetime_member(nathan_id);
generate_block(); generate_block();
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time); generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
trx.set_expiration(db.head_block_id()); trx.set_expiration(db.head_block_id());

View file

@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE( issue_whitelist_uia )
INVOKE(create_advanced_uia); INVOKE(create_advanced_uia);
const asset_object& advanced = get_asset("ADVANCED"); const asset_object& advanced = get_asset("ADVANCED");
const account_object& nathan = create_account("nathan"); const account_object& nathan = create_account("nathan");
upgrade_to_prime(nathan); upgrade_to_lifetime_member(nathan);
trx.clear(); trx.clear();
asset_issue_operation op({asset(), advanced.issuer, advanced.amount(1000), nathan.id}); asset_issue_operation op({asset(), advanced.issuer, advanced.amount(1000), nathan.id});
@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE( transfer_whitelist_uia )
const asset_object& advanced = get_asset("ADVANCED"); const asset_object& advanced = get_asset("ADVANCED");
const account_object& nathan = get_account("nathan"); const account_object& nathan = get_account("nathan");
const account_object& dan = create_account("dan"); const account_object& dan = create_account("dan");
upgrade_to_prime(dan); upgrade_to_lifetime_member(dan);
trx.clear(); trx.clear();
transfer_operation op({advanced.amount(0), nathan.id, dan.id, advanced.amount(100)}); transfer_operation op({advanced.amount(0), nathan.id, dan.id, advanced.amount(100)});