Merge branch 'master' of github.com:cryptonomex/graphene

This commit is contained in:
Daniel Larimer 2015-06-12 09:53:32 -04:00
commit d7e89181a5
44 changed files with 1051 additions and 824 deletions

10
.gitignore vendored
View file

@ -6,12 +6,19 @@ CMakeCache.txt
CMakeFiles
Makefile
compile_commands.json
*.cmake
libraries/utilities/git_revision.cpp
programs/cli_wallet/cli_wallet
programs/js_operation_serializer/js_operation_serializer
programs/witness_node/witness_node
tests/app_test
tests/chain_bench
tests/chain_test
tests/intense_test
tests/performance_test
doxygen
@ -20,3 +27,6 @@ programs/js_operation_serializer/js_operation_serializer
programs/witness_node/witness_node
tests/intense_test
tests/performance_test
witness_node_data_dir
wallet.json

13
.gitmodules vendored
View file

@ -1,6 +1,11 @@
[submodule "docs"]
path = docs
url = https://github.com/cryptonomex/graphene.wiki.git
[submodule "libraries/fc"]
path = libraries/fc
url = https://github.com/cryptonomex/fc
path = libraries/fc
url = https://github.com/cryptonomex/fc.git
ignore = dirty
[submodule "libraries/leveldb"]
path = libraries/leveldb
url = https://github.com/bitcoin/leveldb.git
path = libraries/leveldb
url = https://github.com/bitcoin/leveldb.git
ignore = dirty

View file

@ -1,29 +0,0 @@
How to use fc async to do recurring tasks
-----------------------------------------
_my_task = fc::async( callable, "My Task" );
_my_task = fc::schedule( callable, "My Task 2", exec_time );
Stuff to know about the code
----------------------------
`static_variant<t1, t2>` is a *union type* which says "this variable may be either t1 or t2." It is serializable if t1 and t2 are both serializable.
The file `operations.hpp` documents the available operations, and `database_fixture.hpp` is a good reference for building and submitting transactions for processing.
Tests also show the way to do many things, but are often cluttered with code that generates corner cases to try to break things in every possible way.
Visitors are at the end of `operations.hpp` after the large typedef for `operation` as a `static_variant`. TODO: They should be refactored into a separate header.
Downcasting stuff
-----------------
- You have an `object_id_type` and want to downcast it to a `key_id_type` : `key_id_type( object_id )`
- You have an `operation_result` and want to downcast it to an `object_id_type` : `op_result.get<object_id_type>()`
- Since `operation_result` is a `static_variant`, the above is also how you downcast `static_variant`
Debugging FC exceptions with GDB
--------------------------------
- `catch throw`

View file

@ -6,7 +6,7 @@ This is a quick introduction to get new developers up to speed on Graphene.
Starting Graphene
-----------------
git clone https://gitlab.bitshares.org/dlarimer/graphene
git clone https://github.com/cryptonomex/graphene.git
cd graphene
git submodule update --init --recursive
cmake -DCMAKE_BUILD_TYPE=Debug .

View file

@ -1,21 +0,0 @@
The Boost which ships with Ubuntu 14.04 LTS is too old. You need to download the Boost tarball for Boost 1.57.0
(Note, 1.58.0 requires C++14 and will not build on Ubuntu LTS; this requirement was an accident, see ). Build Boost as follows:
# tarball available at http://sourceforge.net/projects/boost/files/boost/1.57.0/boost_1_57_0.tar.bz2/download
# sha256sum is 910c8c022a33ccec7f088bd65d4f14b466588dda94ba2124e78b8c57db264967
BOOST_ROOT=$(echo ~/opt/boost_1_57_0)
# build Boost from source
cd ~/src/boost_1_57_0
./bootstrap.sh --prefix=$BOOST_ROOT
./b2 link=static variant=debug threading=multi stage
./b2 link=static variant=debug threading=multi install
Then we need to tell `cmake` to use the Boost we just built, instead of using the system-wide Boost:
cd ~/src/graphene
[ -e ./doc/build-ubuntu.md ] && sh -c 'cmake -DBOOST_ROOT="$BOOST_ROOT" -DCMAKE_BUILD_TYPE=Debug .'
If all goes well, you should see the correct Boost version in the output messages to the above command.

View file

@ -1,14 +0,0 @@
/*
* This is draft license text currently under development. This license does not apply to anything right now.
*
* Copyright (c) 2015, Cryptonomex, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. No distribution of binaries compiled from modified versions of the code
*
* 2. Any modifications become property of Cryptonomex, Inc and must be licensed under these same terms
*
* 3. Any hosting provider is authorized to delete any version of this code at the request Cryptonomex, Inc.
*/

View file

@ -1,19 +0,0 @@
This is draft license text currently under development. This license does not apply to anything right now.
Copyright (c) 2015, Cryptonomex, Inc
All rights reserved.
The graphene source code is proprietary and may not be used for any purpose without written permission from Cryptonomex, Inc. The source code is
provided publicly for the sole purpose of allowing users to audit the code and compile their own executables. Blockchain technology depends upon
a strong consensus on the rules of an open protocol; this source code is provided to completely document the protocol rules (bugs and all).
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. No distribution of binaries compiled from modified versions of the code
2. Any modifications made to the code become property of Cryptonomex, Inc and must be licensed under these same terms
3. The software may be forked/cloned on github for the purpose of submitting bug fixes and pull requests. All forked repositories are property of Cryptonomex and by
forking you authorize Github.com to delete your fork at the request Cryptonomex, Inc.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 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.

View file

@ -1,37 +0,0 @@
Witness scheduler RNG
---------------------
The witness scheduler RNG is a random number generator which uses the
blockchain random number generator state as its seed.
The witness scheduler RNG creates an infinite stream of random bytes
by computing `sha256( sha256( seed ) + little_endian_64bit(i) )`, increasing
`i` from 0 to 1 to 2, etc. The RNG only runs during a scheduling block,
and `i` starts from `0` in each scheduling block (relying on different
seeds to produce different results).
This infinite stream of random bytes is equivalent to an infinite
stream of random bits in little bit-endian order. Given a bound `B`,
the bitstream can be used to produce a random number uniformly
distributed in the range `[0, B)` using a sample-and-reject algorithm:
- Let `n` be the smallest integer such that `2^n >= B`.
- Let `x` be the next `n` bits from the bitstream, interpreted as an integer in little bit-endian order.
- If `x <= B`, return `x`. Otherwise, throw `x` away and repeat.
The worst-case running time is unbounded, but each iteration has a
termination probability greater than one half. Thus the average-case
running time is `2` iterations, and a running time of more than `N`
iterations will occur (on average) at most once every `2^N`
RNG queries (assuming a worst-case choice of e.g. `B = 2^63+1` for all
queries). Since each RNG query schedules a witness, the query rate
is (over the long term) equal to the block production rate (although
in practice many queries are all performed at once in scheduling
blocks). So while it is, in theory, possible for the algorithm to
require more than 1000 iterations, in practice this will occur on average
only once every `2^1000` blocks (again assuming all queries have
worst-case `B`).
The sample-and-reject algorithm is totally unbiased; every `x` value
has equal probability.

View file

@ -1,128 +0,0 @@
Turn/Token witness scheduling algorithm
---------------------------------------
The algorithm which determines the order of witnesses is referred
to as the *witness scheduling algorithm*.
This was designed by a community bounty in thread
https://bitsharestalk.org/index.php/topic,15547.0
however, Graphene has an additional requirement which
is not taken into account by the solutions in the thread:
The membership and length of the list of witnesses may change over
time.
So in this article I'll describe my solution.
Turns and tokens
----------------
The solution is based on terms of *turns* and *tokens*.
- Newly inserted witnesses start out with a turn and a token.
- In order for a witness to be scheduled, it must have a turn and a token.
- The scheduler maintains a FIFO of witnesses without tokens.
- If no witness has a turn, then the scheduler gives a turn to all witnesses. This is called "emitting a turn."
- While less than half of the witnesses have tokens, give a token to the first witness in the FIFO and remove it from the FIFO.
- Schedule a witness by picking randomly from all witnesses with both a turn and token.
- When a witness is scheduled, it loses its turn and token.
The generic scheduler
---------------------
The generic scheduler implements turns and tokens. It only depends
on the C++11 stdlib and boost (not even using fc). Types provided
by Graphene are template parameters.
The generic far future scheduler
--------------------------------
The far future scheduler is implemented with the following rules:
- Run until you emit a turn.
- Record all witnesses produced.
- Run until you emit a second turn.
- The witnesses produced between the emission of the first turn (exclusive)
and emission of the second turn (inclusive) are called the *far future schedule*.
Then the schedule for the rest of time is determined by repeating
the future schedule indefinitely. The far future scheduler is required
to give the scheduling algorithm bounded runtime and memory usage even
in chains involving very long gaps.
Slots
-----
Due to dynamic block interval, we must carefully keep in mind
the difference between schedule slots and timestamps. A
*schedule slot number* is a positive integer. A slot number of `n`
represents the `n`th next block-interval-aligned timestamp after
the head block.
Note that the mapping between slot numbers and timestamps will change
if the block interval changes.
Scheduling blocks
-----------------
When each block is produced, the blockchain must determine whether
the scheduler needs to be run. If fewer than `num_witnesses` are
scheduled, the scheduler will run until `2*num_witnesses` are scheduled.
A block in which the scheduler runs is called a *scheduling block*.
Changes in the set of active witnesses do not modify the existing
schedule. Rather, they will be incorporated into new schedule entries
when the scheduler runs in the next scheduling block. Thus, a witness
that has lost an election may still produce 1-2 blocks. Such a witness
is called a *lame duck*.
Near vs. far schedule
---------------------
From a particular chain state, it must be possible to specify a
mapping from slots to witnesses, called the *total witness schedule*.
The total witness schedule is partitioned into a prefix, called the
*near schedule*; the remainder is the *far schedule*.
When a block occurs, `n` entries are *drained* (removed) from the head
of the total schedule, where `n` is the slot number of the new block
according to its parent block.
If the block is a scheduling block, the total schedule is further
transformed. The new near schedule contains `2*num_witnesses` entries,
with the previous near schedule as a prefix. The rest of the near
schedule is determined by the current blockchain RNG.
The new far schedule is determined by running the far future scheduler,
as described above. The far future scheduler also obtains entropy
from the current blockchain RNG.
As an optimization, the implementation does not run the far future
scheduler until a far-future slot is actually queried. With this
optimization, the only circumstance under which validating nodes must
run the far future scheduler is when a block gap longer than `num_witnesses`
occurs (an extremely rare condition).
Minimizing impact of selective dropout
--------------------------------------
The ability of any single malicious witness to affect the results of the
shuffle algorithm is limited because the RNG is based on bit commitment
of the witnesses. However, a malicious witness *is* able to
refuse to produce a block. A run of `m` consecutively scheduled
malicious witnesses can independently make `m` independent choices
of whether to refuse to produce a block. Basically they are able to
control `m` bits of entropy in the shuffle algorithm's output.
It is difficult-to-impossible to entirely eliminate "the last person
being evil" problem in trustless distributed RNG's. But we can at least
mitigate this vector by rate-limiting changes to the total witness
schedule to a very slow rate.
If every block schedules a witness, our adversary with `m` malicious
witnesses gets `m` chances per round to selectively drop out in order
to manipulate the shuffle order, allowing `m` attacks per round.
If witnesses are only scheduled once per round,
a selective dropout requires the malicious witness to produce the
scheduling block, limiting the probability to `m/n` attacks per round.

1
docs Submodule

@ -0,0 +1 @@
Subproject commit 1b53a8eca77783d073ce7cc95991447c3f34b927

View file

@ -155,10 +155,22 @@ namespace graphene { namespace app {
vector<asset> database_api::get_account_balances(account_id_type acnt, const flat_set<asset_id_type>& assets)const
{
vector<asset> result; result.reserve(assets.size());
std::transform(assets.begin(), assets.end(), std::back_inserter(result),
[this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
vector<asset> result;
if (assets.empty())
{
// if the caller passes in an empty list of assets, return balances for all assets the account owns
const account_balance_index& balance_index = _db.get_index_type<account_balance_index>();
auto range = balance_index.indices().get<by_account>().equal_range(acnt);
for (const account_balance_object& balance : boost::make_iterator_range(range.first, range.second))
result.push_back(asset(balance.get_balance()));
}
else
{
result.reserve(assets.size());
std::transform(assets.begin(), assets.end(), std::back_inserter(result),
[this, acnt](asset_id_type id) { return _db.get_balance(acnt, id); });
}
return result;
}

View file

@ -24,22 +24,13 @@ namespace graphene { namespace chain {
void_result account_create_evaluator::do_evaluate( const account_create_operation& op )
{ try {
FC_ASSERT( db().find_object(op.voting_account) );
FC_ASSERT( is_relative(op.memo_key) || db().find_object(op.memo_key) );
database& d = db();
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() )
{
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();
const auto& global_props = d.get_global_properties();
uint32_t max_vote_id = global_props.next_available_vote_id;
const auto& chain_params = global_props.parameters;
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 );
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 )
{
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];
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));
auto& acnt_indx = db().get_index_type<account_index>();
auto& acnt_indx = d.get_index_type<account_index>();
if( op.name.size() )
{
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
// 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 );
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 ){
if( fee_paying_account->is_prime() )
{
obj.registrar = o.registrar;
obj.referrer = o.referrer;
obj.referrer_percent = o.referrer_percent;
}
else
{
obj.registrar = fee_paying_account->registrar;
obj.referrer = fee_paying_account->referrer;
obj.referrer_percent = fee_paying_account->referrer_percent;
}
obj.registrar = o.registrar;
obj.referrer = o.referrer;
obj.lifetime_referrer = o.referrer(db()).lifetime_referrer;
auto& params = db().get_global_properties().parameters;
obj.network_fee_percentage = params.network_percent_of_fee;
obj.lifetime_referrer_fee_percentage = params.lifetime_referrer_percent_of_fee;
obj.referrer_rewards_percentage = o.referrer_percent;
obj.name = o.name;
obj.owner = owner;
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 )
{
database& d = db();
database& d = db();
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);
if( o.upgrade_to_prime ) FC_ASSERT( !acnt->is_prime() );
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.memo_key ) a.memo_key = *o.memo_key;
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_committee = o.num_committee;
});
@ -200,8 +182,8 @@ void_result account_whitelist_evaluator::do_evaluate(const account_whitelist_ope
database& d = db();
listed_account = &o.account_to_list(d);
if( !d.get_global_properties().parameters.allow_non_prime_whitelists )
FC_ASSERT(listed_account->is_prime());
if( !d.get_global_properties().parameters.allow_non_member_whitelists )
FC_ASSERT(o.authorizing_account(d).is_lifetime_member());
return void_result();
} FC_CAPTURE_AND_RETHROW( (o) ) }
@ -224,4 +206,40 @@ void_result account_whitelist_evaluator::do_apply(const account_whitelist_operat
return void_result();
}
void_result account_upgrade_evaluator::do_evaluate(const account_upgrade_evaluator::operation_type& o)
{
database& d = db();
account = &d.get(o.account_to_upgrade);
FC_ASSERT(!account->is_lifetime_member());
return {};
}
void_result account_upgrade_evaluator::do_apply(const account_upgrade_evaluator::operation_type& o)
{
database& d = db();
d.modify(*account, [&](account_object& a) {
if( o.upgrade_to_lifetime_member )
{
// Upgrade to lifetime member. I don't care what the account was before.
a.membership_expiration_date = time_point_sec::maximum();
a.referrer = a.registrar = a.lifetime_referrer = a.get_id();
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - a.network_fee_percentage;
} else if( a.is_annual_member(d.head_block_time()) ) {
// Renew an annual subscription that's still in effect.
FC_ASSERT(a.membership_expiration_date - d.head_block_time() < fc::days(3650),
"May not extend annual membership more than a decade into the future.");
a.membership_expiration_date += fc::days(365);
} else {
// Upgrade from basic account.
assert(a.is_basic_account(d.head_block_time()));
a.membership_expiration_date = d.head_block_time() + fc::days(365);
}
});
return {};
}
} } // graphene::chain

View file

@ -36,4 +36,24 @@ void account_balance_object::adjust_balance(const asset& delta)
balance += delta.amount;
}
uint16_t account_statistics_object::calculate_bulk_discount_percent(const chain_parameters& params) const
{
uint64_t bulk_discount_percent = 0;
if( lifetime_fees_paid >= params.bulk_discount_threshold_max )
bulk_discount_percent = params.max_bulk_discount_percent_of_fee;
else if(params.bulk_discount_threshold_max.value !=
params.bulk_discount_threshold_min.value)
{
bulk_discount_percent =
(params.max_bulk_discount_percent_of_fee *
(lifetime_fees_paid.value -
params.bulk_discount_threshold_min.value)) /
(params.bulk_discount_threshold_max.value -
params.bulk_discount_threshold_min.value);
}
assert( bulk_discount_percent <= GRAPHENE_100_PERCENT );
return bulk_discount_percent;
}
} } // graphene::chain

View file

@ -1,23 +0,0 @@
/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* 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.
*/
#include <graphene/chain/asset_operations.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
namespace graphene { namespace chain {
} }

View file

@ -89,7 +89,7 @@ void database::adjust_core_in_orders( const account_object& acnt, asset delta )
}
}
void database::deposit_cashback( const account_object& acct, share_type amount )
void database::deposit_cashback(const account_object& acct, share_type amount, bool require_vesting)
{
// If we don't have a VBO, or if it has the wrong maturity
// due to a policy change, cut it loose.
@ -112,7 +112,10 @@ void database::deposit_cashback( const account_object& acct, share_type amount )
modify( cashback_vb, [&]( vesting_balance_object& obj )
{
obj.deposit( now, amount );
if( require_vesting )
obj.deposit(now, amount);
else
obj.deposit_vested(now, amount);
} );
return;
}
@ -124,7 +127,7 @@ void database::deposit_cashback( const account_object& acct, share_type amount )
cdd_vesting_policy policy;
policy.vesting_seconds = global_vesting_seconds;
policy.coin_seconds_earned = 0;
policy.coin_seconds_earned = require_vesting? 0 : amount.value * policy.vesting_seconds;
policy.coin_seconds_earned_last_update = now;
obj.policy = policy;

View file

@ -64,6 +64,7 @@ void database::initialize_evaluators()
register_evaluator<key_create_evaluator>();
register_evaluator<account_create_evaluator>();
register_evaluator<account_update_evaluator>();
register_evaluator<account_upgrade_evaluator>();
register_evaluator<account_whitelist_evaluator>();
register_evaluator<delegate_create_evaluator>();
register_evaluator<custom_evaluator>();
@ -153,6 +154,9 @@ void database::init_genesis(const genesis_allocation& initial_allocation)
});
const account_object& genesis_account =
create<account_object>( [&](account_object& n) {
n.membership_expiration_date = time_point_sec::maximum();
n.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
n.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
n.name = "genesis";
n.owner.add_authority(genesis_key.get_id(), 1);
n.owner.weight_threshold = 1;
@ -174,6 +178,12 @@ void database::init_genesis(const genesis_allocation& initial_allocation)
const account_object& delegate_account =
create<account_object>( [&](account_object& a) {
a.active = a.owner = genesis_account.owner;
a.referrer = account_id_type(i);
a.registrar = account_id_type(i);
a.lifetime_referrer = account_id_type(i);
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
a.membership_expiration_date = fc::time_point_sec::maximum();
a.name = string("init") + fc::to_string(i);
a.statistics = stats_obj.id;
});
@ -209,7 +219,7 @@ void database::init_genesis(const genesis_allocation& initial_allocation)
_wso.scheduler._min_token_count = init_witnesses.size() / 2;
_wso.scheduler.update( init_witness_set );
for( int i=0; i<init_witnesses.size(); i++ )
for( size_t i=0; i<init_witnesses.size(); i++ )
_wso.scheduler.produce_schedule( rng );
_wso.last_scheduling_block = 0;

View file

@ -174,74 +174,6 @@ void database::update_active_delegates()
});
} 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
{
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
@ -310,10 +242,10 @@ void database::process_budget()
// blocks_to_maint > 0 because time_to_maint > 0,
// which means numerator is at least equal to block_interval
share_type available_funds = get_max_budget( now );
share_type available_funds = get_max_budget(now);
share_type witness_budget = gpo.parameters.witness_pay_per_block.value * blocks_to_maint;
witness_budget = std::min( witness_budget, available_funds );
witness_budget = std::min(witness_budget, available_funds);
available_funds -= witness_budget;
fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;
@ -328,21 +260,22 @@ void database::process_budget()
available_funds -= worker_budget;
share_type leftover_worker_funds = worker_budget;
pay_workers( leftover_worker_funds );
pay_workers(leftover_worker_funds);
available_funds += leftover_worker_funds;
modify( core, [&]( asset_dynamic_data_object& _core )
modify(core, [&]( asset_dynamic_data_object& _core )
{
_core.current_supply = (_core.current_supply + witness_budget +
worker_budget - leftover_worker_funds -
_core.accumulated_fees);
_core.accumulated_fees = 0;
} );
modify( dpo, [&]( dynamic_global_property_object& _dpo )
});
modify(dpo, [&]( dynamic_global_property_object& _dpo )
{
// Should this be +=?
_dpo.witness_budget = witness_budget;
_dpo.last_budget_time = now;
} );
});
// available_funds is money we could spend, but don't want to.
// we simply let it evaporate back into the reserve.
@ -352,7 +285,153 @@ void database::process_budget()
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) {}
share_type cut_fee(share_type a, uint16_t p)const
{
if( a == 0 || p == 0 )
return 0;
if( p == GRAPHENE_100_PERCENT )
return a;
fc::uint128 r(a.value);
r *= p;
r /= GRAPHENE_100_PERCENT;
return r.to_uint64();
}
void pay_out_fees(const account_object& account, share_type core_fee_total, bool require_vesting)
{
share_type network_cut = cut_fee(core_fee_total, account.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 lifetime_cut = cut_fee(core_fee_total, account.lifetime_referrer_fee_percentage);
share_type referral = core_fee_total - network_cut - lifetime_cut;
d.modify(dynamic_asset_data_id_type()(d), [network_cut](asset_dynamic_data_object& d) {
d.accumulated_fees += network_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 + accumulated + burned + lifetime_cut == core_fee_total );
}
void operator()(const account_object& a) {
const account_statistics_object& stats = a.statistics(d);
if( stats.pending_fees > 0 )
{
share_type vesting_fee_subtotal(stats.pending_fees);
share_type vested_fee_subtotal(stats.pending_vested_fees);
share_type vesting_cashback, vested_cashback;
if( stats.lifetime_fees_paid > props.parameters.bulk_discount_threshold_min &&
a.is_member(d.head_block_time()) )
{
auto bulk_discount_rate = stats.calculate_bulk_discount_percent(props.parameters);
vesting_cashback = cut_fee(vesting_fee_subtotal, bulk_discount_rate);
vesting_fee_subtotal -= vesting_cashback;
vested_cashback = cut_fee(vested_fee_subtotal, bulk_discount_rate);
vested_fee_subtotal -= vested_cashback;
}
pay_out_fees(a, vesting_fee_subtotal, true);
d.deposit_cashback(a, vesting_cashback, true);
pay_out_fees(a, vested_fee_subtotal, false);
d.deposit_cashback(a, vested_cashback, false);
d.modify(stats, [vested_fee_subtotal, vesting_fee_subtotal](account_statistics_object& s) {
s.lifetime_fees_paid += vested_fee_subtotal + vesting_fee_subtotal;
s.pending_fees = 0;
s.pending_vested_fees = 0;
});
}
}
} fee_helper(*this, gpo);
perform_account_maintenance(std::tie(tally_helper, fee_helper));
struct clear_canary {
clear_canary(vector<uint64_t>& target): target(target){}
@ -367,9 +446,8 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
update_active_witnesses();
update_active_delegates();
const global_property_object& global_properties = get_global_properties();
if( global_properties.pending_parameters )
modify(get_global_properties(), [](global_property_object& p) {
if( gpo.pending_parameters )
modify(gpo, [](global_property_object& p) {
p.parameters = std::move(*p.pending_parameters);
p.pending_parameters.reset();
});
@ -388,7 +466,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
}
auto next_maintenance_time = get<dynamic_global_property_object>(dynamic_global_property_id_type()).next_maintenance_time;
auto maintenance_interval = get_global_properties().parameters.maintenance_interval;
auto maintenance_interval = gpo.parameters.maintenance_interval;
if( next_maintenance_time <= next_block.timestamp )
{

View file

@ -35,7 +35,7 @@ pair<witness_id_type, bool> database::get_scheduled_witness(uint32_t slot_num)co
// ask the near scheduler who goes in the given slot
witness_id_type wid;
bool slot_is_near = wso.scheduler.get_slot(slot_num, wid);
bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid);
if( ! slot_is_near )
{
// if the near scheduler doesn't know, we have to extend it to
@ -47,7 +47,7 @@ pair<witness_id_type, bool> database::get_scheduled_witness(uint32_t slot_num)co
far_future_witness_scheduler far_scheduler =
far_future_witness_scheduler(wso.scheduler, far_rng);
if( !far_scheduler.get_slot(slot_num, wid) )
if( !far_scheduler.get_slot(slot_num-1, wid) )
{
// no scheduled witness -- somebody set up us the bomb
// n.b. this code path is impossible, the present
@ -84,7 +84,7 @@ vector<witness_id_type> database::get_near_witness_schedule()const
vector<witness_id_type> result;
result.reserve(wso.scheduler.size());
uint32_t slot_num = 1;
uint32_t slot_num = 0;
witness_id_type wid;
while( wso.scheduler.get_slot(slot_num++, wid) )

View file

@ -24,7 +24,7 @@
namespace graphene { namespace chain {
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();
}

View file

@ -61,55 +61,17 @@ namespace graphene { namespace chain {
void generic_evaluator::pay_fee()
{ 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() )
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.fee_pool -= core_fee_paid;
});
d.modify(dynamic_asset_data_id_type()(d), [burned,accumulated](asset_dynamic_data_object& d) {
d.accumulated_fees += accumulated + burned;
db().modify(*fee_paying_account_statistics, [&](account_statistics_object& s) {
if( core_fee_paid > db().get_global_properties().parameters.cashback_vesting_threshold )
s.pending_fees += core_fee_paid;
else
s.pending_vested_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() }
bool generic_evaluator::verify_authority( const account_object& a, authority::classification c )

View file

@ -21,35 +21,46 @@
namespace graphene { namespace chain {
class account_create_evaluator : public evaluator<account_create_evaluator>
{
public:
typedef account_create_operation operation_type;
class account_create_evaluator : public evaluator<account_create_evaluator>
{
public:
typedef account_create_operation operation_type;
void_result do_evaluate( const account_create_operation& o );
object_id_type do_apply( const account_create_operation& o ) ;
};
void_result do_evaluate( const account_create_operation& o );
object_id_type do_apply( const account_create_operation& o ) ;
};
class account_update_evaluator : public evaluator<account_update_evaluator>
{
public:
typedef account_update_operation operation_type;
class account_update_evaluator : public evaluator<account_update_evaluator>
{
public:
typedef account_update_operation operation_type;
void_result do_evaluate( const account_update_operation& o );
void_result do_apply( const account_update_operation& o );
void_result do_evaluate( const account_update_operation& o );
void_result do_apply( const account_update_operation& o );
const account_object* acnt;
};
const account_object* acnt;
};
class account_whitelist_evaluator : public evaluator<account_whitelist_evaluator>
{
public:
typedef account_whitelist_operation operation_type;
class account_upgrade_evaluator : public evaluator<account_upgrade_evaluator>
{
public:
typedef account_upgrade_operation operation_type;
void_result do_evaluate( const account_whitelist_operation& o);
void_result do_apply( const account_whitelist_operation& o);
void_result do_evaluate(const operation_type& o);
void_result do_apply(const operation_type& o);
const account_object* listed_account;
};
const account_object* account;
};
class account_whitelist_evaluator : public evaluator<account_whitelist_evaluator>
{
public:
typedef account_whitelist_operation operation_type;
void_result do_evaluate( const account_whitelist_operation& o);
void_result do_apply( const account_whitelist_operation& o);
const account_object* listed_account;
};
} } // graphene::chain

View file

@ -39,32 +39,43 @@ namespace graphene { namespace chain {
static const uint8_t type_id = impl_account_statistics_object_type;
/**
* Keep the most recent operation as a root pointer to
* 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 because
* transaction history is so common and this object is already
* 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.
* Keep the most recent operation as a root pointer to 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
* because transaction history is so common and this object is already 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;
/**
* When calculating votes it is necessary to know how much is
* stored in orders (and thus unavailable for transfers). Rather
* than maintaining an index of [asset,owner,order_id] we will
* simply maintain the running total here and update it every
* time an order is created or modified.
* When calculating votes it is necessary to know how much is stored in orders (and thus unavailable for
* transfers). Rather than maintaining an index of [asset,owner,order_id] we will 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
* of calculating bulk discounts.
* Tracks the total fees paid by this account for the purpose 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 arithmetic on each and every operation.
*
*These fees will be paid out as vesting cash-back, and this counter will reset during the maintenance
*interval.
*/
share_type pending_fees;
/**
* Same as @ref pending_fees, except these fees will be paid out as pre-vested cash-back (immediately
* available for withdrawal) rather than requiring the normal vesting period.
*/
share_type pending_vested_fees;
/// @brief Calculate the percentage discount this user receives on his fees
uint16_t calculate_bulk_discount_percent(const chain_parameters& params)const;
};
/**
@ -102,53 +113,54 @@ namespace graphene { namespace chain {
public:
static const uint8_t space_id = protocol_ids;
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
* users to refer each other.
* The time at which this account's membership expires.
* 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;
/**
* Any referral fees not paid to referrer are paid to registrar
*/
uint8_t referrer_percent = 0;
///The account that paid the fee to register this account. Receives a percentage of referral rewards.
account_id_type registrar;
/// The account credited as referring this account. Receives a percentage of referral rewards.
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.
string name;
/// The account's name. This name must be unique among all account names on the graph. May not be empty.
string name;
/**
* 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
* complete and irrevocable loss of the account. Generally the only time the owner authority is required is to
* 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
/// 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-
/// validated account activities. This field is here to prevent confusion if the active authority has zero or
/// 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
/// will be counted as voting for the referenced account's selected votes instead.
account_id_type voting_account;
uint16_t num_witness = 0;
uint16_t num_committee = 0;
account_id_type voting_account;
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
/// account's balance of core asset.
flat_set<vote_id_type> votes;
@ -163,7 +175,7 @@ namespace graphene { namespace chain {
* 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.
*/
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
@ -171,19 +183,33 @@ namespace graphene { namespace chain {
* 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.
*/
flat_set<account_id_type> blacklisting_accounts;
flat_set<account_id_type> blacklisting_accounts;
/**
* Vesting balance which receives cashback_reward deposits.
*/
optional<vesting_balance_id_type> cashback_vb;
/**
* @return true if this is a prime account, false otherwise.
*/
bool is_prime()const
/// @return true if this is a lifetime member account; false otherwise.
bool is_lifetime_member()const
{
return get_id() == referrer;
return membership_expiration_date == time_point_sec::maximum();
}
/// @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);
}
/**
@ -253,9 +279,12 @@ namespace graphene { namespace chain {
typedef generic_index<account_object, account_object_multi_index_type> account_index;
}}
FC_REFLECT_DERIVED( 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) )
FC_REFLECT_DERIVED( graphene::chain::account_balance_object,
@ -270,5 +299,6 @@ FC_REFLECT_DERIVED( graphene::chain::account_statistics_object, (graphene::chain
(most_recent_op)
(total_core_in_orders)
(lifetime_fees_paid)
(pending_fees)(pending_vested_fees)
)

View file

@ -49,6 +49,7 @@
#define GRAPHENE_DEFAULT_TRANSFER_FEE (1*GRAPHENE_BLOCKCHAIN_PRECISION)
#define GRAPHENE_MAX_INSTANCE_ID (uint64_t(-1)>>16)
#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 */
#define GRAPHENE_MAX_MARKET_FEE_PERCENT GRAPHENE_100_PERCENT
#define GRAPHENE_DEFAULT_FORCE_SETTLEMENT_DELAY (60*60*24) ///< 1 day
@ -72,13 +73,14 @@
#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_GENESIS_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks
#define GRAPHENE_DEFAULT_WITNESS_PERCENT (10000/100) // 1%
#define GRAPHENE_DEFAULT_MAX_BULK_DISCOUNT_PERCENT (10000/2) // 50%
#define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
#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_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_BURN_PERCENT_OF_FEE (10000/5) // 20%
#define GRAPHENE_DEFAULT_WITNESS_PAY_PERCENT_OF_ACCUMULATED ( 1728000) /// gives a half life of 1 year assuming 1 second blocks
#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_WITNESS_PAY_PERCENT_PRECISION (1000000000)
#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/asset.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/fork_database.hpp>
@ -253,7 +254,7 @@ namespace graphene { namespace chain {
void adjust_core_in_orders( const account_object& acnt, asset delta );
// helper to handle cashback rewards
void deposit_cashback( const account_object& acct, share_type amount );
void deposit_cashback(const account_object& acct, share_type amount, bool require_vesting = true);
//////////////////// db_debug.cpp ////////////////////
@ -357,7 +358,9 @@ namespace graphene { namespace chain {
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
void update_active_witnesses();
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,30 @@ namespace graphene { namespace chain {
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)... };
(void)l;
}
}
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
* 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.
*
@ -113,7 +113,7 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return fee_paying_account; }
void get_required_auth(flat_set<account_id_type>& active_auth_set , flat_set<account_id_type>&)const;
share_type calculate_fee( const fee_schedule_type& k )const{ return k.at( key_create_fee_type ); }
share_type calculate_fee( const fee_schedule_type& k )const{ return k.key_create_fee; }
void validate()const;
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); }
@ -125,14 +125,13 @@ namespace graphene { namespace chain {
struct account_create_operation
{
asset fee;
/// This account pays the fee. Must be a lifetime member.
account_id_type registrar;
/**
* 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.
*/
/// This account receives a portion of the fee split between registrar and referrer. Must be a member.
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;
string name;
@ -194,7 +193,7 @@ namespace graphene { namespace chain {
account_id_type fee_payer()const { return authorizing_account; }
void get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const;
void validate()const { FC_ASSERT( fee.amount >= 0 ); FC_ASSERT(new_listing < 0x4); }
share_type calculate_fee(const fee_schedule_type& k)const { return k.at(account_whitelist_fee_type); }
share_type calculate_fee(const fee_schedule_type& k)const { return k.account_whitelist_fee; }
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); }
};
@ -214,12 +213,6 @@ namespace graphene { namespace chain {
uint16_t num_witness = 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; }
void get_required_auth(flat_set<account_id_type>& active_auth_set , flat_set<account_id_type>& owner_auth_set)const;
void validate()const;
@ -227,6 +220,64 @@ namespace graphene { namespace chain {
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); }
};
/**
* @brief Manage an account's membership status
* @ingroup operations
*
* This operation is used to upgrade an account to a member, or renew its subscription. If an account which is an
* unexpired annual subscription member publishes this operation with @ref upgrade_to_lifetime_member set to false,
* the account's membership expiration date will be pushed backward one year. If a basic account publishes it with
* @ref upgrade_to_lifetime_member set to false, the account will be upgraded to a subscription member with an
* expiration date one year after the processing time of this operation.
*
* Any account may use this operation to become a lifetime member by setting @ref upgrade_to_lifetime_member to
* true. Once an account has become a lifetime member, it may not use this operation anymore.
*/
struct account_upgrade_operation
{
asset fee;
/// The account to upgrade; must not already be a lifetime member
account_id_type account_to_upgrade;
/// If true, the account will be upgraded to a lifetime member; otherwise, it will add a year to the subscription
bool upgrade_to_lifetime_member = false;
account_id_type fee_payer()const { return account_to_upgrade; }
void get_required_auth(flat_set<account_id_type>& active_auth_set , flat_set<account_id_type>&)const
{ active_auth_set.insert(account_to_upgrade); }
void validate()const;
share_type calculate_fee( const fee_schedule_type& k )const;
void get_balance_delta( balance_accumulator& acc, const operation_result& = asset())const { acc.adjust( fee_payer(), -fee ); }
};
/**
* @brief transfers the account to another account while clearing the white list
* @ingroup operations
*
* In theory an account can be transferred by simply updating the authorities, but that kind
* of transfer lacks semantic meaning and is more often done to rotate keys without transferring
* ownership. This operation is used to indicate the legal transfer of title to this account and
* a break in the operation history.
*
* The account_id's owner/active/voting/memo authority should be set to new_owner
*
* This operation will clear the account's whitelist statuses, but not the blacklist statuses.
*/
struct account_transfer_operation
{
asset fee;
account_id_type account_id;
account_id_type new_owner;
account_id_type fee_payer()const { return account_id; }
void get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const;
void validate()const;
share_type calculate_fee( const fee_schedule_type& k )const;
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); }
};
<<<<<<< HEAD
=======
/**
* @brief Create a delegate object, as a bid to hold a delegate seat on the network.
* @ingroup operations
@ -246,35 +297,7 @@ namespace graphene { namespace chain {
share_type calculate_fee( const fee_schedule_type& k )const;
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); }
};
/**
* @brief transfers the account to another account while clearing the white list
* @ingroup operations
*
* In theory an account can be transferred by simply updating the authorities, but that kind
* of transfer lacks semantic meaning and is more often done to rotate keys without transferring
* ownership. This operation is used to indicate the legal transfer of title to this account and
* a break in the operation history.
*
* The account_id's owner/active/voting/memo authority should be set to new_owner
*
* This operation will clear the account's whitelist statuses, but not the blacklist statuses.
*/
struct account_transfer_operation
{
asset fee;
account_id_type account_id;
account_id_type new_owner;
account_id_type fee_payer()const { return account_id; }
void get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const;
void validate()const;
share_type calculate_fee( const fee_schedule_type& k )const;
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const { acc.adjust( fee_payer(), -fee ); }
};
>>>>>>> dfee44a40827e40becf4d8f21bff5b2bfa1303a2
/**
* @brief Create a witness object, as a bid to hold a witness position on the network.
@ -756,7 +779,7 @@ namespace graphene { namespace chain {
{ active_auth_set.insert(fee_payer()); }
void validate()const;
share_type calculate_fee( const fee_schedule_type& k )const
{ return k.at(asset_update_fee_type); }
{ return k.asset_update_fee; }
void get_balance_delta( balance_accumulator& acc, const operation_result& result = asset())const
{ acc.adjust( fee_payer(), -fee ); }
};
@ -1198,13 +1221,13 @@ namespace graphene { namespace chain {
* 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
* make a withdrawal. Any number of withdraws may be made so long as the total amount withdrawn is less than the limit
* for any given period.
* make a withdrawal. Any number of withdrawals may be made so long as the total amount withdrawn per period does
* not exceed the limit for any given period.
*
* 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
* the withdrawal limit. The withdrawal must be made in the same asset as the limit; attempts with withdraw any other
* asset type will be rejected.
* the withdrawal limit. The withdrawal must be made in the same asset as the limit; attempts with withdraw any
* 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
* operation.
@ -1682,6 +1705,7 @@ namespace graphene { namespace chain {
account_create_operation,
account_update_operation,
account_whitelist_operation,
account_upgrade_operation,
account_transfer_operation,
asset_create_operation,
asset_update_operation,
@ -1856,10 +1880,10 @@ FC_REFLECT( graphene::chain::account_create_operation,
(num_witness)(num_committee)(vote)
)
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
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( graphene::chain::account_upgrade_operation, (fee)(account_to_upgrade)(upgrade_to_lifetime_member) )
FC_REFLECT_TYPENAME( graphene::chain::account_whitelist_operation::account_listing)
FC_REFLECT_ENUM( graphene::chain::account_whitelist_operation::account_listing,
@ -1968,5 +1992,9 @@ FC_REFLECT( graphene::chain::blind_transfer_operation,
(to_account)(to_account_name)(to_address)(to_amount) )
FC_REFLECT_TYPENAME( graphene::chain::operation )
<<<<<<< HEAD
=======
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
>>>>>>> dfee44a40827e40becf4d8f21bff5b2bfa1303a2

View file

@ -6,6 +6,8 @@
#include <graphene/chain/types.hpp>
#include <graphene/chain/operations.hpp>
#include <numeric>
// this is for htonl() and ntohl() functions
// TODO: write and use FC wrappers for these functions
#ifndef WIN32

View file

@ -89,6 +89,7 @@ namespace graphene { namespace chain {
};
inline bool is_relative( object_id_type o ){ return o.space() == 0; }
<<<<<<< HEAD
/**
* There are many types of fees charged by the network
* for different operations. These fees are published by
@ -137,6 +138,8 @@ namespace graphene { namespace chain {
blind_transfer_fee_type, ///< the cost per killobyte of the blinded transfer size
FEE_TYPE_COUNT ///< Sentry value which contains the number of different fee types
};
=======
>>>>>>> dfee44a40827e40becf4d8f21bff5b2bfa1303a2
/**
* List all object types from all namespaces here so they can
@ -376,6 +379,7 @@ namespace graphene { namespace chain {
struct fee_schedule_type
{
<<<<<<< HEAD
fee_schedule_type()
{
memset( (char*)this, 0, sizeof(*this) );
@ -424,6 +428,74 @@ namespace graphene { namespace chain {
uint32_t worker_create_fee; ///< the cost to create a new worker
uint32_t worker_delete_fee; ///< the cost to delete a worker
uint32_t blind_transfer_fee; ///< cost per kb for blind transfers
=======
/**
* @brief The fee_set_visitor struct sets all fees to a particular value in one fell swoop
*
* Example:
* @code
* fee_schedule_type sch;
* // Set all fees to 50
* fc::reflector<fee_schedule_type>::visit(fee_schedule_type::fee_set_visitor{sch, 50});
* @endcode
*/
struct fee_set_visitor {
fee_schedule_type& f;
uint32_t fee;
template<typename Member, typename Class, Member (Class::*member)>
void operator()(const char*)const
{
f.*member = fee;
}
};
fee_schedule_type()
{
memset( (char*)this, 0, sizeof(*this) );
}
uint32_t key_create_fee; ///< the cost to register a public key with the blockchain
uint32_t account_create_fee; ///< the cost to register the cheapest non-free account
uint32_t account_len8_fee;
uint32_t account_len7_fee;
uint32_t account_len6_fee;
uint32_t account_len5_fee;
uint32_t account_len4_fee;
uint32_t account_len3_fee;
uint32_t account_len2_fee;
uint32_t account_premium_fee; ///< accounts with premium names; i.e. @ref is_cheap_name returns false
uint32_t account_whitelist_fee; ///< the fee to whitelist an account
uint32_t delegate_create_fee; ///< fixed fee for registering as a delegate; used to discourage frivioulous delegates
uint32_t witness_withdraw_pay_fee; ///< fee for withdrawing witness pay
uint32_t transfer_fee; ///< fee for transferring some asset
uint32_t limit_order_fee; ///< fee for placing a limit order in the markets
uint32_t short_order_fee; ///< fee for placing a short order in the markets
uint32_t publish_feed_fee; ///< fee for publishing a price feed
uint32_t asset_create_fee; ///< the cost to register the cheapest asset
uint32_t asset_update_fee; ///< the cost to modify a registered asset
uint32_t asset_issue_fee; ///< the cost to modify a registered asset
uint32_t asset_fund_fee_pool_fee; ///< the cost to add funds to an asset's fee pool
uint32_t asset_settle_fee; ///< the cost to trigger a forced settlement of a market-issued asset
uint32_t market_fee; ///< a percentage charged on market orders
uint32_t transaction_fee; ///< a base price for every transaction
uint32_t data_fee; ///< a price per 1024 bytes of user data
uint32_t signature_fee; ///< a surcharge on transactions with more than 2 signatures.
uint32_t global_parameters_update_fee; ///< the cost to update the global parameters
uint32_t membership_annual_fee; ///< the annual cost of a membership subscription
uint32_t membership_lifetime_fee; ///< the cost to upgrade to a lifetime member
uint32_t withdraw_permission_update_fee; ///< the cost to create/update a withdraw permission
uint32_t create_bond_offer_fee;
uint32_t cancel_bond_offer_fee;
uint32_t accept_bond_offer_fee;
uint32_t claim_bond_collateral_fee;
uint32_t file_storage_fee_per_day; ///< the cost of leasing a file with 2^16 bytes for 1 day
uint32_t vesting_balance_create_fee;
uint32_t vesting_balance_withdraw_fee;
uint32_t global_settle_fee;
uint32_t worker_create_fee; ///< the cost to create a new worker
uint32_t worker_delete_fee; ///< the cost to delete a worker
>>>>>>> dfee44a40827e40becf4d8f21bff5b2bfa1303a2
};
@ -452,8 +524,7 @@ namespace graphene { namespace chain {
struct chain_parameters
{
fee_schedule_type current_fees; ///< current schedule of fees, indexed by @ref fee_type
uint32_t witness_pay_percent_of_accumulated = GRAPHENE_DEFAULT_WITNESS_PAY_PERCENT_OF_ACCUMULATED; ///< percentage of accumulated fees in core asset to pay to witnesses for block production
fee_schedule_type current_fees; ///< current schedule of fees
uint8_t block_interval = GRAPHENE_DEFAULT_BLOCK_INTERVAL; ///< interval in seconds between blocks
uint32_t maintenance_interval = GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL; ///< interval in sections between blockchain maintenance events
uint32_t maximum_transaction_size = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE; ///< maximum allowable size in bytes for a transaction
@ -467,27 +538,29 @@ namespace graphene { namespace chain {
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_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 witness_percent_of_fee = GRAPHENE_DEFAULT_WITNESS_PERCENT; ///< percent of revenue paid to witnesses
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 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
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
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
bool count_non_prime_votes = true; ///< set to false to restrict voting privlegages to prime accounts
bool allow_non_prime_whitelists = false; ///< true if non-prime accounts may set whitelists and blacklists; false otherwise
bool count_non_member_votes = true; ///< set to false to restrict voting privlegages to member accounts
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 worker_budget_per_day = GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY; ///< CORE to be allocated to workers (per day)
void validate()const
{
FC_ASSERT( witness_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( 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 > 0 );
FC_ASSERT( witness_pay_percent_of_accumulated < GRAPHENE_WITNESS_PAY_PERCENT_PRECISION );
FC_ASSERT( block_interval <= GRAPHENE_MAX_BLOCK_INTERVAL );
FC_ASSERT( block_interval > 0 );
FC_ASSERT( maintenance_interval > block_interval,
@ -502,7 +575,6 @@ namespace graphene { namespace chain {
"Maximum transaction expiration time must be greater than a block interval" );
FC_ASSERT( maximum_proposal_lifetime - genesis_proposal_review_period > block_interval,
"Genesis proposal review period must be less than the maximum proposal lifetime" );
for( uint32_t i = 0; i < FEE_TYPE_COUNT; ++i ) { FC_ASSERT( current_fees.at(i) >= 0 ); }
}
};
@ -574,6 +646,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type,
(account_len5_fee)
(account_len4_fee)
(account_len3_fee)
(account_len2_fee)
(account_premium_fee)
(account_whitelist_fee)
(delegate_create_fee)
@ -592,7 +665,8 @@ FC_REFLECT( graphene::chain::fee_schedule_type,
(data_fee)
(signature_fee)
(global_parameters_update_fee)
(prime_upgrade_fee)
(membership_annual_fee)
(membership_lifetime_fee)
(withdraw_permission_update_fee)
(create_bond_offer_fee)
(cancel_bond_offer_fee)
@ -606,6 +680,7 @@ FC_REFLECT( graphene::chain::fee_schedule_type,
(worker_delete_fee)
)
<<<<<<< HEAD
FC_REFLECT_ENUM( graphene::chain::fee_type,
(key_create_fee_type)
@ -650,9 +725,10 @@ FC_REFLECT_ENUM( graphene::chain::fee_type,
(FEE_TYPE_COUNT)
)
=======
>>>>>>> dfee44a40827e40becf4d8f21bff5b2bfa1303a2
FC_REFLECT( graphene::chain::chain_parameters,
(current_fees)
(witness_pay_percent_of_accumulated)
(block_interval)
(maintenance_interval)
(maximum_transaction_size)
@ -664,13 +740,15 @@ FC_REFLECT( graphene::chain::chain_parameters,
(maximum_asset_feed_publishers)
(maximum_authority_membership)
(burn_percent_of_fee)
(witness_percent_of_fee)
(network_percent_of_fee)
(lifetime_referrer_percent_of_fee)
(max_bulk_discount_percent_of_fee)
(cashback_vesting_period_seconds)
(cashback_vesting_threshold)
(bulk_discount_threshold_min)
(bulk_discount_threshold_max)
(count_non_prime_votes)
(allow_non_prime_whitelists)
(count_non_member_votes)
(allow_non_member_whitelists)
(witness_pay_per_block)
(worker_budget_per_day)
)

View file

@ -35,9 +35,9 @@ namespace graphene { namespace chain {
vesting_policy_context(
asset _balance,
fc::time_point_sec _now,
asset _amount )
: balance( _balance ), now( _now ), amount( _amount ) {}
asset _amount)
: balance(_balance), now(_now), amount(_amount) {}
asset balance;
fc::time_point_sec now;
asset amount;
@ -53,11 +53,14 @@ namespace graphene { namespace chain {
share_type begin_balance; // same asset as balance
share_type total_withdrawn; // same asset as balance
asset get_allowed_withdraw( const vesting_policy_context& ctx )const;
bool is_deposit_allowed( const vesting_policy_context& ctx )const;
bool is_withdraw_allowed( const vesting_policy_context& ctx )const;
void on_deposit( const vesting_policy_context& ctx );
void on_withdraw( const vesting_policy_context& ctx );
asset get_allowed_withdraw(const vesting_policy_context& ctx)const;
bool is_deposit_allowed(const vesting_policy_context& ctx)const;
bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; }
bool is_withdraw_allowed(const vesting_policy_context& ctx)const;
void on_deposit(const vesting_policy_context& ctx);
void on_deposit_vested(const vesting_policy_context&)
{ FC_THROW( "May not deposit vested into a linear vesting balance." ); }
void on_withdraw(const vesting_policy_context& ctx);
};
struct cdd_vesting_policy
@ -71,20 +74,22 @@ namespace graphene { namespace chain {
* non-destructively figure out how many coin seconds
* are available.
*/
fc::uint128_t compute_coin_seconds_earned( const vesting_policy_context& ctx )const;
fc::uint128_t compute_coin_seconds_earned(const vesting_policy_context& ctx)const;
/**
* Update coin_seconds_earned and
* coin_seconds_earned_last_update fields; called by both
* on_deposit() and on_withdraw().
*/
void update_coin_seconds_earned( const vesting_policy_context& ctx );
void update_coin_seconds_earned(const vesting_policy_context& ctx);
asset get_allowed_withdraw( const vesting_policy_context& ctx )const;
bool is_deposit_allowed( const vesting_policy_context& ctx )const;
bool is_withdraw_allowed( const vesting_policy_context& ctx )const;
void on_deposit( const vesting_policy_context& ctx );
void on_withdraw( const vesting_policy_context& ctx );
asset get_allowed_withdraw(const vesting_policy_context& ctx)const;
bool is_deposit_allowed(const vesting_policy_context& ctx)const;
bool is_deposit_vested_allowed(const vesting_policy_context& ctx)const;
bool is_withdraw_allowed(const vesting_policy_context& ctx)const;
void on_deposit(const vesting_policy_context& ctx);
void on_deposit_vested(const vesting_policy_context& ctx);
void on_withdraw(const vesting_policy_context& ctx);
};
typedef fc::static_variant<
@ -102,17 +107,21 @@ namespace graphene { namespace chain {
static const uint8_t space_id = protocol_ids;
static const uint8_t type_id = vesting_balance_object_type;
account_id_type owner;
asset balance;
vesting_policy policy;
account_id_type owner;
asset balance;
vesting_policy policy;
vesting_balance_object() {}
/**
* Used to increase existing vesting balances.
*/
void deposit( const fc::time_point_sec& now, const asset& amount );
bool is_deposit_allowed( const fc::time_point_sec& now, const asset& amount )const;
void deposit(const fc::time_point_sec& now, const asset& amount);
bool is_deposit_allowed(const fc::time_point_sec& now, const asset& amount)const;
/// @brief Deposit amount into vesting balance, making the new funds vest immediately
void deposit_vested(const fc::time_point_sec& now, const asset& amount);
bool is_deposit_vested_allowed(const fc::time_point_sec& now, const asset& amount)const;
/**
* Used to remove a vesting balance from the VBO. As well
@ -122,27 +131,27 @@ namespace graphene { namespace chain {
* The money doesn't "go" anywhere; the caller is responsible
* for crediting it to the proper account.
*/
void withdraw( const fc::time_point_sec& now, const asset& amount );
bool is_withdraw_allowed( const fc::time_point_sec& now, const asset& amount )const;
void withdraw(const fc::time_point_sec& now, const asset& amount);
bool is_withdraw_allowed(const fc::time_point_sec& now, const asset& amount)const;
};
} } // graphene::chain
FC_REFLECT( graphene::chain::linear_vesting_policy,
(vesting_seconds)
(begin_date)
(begin_balance)
(total_withdrawn)
)
FC_REFLECT(graphene::chain::linear_vesting_policy,
(vesting_seconds)
(begin_date)
(begin_balance)
(total_withdrawn)
)
FC_REFLECT( graphene::chain::cdd_vesting_policy,
(vesting_seconds)
(coin_seconds_earned)
(coin_seconds_earned_last_update)
)
FC_REFLECT(graphene::chain::cdd_vesting_policy,
(vesting_seconds)
(coin_seconds_earned)
(coin_seconds_earned_last_update)
)
FC_REFLECT_DERIVED( graphene::chain::vesting_balance_object, (graphene::db::object),
(owner)
(balance)
(policy)
)
FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object),
(owner)
(balance)
(policy)
)

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
* 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.
* Any number of withdraws may be made so long as the total amount withdrawn is less than the limit for any given
* period.
* and withdraw up to @ref withdrawal_limit from @ref withdraw_from_account. Only @ref authorized_account may do
* this. Any number of withdrawals may be made so long as the total amount withdrawn per period does not exceed the
* limit for any given period.
*/
class withdraw_permission_object : public graphene::db::abstract_object<withdraw_permission_object>
{
@ -54,14 +54,14 @@ namespace graphene { namespace chain {
/// tracks the total amount
share_type claimed_this_period;
/// 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 )
return withdrawal_limit;
return asset(
( withdrawal_limit.amount > claimed_this_period )
? withdrawal_limit.amount - claimed_this_period
: 0, withdrawal_limit.asset_id );
: 0, withdrawal_limit.asset_id );
}
};

View file

@ -95,22 +95,33 @@ bool is_cheap_name( const string& n )
share_type account_create_operation::calculate_fee( const fee_schedule_type& schedule )const
{
auto core_fee_required = schedule.at(account_create_fee_type);
auto core_fee_required = schedule.account_create_fee;
uint32_t s = name.size();
if( is_cheap_name( name ) ) s = 63;
FC_ASSERT( s >= 2 );
if( s <= 8 )
core_fee_required = schedule.at(account_create_fee_type+9-s);
if( s == 8 )
core_fee_required = schedule.account_len8_fee;
else if( s == 7 )
core_fee_required = schedule.account_len7_fee;
else if( s == 6 )
core_fee_required = schedule.account_len6_fee;
else if( s == 5 )
core_fee_required = schedule.account_len5_fee;
else if( s == 4 )
core_fee_required = schedule.account_len4_fee;
else if( s == 3 )
core_fee_required = schedule.account_len3_fee;
else if( s == 2 )
core_fee_required = schedule.account_len2_fee;
return core_fee_required;
}
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.account_create_fee;
}
void account_update_operation::get_required_auth(flat_set<account_id_type>& active_auth_set,
flat_set<account_id_type>& owner_auth_set) const
@ -125,13 +136,13 @@ void account_update_operation::validate()const
{
FC_ASSERT( fee.amount >= 0 );
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 );
}
share_type asset_create_operation::calculate_fee( const fee_schedule_type& schedule )const
{
auto core_fee_required = schedule.at(asset_create_fee_type);
auto core_fee_required = schedule.asset_create_fee;
uint32_t s = symbol.size();
while( s <= 6 ) { core_fee_required *= 30; ++s; }
@ -141,10 +152,10 @@ share_type asset_create_operation::calculate_fee( const fee_schedule_type& sched
share_type transfer_operation::calculate_fee( const fee_schedule_type& schedule )const
{
auto core_fee_required = schedule.at( transfer_fee_type );
share_type core_fee_required = schedule.transfer_fee;
if( memo )
{
core_fee_required += share_type((memo->message.size() * schedule.at( data_fee_type ).value)/1024);
core_fee_required += share_type((memo->message.size() * schedule.data_fee)/1024);
}
return core_fee_required;
}
@ -195,7 +206,7 @@ void account_create_operation::validate()const
share_type asset_publish_feed_operation::calculate_fee( const fee_schedule_type& schedule )const
{
return schedule.at( publish_feed_fee_type );
return schedule.publish_feed_fee;
}
void asset_publish_feed_operation::validate()const
@ -266,7 +277,7 @@ void asset_update_operation::validate()const
share_type asset_update_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( asset_update_fee_type );
return k.asset_update_fee;
}
void asset_burn_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -283,7 +294,7 @@ void asset_burn_operation::validate()const
share_type asset_burn_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( asset_issue_fee_type );
return k.asset_issue_fee;
}
void asset_issue_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -301,12 +312,12 @@ void asset_issue_operation::validate()const
share_type asset_issue_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( asset_issue_fee_type );
return k.asset_issue_fee;
}
share_type delegate_create_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( delegate_create_fee_type ) ;
return k.delegate_create_fee ;
}
void delegate_create_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -333,7 +344,7 @@ void asset_fund_fee_pool_operation::validate() const
share_type asset_fund_fee_pool_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( asset_fund_fee_pool_fee_type );
return k.asset_fund_fee_pool_fee;
}
void limit_order_create_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -351,7 +362,7 @@ void limit_order_create_operation::validate()const
share_type limit_order_create_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( limit_order_fee_type );
return k.limit_order_fee;
}
void limit_order_cancel_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -366,7 +377,7 @@ void limit_order_cancel_operation::validate()const
share_type limit_order_cancel_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( limit_order_fee_type );
return k.limit_order_fee;
}
void short_order_create_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -384,7 +395,7 @@ void short_order_create_operation::validate()const
share_type short_order_create_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( short_order_fee_type );
return k.short_order_fee;
}
void short_order_cancel_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
{
@ -398,7 +409,7 @@ void short_order_cancel_operation::validate()const
share_type short_order_cancel_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( short_order_fee_type );
return k.short_order_fee;
}
void call_order_update_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -420,7 +431,7 @@ void call_order_update_operation::validate()const
share_type call_order_update_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( short_order_fee_type );
return k.short_order_fee;
}
proposal_create_operation proposal_create_operation::genesis_proposal(const database& db)
@ -502,7 +513,7 @@ void account_transfer_operation::get_required_auth(flat_set<account_id_type>& ac
share_type account_transfer_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at(transfer_fee_type);
return k.transfer_fee;
}
@ -524,7 +535,7 @@ void witness_withdraw_pay_operation::validate() const
share_type witness_withdraw_pay_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at(witness_withdraw_pay_fee_type);
return k.witness_withdraw_pay_fee;
}
void account_whitelist_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -540,7 +551,7 @@ void global_parameters_update_operation::validate() const
share_type global_parameters_update_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at(global_parameters_update_fee_type);
return k.global_parameters_update_fee;
}
void witness_create_operation::get_required_auth(flat_set<graphene::chain::account_id_type>& active_auth_set, flat_set<graphene::chain::account_id_type>&) const
@ -555,7 +566,7 @@ void witness_create_operation::validate() const
share_type witness_create_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at(delegate_create_fee_type);
return k.delegate_create_fee;
}
void withdraw_permission_update_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const
@ -574,7 +585,7 @@ void withdraw_permission_update_operation::validate()const
share_type withdraw_permission_update_operation::calculate_fee( const fee_schedule_type& schedule )const
{
return schedule.at( withdraw_permission_update_fee_type );
return schedule.withdraw_permission_update_fee;
}
void withdraw_permission_claim_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const
@ -591,9 +602,9 @@ void withdraw_permission_claim_operation::validate()const
share_type withdraw_permission_claim_operation::calculate_fee( const fee_schedule_type& schedule )const
{
auto core_fee_required = schedule.at( transfer_fee_type );
share_type core_fee_required = schedule.transfer_fee;
if( memo )
core_fee_required += share_type((memo->message.size() * schedule.at( data_fee_type ).value)/1024);
core_fee_required += share_type((memo->message.size() * schedule.data_fee)/1024);
return core_fee_required;
}
@ -610,7 +621,7 @@ void withdraw_permission_delete_operation::validate() const
share_type withdraw_permission_delete_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at(withdraw_permission_update_fee_type);
return k.withdraw_permission_update_fee;
}
void withdraw_permission_create_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -630,7 +641,7 @@ void withdraw_permission_create_operation::validate() const
share_type withdraw_permission_create_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at(withdraw_permission_update_fee_type);
return k.withdraw_permission_update_fee;
}
@ -648,7 +659,7 @@ void asset_global_settle_operation::validate()const
share_type asset_global_settle_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at(global_settle_fee_type);
return k.global_settle_fee;
}
void asset_settle_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -664,7 +675,7 @@ void asset_settle_operation::validate() const
share_type asset_settle_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at(asset_settle_fee_type);
return k.asset_settle_fee;
}
@ -686,7 +697,7 @@ void asset_update_bitasset_operation::validate() const
share_type asset_update_bitasset_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( asset_update_fee_type );
return k.asset_update_fee;
}
void asset_update_feed_producers_operation::validate() const
@ -704,7 +715,7 @@ void file_write_operation::validate()const
share_type file_write_operation::calculate_fee( const fee_schedule_type& k )const
{
return ((((k.at( file_storage_fee_per_day_type ).value * lease_seconds)/(60*60*24))*file_size)/0xff) + ((data.size() * k.at( data_fee_type ).value)/1024);
return ((((k.file_storage_fee_per_day * lease_seconds)/(60*60*24))*file_size)/0xff) + ((data.size() * k.data_fee)/1024);
}
@ -716,7 +727,7 @@ void vesting_balance_create_operation::get_required_auth(flat_set<account_id_typ
share_type vesting_balance_create_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( vesting_balance_create_fee_type );
return k.vesting_balance_create_fee;
}
void vesting_balance_create_operation::validate()const
@ -739,7 +750,7 @@ void vesting_balance_withdraw_operation::validate()const
share_type vesting_balance_withdraw_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( vesting_balance_withdraw_fee_type );
return k.vesting_balance_withdraw_fee;
}
void memo_data::set_message( const fc::ecc::private_key& priv,
@ -791,7 +802,7 @@ void custom_operation::validate()const
}
share_type custom_operation::calculate_fee( const fee_schedule_type& k )const
{
return (data.size() * k.at( data_fee_type ).value)/1024;
return (data.size() * k.data_fee)/1024;
}
void bond_create_offer_operation::get_required_auth( flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>& )const
@ -812,7 +823,7 @@ void bond_create_offer_operation::validate()const
share_type bond_create_offer_operation::calculate_fee( const fee_schedule_type& schedule )const
{
return schedule.at( create_bond_offer_fee_type );
return schedule.create_bond_offer_fee;
}
@ -827,7 +838,7 @@ void bond_cancel_offer_operation::validate()const
}
share_type bond_cancel_offer_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( cancel_bond_offer_fee_type );
return k.cancel_bond_offer_fee;
}
void bond_accept_offer_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const
@ -845,7 +856,7 @@ void bond_accept_offer_operation::validate()const
share_type bond_accept_offer_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( accept_bond_offer_fee_type );
return k.accept_bond_offer_fee;
}
void bond_claim_collateral_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const
{
@ -862,7 +873,7 @@ void bond_claim_collateral_operation::validate()const
share_type bond_claim_collateral_operation::calculate_fee( const fee_schedule_type& k )const
{
return k.at( claim_bond_collateral_fee_type );
return k.claim_bond_collateral_fee;
}
void worker_create_operation::get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&) const
@ -880,7 +891,7 @@ void worker_create_operation::validate() const
share_type worker_create_operation::calculate_fee(const fee_schedule_type& k) const
{
return k.at( worker_create_fee_type );
return k.worker_create_fee;
}
string memo_message::serialize() const
@ -899,6 +910,7 @@ memo_message memo_message::deserialize(const string& serial)
return result;
}
<<<<<<< HEAD
/**
* If fee_payer = temp_account_id, then the fee is paid by the surplus balance of inputs-outputs and
* 100% of the fee goes to the network.
@ -956,6 +968,18 @@ void blind_transfer_operation::get_balance_delta( balance_accumulator
acc.adjust( fee_payer(), -fee );
acc.adjust( from_account, asset(-from_amount,fee.asset_id) );
acc.adjust( to_account, asset(to_amount,fee.asset_id) );
=======
void account_upgrade_operation::validate() const
{
FC_ASSERT( fee.amount >= 0 );
}
share_type account_upgrade_operation::calculate_fee(const fee_schedule_type& k) const
{
if( upgrade_to_lifetime_member )
return k.membership_lifetime_fee;
return k.membership_annual_fee;
>>>>>>> dfee44a40827e40becf4d8f21bff5b2bfa1303a2
}
} } // namespace graphene::chain

View file

@ -20,66 +20,64 @@
namespace graphene { namespace chain {
inline bool sum_below_max_shares( const asset& a, const asset& b )
inline bool sum_below_max_shares(const asset& a, const asset& b)
{
assert( GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY );
return ( a.amount <= GRAPHENE_MAX_SHARE_SUPPLY)
assert(GRAPHENE_MAX_SHARE_SUPPLY + GRAPHENE_MAX_SHARE_SUPPLY > GRAPHENE_MAX_SHARE_SUPPLY);
return (a.amount <= GRAPHENE_MAX_SHARE_SUPPLY)
&& ( b.amount <= GRAPHENE_MAX_SHARE_SUPPLY)
&& ((a.amount + b.amount) <= GRAPHENE_MAX_SHARE_SUPPLY)
;
}
asset linear_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx ) const
asset linear_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx) const
{
if( ctx.now <= begin_date )
return asset( 0, ctx.balance.asset_id );
if( vesting_seconds == 0 )
if(ctx.now <= begin_date)
return asset(0, ctx.balance.asset_id);
if(vesting_seconds == 0)
return ctx.balance;
int64_t elapsed_seconds = (ctx.now - begin_date).to_seconds();
// if elapsed_seconds <= 0, then ctx.now <= begin_date,
// and we should have returned above.
assert( elapsed_seconds > 0 );
assert(elapsed_seconds > 0);
fc::uint128_t total_allowed = begin_balance.value;
total_allowed *= uint64_t( elapsed_seconds );
total_allowed *= uint64_t(elapsed_seconds);
total_allowed /= vesting_seconds;
if( total_allowed <= total_withdrawn.value )
return asset( 0, ctx.balance.asset_id );
if(total_allowed <= total_withdrawn.value)
return asset(0, ctx.balance.asset_id);
total_allowed -= total_withdrawn.value;
FC_ASSERT( total_allowed <= GRAPHENE_MAX_SHARE_SUPPLY );
return asset( total_allowed.to_uint64(), ctx.balance.asset_id );
FC_ASSERT(total_allowed <= GRAPHENE_MAX_SHARE_SUPPLY);
return asset(total_allowed.to_uint64(), ctx.balance.asset_id);
}
void linear_vesting_policy::on_deposit( const vesting_policy_context& ctx )
void linear_vesting_policy::on_deposit(const vesting_policy_context& ctx)
{
return;
}
bool linear_vesting_policy::is_deposit_allowed( const vesting_policy_context& ctx )const
bool linear_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const
{
return (ctx.amount.asset_id == ctx.balance.asset_id)
&& sum_below_max_shares( ctx.amount, ctx.balance );
&& sum_below_max_shares(ctx.amount, ctx.balance);
}
void linear_vesting_policy::on_withdraw( const vesting_policy_context& ctx )
void linear_vesting_policy::on_withdraw(const vesting_policy_context& ctx)
{
total_withdrawn += ctx.amount.amount;
return;
}
bool linear_vesting_policy::is_withdraw_allowed( const vesting_policy_context& ctx )const
bool linear_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const
{
return ( ctx.amount <= get_allowed_withdraw( ctx ) );
return (ctx.amount <= get_allowed_withdraw(ctx));
}
fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned( const vesting_policy_context& ctx )const
fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned(const vesting_policy_context& ctx)const
{
assert( ctx.now >= coin_seconds_earned_last_update );
assert(ctx.now >= coin_seconds_earned_last_update);
int64_t delta_seconds = (ctx.now - coin_seconds_earned_last_update).to_seconds();
assert( delta_seconds >= 0 );
assert(delta_seconds >= 0);
fc::uint128_t delta_coin_seconds = ctx.balance.amount.value;
delta_coin_seconds *= delta_seconds;
@ -87,113 +85,132 @@ fc::uint128_t cdd_vesting_policy::compute_coin_seconds_earned( const vesting_pol
fc::uint128_t coin_seconds_earned_cap = ctx.balance.amount.value;
coin_seconds_earned_cap *= vesting_seconds;
return std::min(
coin_seconds_earned + delta_coin_seconds,
coin_seconds_earned_cap
);
return std::min(coin_seconds_earned + delta_coin_seconds, coin_seconds_earned_cap);
}
void cdd_vesting_policy::update_coin_seconds_earned( const vesting_policy_context& ctx )
void cdd_vesting_policy::update_coin_seconds_earned(const vesting_policy_context& ctx)
{
coin_seconds_earned = compute_coin_seconds_earned( ctx );
coin_seconds_earned = compute_coin_seconds_earned(ctx);
coin_seconds_earned_last_update = ctx.now;
return;
}
asset cdd_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const
asset cdd_vesting_policy::get_allowed_withdraw(const vesting_policy_context& ctx)const
{
fc::uint128_t cs_earned = compute_coin_seconds_earned( ctx );
fc::uint128_t cs_earned = compute_coin_seconds_earned(ctx);
fc::uint128_t withdraw_available = cs_earned / vesting_seconds;
assert( withdraw_available <= ctx.balance.amount.value );
return asset( withdraw_available.to_uint64(), ctx.balance.asset_id );
assert(withdraw_available <= ctx.balance.amount.value);
return asset(withdraw_available.to_uint64(), ctx.balance.asset_id);
}
void cdd_vesting_policy::on_deposit( const vesting_policy_context& ctx )
void cdd_vesting_policy::on_deposit(const vesting_policy_context& ctx)
{
update_coin_seconds_earned( ctx );
return;
update_coin_seconds_earned(ctx);
}
void cdd_vesting_policy::on_withdraw( const vesting_policy_context& ctx )
void cdd_vesting_policy::on_deposit_vested(const vesting_policy_context& ctx)
{
update_coin_seconds_earned( ctx );
on_deposit(ctx);
coin_seconds_earned += ctx.amount.amount.value * vesting_seconds;
}
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;
// is_withdraw_allowed should forbid any withdrawal that
// would trigger this assert
assert( coin_seconds_needed <= coin_seconds_earned );
assert(coin_seconds_needed <= coin_seconds_earned);
coin_seconds_earned -= coin_seconds_needed;
return;
}
bool cdd_vesting_policy::is_deposit_allowed( const vesting_policy_context& ctx )const
bool cdd_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const
{
return (ctx.amount.asset_id == ctx.balance.asset_id)
&& sum_below_max_shares( ctx.amount, ctx.balance );
&& sum_below_max_shares(ctx.amount, ctx.balance);
}
bool cdd_vesting_policy::is_withdraw_allowed( const vesting_policy_context& ctx )const
bool cdd_vesting_policy::is_deposit_vested_allowed(const vesting_policy_context& ctx) const
{
return ( ctx.amount <= get_allowed_withdraw( ctx ) );
return is_deposit_allowed(ctx);
}
#define VESTING_VISITOR( NAME, MAYBE_CONST ) \
bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const
{
return (ctx.amount <= get_allowed_withdraw(ctx));
}
#define VESTING_VISITOR(NAME, MAYBE_CONST) \
struct NAME ## _visitor \
{ \
typedef decltype( \
std::declval<linear_vesting_policy>().NAME( \
std::declval<vesting_policy_context>()) \
) result_type; \
) result_type; \
\
NAME ## _visitor( \
const asset& balance, \
const time_point_sec& now, \
const asset& amount \
) \
: ctx( balance, now, amount ) {} \
) \
: ctx(balance, now, amount) {} \
\
template< typename Policy > \
result_type \
operator()( MAYBE_CONST Policy& policy ) MAYBE_CONST \
operator()(MAYBE_CONST Policy& policy) MAYBE_CONST \
{ \
return policy.NAME( ctx ); \
return policy.NAME(ctx); \
} \
\
vesting_policy_context ctx; \
}
VESTING_VISITOR( on_deposit, );
VESTING_VISITOR( on_withdraw, );
VESTING_VISITOR( is_deposit_allowed, const );
VESTING_VISITOR( is_withdraw_allowed, const );
VESTING_VISITOR(on_deposit,);
VESTING_VISITOR(on_deposit_vested,);
VESTING_VISITOR(on_withdraw,);
VESTING_VISITOR(is_deposit_allowed, const);
VESTING_VISITOR(is_deposit_vested_allowed, const);
VESTING_VISITOR(is_withdraw_allowed, const);
bool vesting_balance_object::is_deposit_allowed(const time_point_sec& now, const asset& amount)const
{
return policy.visit( is_deposit_allowed_visitor( balance, now, amount ) );
return policy.visit(is_deposit_allowed_visitor(balance, now, amount));
}
bool vesting_balance_object::is_withdraw_allowed(const time_point_sec& now, const asset& amount)const
{
bool result = policy.visit( is_withdraw_allowed_visitor( balance, now, amount ) );
bool result = policy.visit(is_withdraw_allowed_visitor(balance, now, amount));
// if some policy allows you to withdraw more than your balance,
// there's a programming bug in the policy algorithm
assert( (amount <= balance) || (!result) );
assert((amount <= balance) || (!result));
return result;
}
void vesting_balance_object::deposit(const time_point_sec& now, const asset& amount)
{
on_deposit_visitor vtor( balance, now, amount );
policy.visit( vtor );
on_deposit_visitor vtor(balance, now, amount);
policy.visit(vtor);
balance += amount;
}
void vesting_balance_object::deposit_vested(const time_point_sec& now, const asset& amount)
{
on_deposit_vested_visitor vtor(balance, now, amount);
policy.visit(vtor);
balance += amount;
}
bool vesting_balance_object::is_deposit_vested_allowed(const time_point_sec& now, const asset& amount) const
{
return policy.visit(is_deposit_vested_allowed_visitor(balance, now, amount));
}
void vesting_balance_object::withdraw(const time_point_sec& now, const asset& amount)
{
assert( amount <= balance );
on_withdraw_visitor vtor( balance, now, amount );
policy.visit( vtor );
assert(amount <= balance);
on_withdraw_visitor vtor(balance, now, amount);
policy.visit(vtor);
balance -= amount;
}

View file

@ -25,7 +25,7 @@
namespace graphene { namespace chain {
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();
}

View file

@ -26,7 +26,7 @@ object_id_type worker_create_evaluator::do_evaluate(const worker_create_evaluato
{ try {
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());
return object_id_type();

View file

@ -162,6 +162,7 @@ struct operation_get_impacted_accounts
add_authority( *o.active );
}
}
void operator()( const account_upgrade_operation& )const {}
void operator()( const account_transfer_operation& o )const
{
_impacted.insert( o.new_owner );
@ -236,12 +237,12 @@ struct operation_get_impacted_accounts
void operator()( const bond_create_offer_operation& o )const { }
void operator()( const bond_cancel_offer_operation& o )const { }
void operator()( const bond_accept_offer_operation& o )const {
void operator()( const bond_accept_offer_operation& o )const {
_impacted.insert( o.borrower );
_impacted.insert( o.lender );
}
void operator()( const bond_claim_collateral_operation& o )const
{
void operator()( const bond_claim_collateral_operation& o )const
{
_impacted.insert( o.lender );
_impacted.insert( o.claimer );
}

View file

@ -140,7 +140,6 @@ void witness_plugin::block_production_loop()
// we must control the witness scheduled to produce the next block.
if( _witnesses.find( scheduled_witness ) == _witnesses.end() ) {
elog("Not producing block because it's not my turn.");
return false;
}

View file

@ -695,7 +695,7 @@ public:
account_object registrar_account_object =
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;
@ -761,17 +761,13 @@ public:
{ try {
FC_ASSERT( !self.is_locked() );
account_object account_obj = get_account(name);
FC_ASSERT( !account_obj.is_prime() );
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;
FC_ASSERT( !account_obj.is_lifetime_member() );
signed_transaction tx;
tx.operations.push_back( update_op );
account_upgrade_operation op;
op.account_to_upgrade = account_obj.get_id();
op.upgrade_to_lifetime_member = true;
tx.operations = {op};
tx.visit( operation_set_fee( _remote_db->get_global_properties().parameters.current_fees ) );
tx.validate();
@ -802,7 +798,7 @@ public:
account_object referrer_account_object =
this->get_account( referrer_account );
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
key_create_operation owner_key_create_op;

View file

@ -119,6 +119,7 @@ void database_fixture::verify_asset_supplies( )const
for( const account_statistics_object& a : statistics_index )
{
reported_core_in_orders += a.total_core_in_orders;
total_balances[asset_id_type()] += a.pending_fees + a.pending_vested_fees;
}
for( const limit_order_object& o : db.get_index_type<limit_order_index>().indices() )
{
@ -670,27 +671,28 @@ void database_fixture::enable_fees(
{
db.modify(global_property_id_type()(db), [fee](global_property_object& gpo)
{
for( int i=0; i < FEE_TYPE_COUNT; ++i)
gpo.parameters.current_fees.set(i, fee);
gpo.parameters.current_fees.set( prime_upgrade_fee_type, 10*fee.value );
fc::reflector<fee_schedule_type>::visit(fee_schedule_type::fee_set_visitor{gpo.parameters.current_fees,
uint32_t(fee.value)});
gpo.parameters.current_fees.membership_annual_fee = 3*fee.value;
gpo.parameters.current_fees.membership_lifetime_fee = 10*fee.value;
} );
}
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
{
account_update_operation op;
op.account = account.id;
op.upgrade_to_prime = true;
trx.operations.emplace_back(operation(op));
account_upgrade_operation op;
op.account_to_upgrade = account.get_id();
op.upgrade_to_lifetime_member = true;
trx.operations = {op};
db.push_transaction( trx, ~0 );
FC_ASSERT( account.is_prime() );
FC_ASSERT( op.account_to_upgrade(db).is_lifetime_member() );
trx.clear();
}
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 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 upgrade_to_prime( account_id_type account );
void upgrade_to_prime( const account_object& account );
void upgrade_to_lifetime_member( account_id_type account );
void upgrade_to_lifetime_member( const account_object& account );
void print_market( const string& syma, const string& symb )const;
string pretty( const asset& a )const;
void print_short_order( const short_order_object& cur )const;

View file

@ -16,6 +16,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <bitset>
#include <iostream>
#include <boost/test/unit_test.hpp>
@ -25,6 +26,7 @@
#include <graphene/chain/key_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <fc/crypto/digest.hpp>
@ -236,4 +238,169 @@ BOOST_FIXTURE_TEST_CASE( update_account_keys, database_fixture )
}
}
/**
* To have a secure random number we need to ensure that the same
* delegate does not get to produce two blocks in a row. There is
* always a chance that the last delegate of one round will be the
* first delegate of the next round.
*
* This means that when we shuffle delegates we need to make sure
* that there is at least N/2 delegates between consecutive turns
* of the same delegate. This means that durring the random
* shuffle we need to restrict the placement of delegates to maintain
* this invariant.
*
* This test checks the requirement using Monte Carlo approach
* (produce lots of blocks and check the invariant holds).
*/
BOOST_FIXTURE_TEST_CASE( witness_order_mc_test, database_fixture )
{
try {
size_t num_witnesses = db.get_global_properties().active_witnesses.size();
size_t dmin = num_witnesses >> 1;
vector< witness_id_type > cur_round;
vector< witness_id_type > full_schedule;
// if we make the maximum witness count testable,
// we'll need to enlarge this.
std::bitset< 0x40 > witness_seen;
size_t total_blocks = 1000000;
cur_round.reserve( num_witnesses );
full_schedule.reserve( total_blocks );
cur_round.push_back( db.get_dynamic_global_properties().current_witness );
// we assert so the test doesn't continue, which would
// corrupt memory
assert( num_witnesses <= witness_seen.size() );
while( full_schedule.size() < total_blocks )
{
if( (db.head_block_num() & 0x3FFF) == 0 )
{
wdump( (db.head_block_num()) );
}
witness_id_type wid = db.get_scheduled_witness( 1 ).first;
full_schedule.push_back( wid );
cur_round.push_back( wid );
if( cur_round.size() == num_witnesses )
{
// check that the current round contains exactly 1 copy
// of each witness
witness_seen.reset();
for( const witness_id_type& w : cur_round )
{
uint64_t inst = w.instance.value;
BOOST_CHECK( !witness_seen.test( inst ) );
assert( !witness_seen.test( inst ) );
witness_seen.set( inst );
}
cur_round.clear();
}
generate_block();
}
for( size_t i=0,m=full_schedule.size(); i<m; i++ )
{
for( size_t j=i+1,n=std::min( m, i+dmin ); j<n; j++ )
{
BOOST_CHECK( full_schedule[i] != full_schedule[j] );
assert( full_schedule[i] != full_schedule[j] );
}
}
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
/**
* To have a secure random number we need to ensure that the same
* delegate does not get to produce two blocks in a row. There is
* always a chance that the last delegate of one round will be the
* first delegate of the next round.
*
* This means that when we shuffle delegates we need to make sure
* that there is at least N/2 delegates between consecutive turns
* of the same delegate. This means that durring the random
* shuffle we need to restrict the placement of delegates to maintain
* this invariant.
*
* This test checks the requirement using Monte Carlo approach
* (produce lots of blocks and check the invariant holds).
*/
BOOST_FIXTURE_TEST_CASE( generic_scheduler_mc_test, database_fixture )
{
try {
size_t num_witnesses = db.get_global_properties().active_witnesses.size();
size_t dmin = num_witnesses >> 1;
witness_scheduler_rng rng(
// - - - - + - - - - 1 - - - - + - - - - 2 - - - - + - - -
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
);
witness_scheduler scheduler;
vector< witness_id_type > witness_ids;
witness_ids.reserve( num_witnesses );
for( size_t i=0; i<num_witnesses; i++ )
witness_ids.push_back( witness_id_type(i) );
scheduler._min_token_count = num_witnesses / 2;
scheduler.insert_all( witness_ids );
for( size_t i=0; i<num_witnesses; i++ )
scheduler.produce_schedule( rng );
vector< witness_id_type > cur_round;
vector< witness_id_type > full_schedule;
// if we make the maximum witness count testable,
// we'll need to enlarge this.
std::bitset< 0x40 > witness_seen;
size_t total_blocks = 1000000;
cur_round.reserve( num_witnesses );
full_schedule.reserve( total_blocks );
// we assert so the test doesn't continue, which would
// corrupt memory
assert( num_witnesses <= witness_seen.size() );
while( full_schedule.size() < total_blocks )
{
scheduler.produce_schedule( rng );
witness_id_type wid = scheduler.consume_schedule();
full_schedule.push_back( wid );
cur_round.push_back( wid );
if( cur_round.size() == num_witnesses )
{
// check that the current round contains exactly 1 copy
// of each witness
witness_seen.reset();
for( const witness_id_type& w : cur_round )
{
uint64_t inst = w.instance.value;
BOOST_CHECK( !witness_seen.test( inst ) );
assert( !witness_seen.test( inst ) );
witness_seen.set( inst );
}
cur_round.clear();
}
}
for( size_t i=0,m=full_schedule.size(); i<m; i++ )
{
for( size_t j=i+1,n=std::min( m, i+dmin ); j<n; j++ )
{
BOOST_CHECK( full_schedule[i] != full_schedule[j] );
assert( full_schedule[i] != full_schedule[j] );
}
}
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -434,7 +434,7 @@ BOOST_FIXTURE_TEST_CASE( fired_delegates, database_fixture )
for( int i = 0; i < 15; ++i )
{
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);
}
@ -816,6 +816,7 @@ BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )
private_key_type sam_key = generate_private_key("sam");
account_object sam_account_object = create_account( "sam", sam_key );
upgrade_to_lifetime_member(sam_account_object);
account_object genesis_account_object = genesis_account(db);
const asset_object& core = asset_id_type()(db);
@ -980,8 +981,8 @@ BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture )
{ try {
ACTORS((nathan)(vikram));
upgrade_to_prime(nathan_id);
upgrade_to_prime(vikram_id);
upgrade_to_lifetime_member(nathan_id);
upgrade_to_lifetime_member(vikram_id);
delegate_id_type nathan_delegate = create_delegate(nathan_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());
auto initial_properties = db.get_global_properties();
const account_object& nathan = create_account("nathan");
upgrade_to_prime(nathan);
upgrade_to_lifetime_member(nathan);
const delegate_object nathans_delegate = create_delegate(nathan);
{
account_update_operation op;

View file

@ -107,6 +107,7 @@ BOOST_AUTO_TEST_CASE( child_account )
const auto& nathan_key = register_key(nathan_private_key.get_public_key());
const account_object& nathan = get_account("nathan");
const account_object& root = create_account("root");
upgrade_to_lifetime_member(root);
skip_key_index_test = true;
db.modify(nathan, [nathan_key](account_object& a) {
@ -128,7 +129,7 @@ BOOST_AUTO_TEST_CASE( child_account )
BOOST_REQUIRE_THROW(db.push_transaction(trx), fc::exception);
trx.signatures.clear();
op.owner = authority(1, account_id_type(nathan.id), 1);
trx.operations.back() = op;
trx.operations = {op};
sign(trx, key_id_type(), fc::ecc::private_key::regenerate(fc::sha256::hash(string("genesis"))));
sign(trx, nathan_key.id, nathan_private_key);
db.push_transaction(trx);
@ -186,13 +187,16 @@ BOOST_AUTO_TEST_CASE( update_account )
transfer(account_id_type()(db), nathan, asset(3000000));
enable_fees();
op.upgrade_to_prime = true;
op.fee = op.calculate_fee( db.get_global_properties().parameters.current_fees );
trx.operations.push_back(op);
db.push_transaction(trx, ~0);
{
account_upgrade_operation op;
op.account_to_upgrade = nathan.id;
op.upgrade_to_lifetime_member = true;
op.fee = op.calculate_fee(db.get_global_properties().parameters.current_fees);
trx.operations = {op};
db.push_transaction(trx, ~0);
}
BOOST_CHECK( nathan.referrer == nathan.id );
BOOST_CHECK( nathan.referrer_percent == 100 );
BOOST_CHECK( nathan.is_lifetime_member() );
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
@ -1866,13 +1870,13 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
const asset_object* core = &asset_id_type()(db);
const account_object* nathan = &get_account("nathan");
enable_fees(100000000);
BOOST_CHECK_GT(db.current_fee_schedule().at(prime_upgrade_fee_type).value, 0);
enable_fees(105000000);
BOOST_CHECK_GT(db.current_fee_schedule().membership_lifetime_fee, 0);
BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 0);
account_update_operation uop;
uop.account = nathan->get_id();
uop.upgrade_to_prime = true;
account_upgrade_operation uop;
uop.account_to_upgrade = nathan->get_id();
uop.upgrade_to_lifetime_member = true;
trx.set_expiration(db.head_block_id());
trx.operations.push_back(uop);
trx.visit(operation_set_fee(db.current_fee_schedule()));
@ -1880,17 +1884,13 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
trx.sign(key_id_type(),generate_private_key("genesis"));
db.push_transaction(trx);
trx.clear();
BOOST_CHECK_EQUAL(get_balance(*nathan, *core), 9000000000);
BOOST_CHECK_EQUAL(core->dynamic_asset_data_id(db).accumulated_fees.value, 210000000);
// TODO: Replace this with another check
//BOOST_CHECK_EQUAL(account_id_type()(db).statistics(db).cashback_rewards.value, 1000000000-210000000);
BOOST_CHECK_EQUAL(get_balance(*nathan, *core), 8950000000);
generate_block();
nathan = &get_account("nathan");
core = &asset_id_type()(db);
const witness_object* witness = &db.fetch_block_by_number(db.head_block_num())->witness(db);
BOOST_CHECK_GT(core->dynamic_asset_data_id(db).accumulated_fees.value, 0);
BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0);
auto schedule_maint = [&]()
@ -1900,7 +1900,7 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
{
_dpo.next_maintenance_time = db.head_block_time() + 1;
} );
} ;
};
// generate some blocks
while( db.head_block_num() < 30 )
@ -1913,6 +1913,8 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
// maintenance will be in block 31. time of block 31 - time of block 1 = 30 * 5 seconds.
schedule_maint();
// TODO: Replace this with another check
//BOOST_CHECK_EQUAL(account_id_type()(db).statistics(db).cashback_rewards.value, 1000000000-200000000);
// first witness paid from old budget (so no pay)
BOOST_CHECK_EQUAL( core->burned(db).value, 0 );
generate_block();
@ -1967,25 +1969,6 @@ BOOST_AUTO_TEST_CASE( witness_withdraw_pay_test )
BOOST_CHECK_EQUAL(witness->accumulated_income.value, 0);
} FC_LOG_AND_RETHROW() }
/**
* To have a secure random number we need to ensure that the same
* delegate does not get to produce two blocks in a row. There is
* always a chance that the last delegate of one round will be the
* first delegate of the next round.
*
* This means that when we shuffle delegates we need to make sure
* that there is at least N/2 delegates between consecutive turns
* of the same delegate. This means that durring the random
* shuffle we need to restrict the placement of delegates to maintain
* this invariant.
*/
BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( unimp_delegate_groups_test, 1 )
BOOST_AUTO_TEST_CASE( unimp_delegate_groups_test )
{
BOOST_FAIL( "not implemented" );
}
/**
* This test should simulate a prediction market which means the following:
*
@ -2124,7 +2107,7 @@ BOOST_AUTO_TEST_CASE( unimp_transfer_cashback_test )
const account_object& sam = create_account( "sam" );
transfer(account_id_type()(db), sam, asset(30000));
upgrade_to_prime(sam);
upgrade_to_lifetime_member(sam);
ilog( "Creating alice" );
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 )
{ try {
ACTOR(nathan);
upgrade_to_prime(nathan_id);
upgrade_to_lifetime_member(nathan_id);
trx.clear();
witness_id_type nathan_witness_id = create_witness(nathan_id, nathan_key_id, nathan_private_key).id;
// Give nathan some voting stake
@ -494,7 +494,7 @@ BOOST_AUTO_TEST_CASE( global_settle_test )
BOOST_AUTO_TEST_CASE( worker_create_test )
{ try {
ACTOR(nathan);
upgrade_to_prime(nathan_id);
upgrade_to_lifetime_member(nathan_id);
generate_block();
{
@ -615,7 +615,7 @@ BOOST_AUTO_TEST_CASE( worker_pay_test )
BOOST_AUTO_TEST_CASE( refund_worker_test )
{try{
ACTOR(nathan);
upgrade_to_prime(nathan_id);
upgrade_to_lifetime_member(nathan_id);
generate_block();
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time);
trx.set_expiration(db.head_block_id());

View file

@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE( issue_whitelist_uia )
INVOKE(create_advanced_uia);
const asset_object& advanced = get_asset("ADVANCED");
const account_object& nathan = create_account("nathan");
upgrade_to_prime(nathan);
upgrade_to_lifetime_member(nathan);
trx.clear();
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 account_object& nathan = get_account("nathan");
const account_object& dan = create_account("dan");
upgrade_to_prime(dan);
upgrade_to_lifetime_member(dan);
trx.clear();
transfer_operation op({advanced.amount(0), nathan.id, dan.id, advanced.amount(100)});