2015-06-08 15:50:35 +00:00
/*
2015-10-12 17:48:40 +00:00
* Copyright ( c ) 2015 Cryptonomex , Inc . , and contributors .
*
2016-01-06 09:51:18 +00:00
* The MIT License
2015-10-12 17:48:40 +00:00
*
2016-01-06 09:51:18 +00:00
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
2015-10-12 17:48:40 +00:00
*
2016-01-06 09:51:18 +00:00
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
2015-10-12 17:02:59 +00:00
*
2016-01-06 09:51:18 +00:00
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
2015-06-08 15:50:35 +00:00
*/
2015-08-25 21:54:04 +00:00
# include <boost/multiprecision/integer.hpp>
2015-08-13 18:27:39 +00:00
# include <fc/smart_ref_impl.hpp>
# include <fc/uint128.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/database.hpp>
2016-01-22 20:57:53 +00:00
# include <graphene/chain/hardfork.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/account_object.hpp>
# include <graphene/chain/asset_object.hpp>
2016-01-08 16:01:25 +00:00
# include <graphene/chain/budget_record_object.hpp>
2016-02-11 09:59:28 +00:00
# include <graphene/chain/buyback_object.hpp>
2016-01-08 16:01:25 +00:00
# include <graphene/chain/chain_property_object.hpp>
2015-07-13 20:06:02 +00:00
# include <graphene/chain/committee_member_object.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/global_property_object.hpp>
2016-02-11 09:59:28 +00:00
# include <graphene/chain/market_object.hpp>
2016-01-27 15:30:32 +00:00
# include <graphene/chain/special_authority_object.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/vesting_balance_object.hpp>
2016-01-22 20:57:53 +00:00
# include <graphene/chain/vote_count.hpp>
2015-06-08 15:50:35 +00:00
# include <graphene/chain/witness_object.hpp>
2016-01-08 16:39:01 +00:00
# include <graphene/chain/worker_object.hpp>
2015-06-08 15:50:35 +00:00
namespace graphene { namespace chain {
2015-06-22 19:03:18 +00:00
template < class Index >
vector < std : : reference_wrapper < const typename Index : : object_type > > database : : sort_votable_objects ( size_t count ) const
2015-06-08 15:50:35 +00:00
{
2015-06-22 19:03:18 +00:00
using ObjectType = typename Index : : object_type ;
const auto & all_objects = get_index_type < Index > ( ) . indices ( ) ;
2015-06-08 15:50:35 +00:00
count = std : : min ( count , all_objects . size ( ) ) ;
vector < std : : reference_wrapper < const ObjectType > > refs ;
refs . reserve ( all_objects . size ( ) ) ;
std : : transform ( all_objects . begin ( ) , all_objects . end ( ) ,
std : : back_inserter ( refs ) ,
[ ] ( const ObjectType & o ) { return std : : cref ( o ) ; } ) ;
2015-06-22 19:03:18 +00:00
std : : partial_sort ( refs . begin ( ) , refs . begin ( ) + count , refs . end ( ) ,
[ this ] ( const ObjectType & a , const ObjectType & b ) - > bool {
2015-07-08 19:00:46 +00:00
share_type oa_vote = _vote_tally_buffer [ a . vote_id ] ;
share_type ob_vote = _vote_tally_buffer [ b . vote_id ] ;
if ( oa_vote ! = ob_vote )
return oa_vote > ob_vote ;
return a . vote_id < b . vote_id ;
2015-06-08 15:50:35 +00:00
} ) ;
refs . resize ( count , refs . front ( ) ) ;
return refs ;
}
2015-06-18 21:05:12 +00:00
template < class . . . Types >
void database : : perform_account_maintenance ( std : : tuple < Types . . . > helpers )
{
2015-10-13 13:29:48 +00:00
const auto & idx = get_index_type < account_index > ( ) . indices ( ) . get < by_name > ( ) ;
2015-06-18 21:05:12 +00:00
for ( const account_object & a : idx )
detail : : for_each ( helpers , a , detail : : gen_seq < sizeof . . . ( Types ) > ( ) ) ;
}
2015-07-08 20:39:23 +00:00
/// @brief A visitor for @ref worker_type which calls pay_worker on the worker within
struct worker_pay_visitor
{
private :
share_type pay ;
database & db ;
public :
worker_pay_visitor ( share_type pay , database & db )
: pay ( pay ) , db ( db ) { }
typedef void result_type ;
template < typename W >
void operator ( ) ( W & worker ) const
{
worker . pay_worker ( pay , db ) ;
}
} ;
2015-08-14 21:56:21 +00:00
void database : : update_worker_votes ( )
{
auto & idx = get_index_type < worker_index > ( ) ;
2015-09-11 21:47:27 +00:00
auto itr = idx . indices ( ) . get < by_account > ( ) . begin ( ) ;
while ( itr ! = idx . indices ( ) . get < by_account > ( ) . end ( ) )
2015-08-14 21:56:21 +00:00
{
2015-09-11 21:47:27 +00:00
modify ( * itr , [ & ] ( worker_object & obj ) {
2015-08-14 21:56:21 +00:00
obj . total_votes_for = _vote_tally_buffer [ obj . vote_for ] ;
obj . total_votes_against = _vote_tally_buffer [ obj . vote_against ] ;
} ) ;
+ + itr ;
}
}
2015-07-08 20:39:23 +00:00
2015-06-08 15:50:35 +00:00
void database : : pay_workers ( share_type & budget )
{
2015-06-18 19:17:48 +00:00
// ilog("Processing payroll! Available budget is ${b}", ("b", budget));
2015-06-08 15:50:35 +00:00
vector < std : : reference_wrapper < const worker_object > > active_workers ;
get_index_type < worker_index > ( ) . inspect_all_objects ( [ this , & active_workers ] ( const object & o ) {
const worker_object & w = static_cast < const worker_object & > ( o ) ;
2015-09-16 19:50:09 +00:00
auto now = head_block_time ( ) ;
2016-01-08 16:39:01 +00:00
if ( w . is_active ( now ) & & w . approving_stake ( ) > 0 )
2015-06-08 15:50:35 +00:00
active_workers . emplace_back ( w ) ;
} ) ;
2015-06-29 19:24:22 +00:00
// worker with more votes is preferred
// if two workers exactly tie for votes, worker with lower ID is preferred
2015-06-08 15:50:35 +00:00
std : : sort ( active_workers . begin ( ) , active_workers . end ( ) , [ this ] ( const worker_object & wa , const worker_object & wb ) {
2016-01-08 16:39:01 +00:00
share_type wa_vote = wa . approving_stake ( ) ;
share_type wb_vote = wb . approving_stake ( ) ;
2015-06-29 19:24:22 +00:00
if ( wa_vote ! = wb_vote )
return wa_vote > wb_vote ;
return wa . id < wb . id ;
2015-06-08 15:50:35 +00:00
} ) ;
2015-09-11 12:53:50 +00:00
for ( uint32_t i = 0 ; i < active_workers . size ( ) & & budget > 0 ; + + i )
2015-06-08 15:50:35 +00:00
{
const worker_object & active_worker = active_workers [ i ] ;
share_type requested_pay = active_worker . daily_pay ;
2015-09-16 19:50:09 +00:00
if ( head_block_time ( ) - get_dynamic_global_properties ( ) . last_budget_time ! = fc : : days ( 1 ) )
2015-06-08 15:50:35 +00:00
{
fc : : uint128 pay ( requested_pay . value ) ;
2015-09-16 19:50:09 +00:00
pay * = ( head_block_time ( ) - get_dynamic_global_properties ( ) . last_budget_time ) . count ( ) ;
2015-06-08 15:50:35 +00:00
pay / = fc : : days ( 1 ) . count ( ) ;
requested_pay = pay . to_uint64 ( ) ;
}
share_type actual_pay = std : : min ( budget , requested_pay ) ;
2015-06-18 19:17:48 +00:00
//ilog(" ==> Paying ${a} to worker ${w}", ("w", active_worker.id)("a", actual_pay));
2015-06-08 15:50:35 +00:00
modify ( active_worker , [ & ] ( worker_object & w ) {
w . worker . visit ( worker_pay_visitor ( actual_pay , * this ) ) ;
} ) ;
budget - = actual_pay ;
}
}
void database : : update_active_witnesses ( )
{ try {
assert ( _witness_count_histogram_buffer . size ( ) > 0 ) ;
2015-10-02 19:39:10 +00:00
share_type stake_target = ( _total_voting_stake - _witness_count_histogram_buffer [ 0 ] ) / 2 ;
2015-10-01 17:56:22 +00:00
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
/// the number of witnesses to have (they abstain and are non-voting accounts)
share_type stake_tally = 0 ;
2015-07-01 13:56:37 +00:00
size_t witness_count = 0 ;
2015-07-08 20:00:22 +00:00
if ( stake_target > 0 )
2015-10-01 17:56:22 +00:00
{
2015-07-08 20:00:22 +00:00
while ( ( witness_count < _witness_count_histogram_buffer . size ( ) - 1 )
& & ( stake_tally < = stake_target ) )
2015-10-01 17:56:22 +00:00
{
2015-07-08 20:00:22 +00:00
stake_tally + = _witness_count_histogram_buffer [ + + witness_count ] ;
2015-10-01 17:56:22 +00:00
}
}
2015-06-08 15:50:35 +00:00
2015-08-17 18:33:10 +00:00
const chain_property_object & cpo = get_chain_properties ( ) ;
auto wits = sort_votable_objects < witness_index > ( std : : max ( witness_count * 2 + 1 , ( size_t ) cpo . immutable_parameters . min_witness_count ) ) ;
2015-10-01 17:56:22 +00:00
2015-06-08 15:50:35 +00:00
const global_property_object & gpo = get_global_properties ( ) ;
2015-10-01 17:56:22 +00:00
const auto & all_witnesses = get_index_type < witness_index > ( ) . indices ( ) ;
for ( const witness_object & wit : all_witnesses )
2015-08-14 21:56:21 +00:00
{
modify ( wit , [ & ] ( witness_object & obj ) {
obj . total_votes = _vote_tally_buffer [ wit . vote_id ] ;
} ) ;
}
2015-06-18 19:51:11 +00:00
// Update witness authority
2016-01-22 20:57:53 +00:00
modify ( get ( GRAPHENE_WITNESS_ACCOUNT ) , [ & ] ( account_object & a )
{
if ( head_block_time ( ) < HARDFORK_533_TIME )
{
2016-01-22 21:14:50 +00:00
uint64_t total_votes = 0 ;
map < account_id_type , uint64_t > weights ;
a . active . weight_threshold = 0 ;
a . active . clear ( ) ;
2015-06-18 19:51:11 +00:00
2016-01-22 21:14:50 +00:00
for ( const witness_object & wit : wits )
{
weights . emplace ( wit . witness_account , _vote_tally_buffer [ wit . vote_id ] ) ;
total_votes + = _vote_tally_buffer [ wit . vote_id ] ;
}
2015-06-18 19:51:11 +00:00
2016-01-22 21:14:50 +00:00
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
// then I want to keep the most significant 16 bits of what's left.
int8_t bits_to_drop = std : : max ( int ( boost : : multiprecision : : detail : : find_msb ( total_votes ) ) - 15 , 0 ) ;
for ( const auto & weight : weights )
{
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
uint16_t votes = std : : max ( ( weight . second > > bits_to_drop ) , uint64_t ( 1 ) ) ;
a . active . account_auths [ weight . first ] + = votes ;
a . active . weight_threshold + = votes ;
}
2015-06-18 19:51:11 +00:00
2016-01-22 21:14:50 +00:00
a . active . weight_threshold / = 2 ;
a . active . weight_threshold + = 1 ;
2016-01-22 20:57:53 +00:00
}
else
{
vote_counter vc ;
for ( const witness_object & wit : wits )
vc . add ( wit . witness_account , _vote_tally_buffer [ wit . vote_id ] ) ;
vc . finish ( a . active ) ;
}
} ) ;
2015-06-18 19:51:11 +00:00
2015-07-06 18:00:35 +00:00
modify ( gpo , [ & ] ( global_property_object & gp ) {
2015-06-08 15:50:35 +00:00
gp . active_witnesses . clear ( ) ;
2015-07-06 18:00:35 +00:00
gp . active_witnesses . reserve ( wits . size ( ) ) ;
2015-06-08 15:50:35 +00:00
std : : transform ( wits . begin ( ) , wits . end ( ) ,
std : : inserter ( gp . active_witnesses , gp . active_witnesses . end ( ) ) ,
[ ] ( const witness_object & w ) {
return w . id ;
} ) ;
} ) ;
} FC_CAPTURE_AND_RETHROW ( ) }
2015-07-13 20:06:02 +00:00
void database : : update_active_committee_members ( )
2015-06-08 15:50:35 +00:00
{ try {
assert ( _committee_count_histogram_buffer . size ( ) > 0 ) ;
2015-10-02 19:39:10 +00:00
share_type stake_target = ( _total_voting_stake - _witness_count_histogram_buffer [ 0 ] ) / 2 ;
2015-10-01 17:56:22 +00:00
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
/// the number of witnesses to have (they abstain and are non-voting accounts)
uint64_t stake_tally = 0 ; // _committee_count_histogram_buffer[0];
2015-07-13 20:06:02 +00:00
size_t committee_member_count = 0 ;
2015-07-08 20:00:22 +00:00
if ( stake_target > 0 )
2015-07-13 20:06:02 +00:00
while ( ( committee_member_count < _committee_count_histogram_buffer . size ( ) - 1 )
2015-07-08 20:00:22 +00:00
& & ( stake_tally < = stake_target ) )
2015-07-13 20:06:02 +00:00
stake_tally + = _committee_count_histogram_buffer [ + + committee_member_count ] ;
2015-06-08 15:50:35 +00:00
2015-08-17 18:33:10 +00:00
const chain_property_object & cpo = get_chain_properties ( ) ;
auto committee_members = sort_votable_objects < committee_member_index > ( std : : max ( committee_member_count * 2 + 1 , ( size_t ) cpo . immutable_parameters . min_committee_member_count ) ) ;
2015-06-08 15:50:35 +00:00
2015-08-14 21:56:21 +00:00
for ( const committee_member_object & del : committee_members )
{
modify ( del , [ & ] ( committee_member_object & obj ) {
obj . total_votes = _vote_tally_buffer [ del . vote_id ] ;
} ) ;
}
2015-07-13 19:19:36 +00:00
// Update committee authorities
2015-07-13 20:06:02 +00:00
if ( ! committee_members . empty ( ) )
2015-06-18 19:51:11 +00:00
{
2016-01-22 20:57:53 +00:00
modify ( get ( GRAPHENE_COMMITTEE_ACCOUNT ) , [ & ] ( account_object & a )
{
if ( head_block_time ( ) < HARDFORK_533_TIME )
{
2016-01-22 21:14:50 +00:00
uint64_t total_votes = 0 ;
map < account_id_type , uint64_t > weights ;
a . active . weight_threshold = 0 ;
a . active . clear ( ) ;
for ( const committee_member_object & del : committee_members )
{
weights . emplace ( del . committee_member_account , _vote_tally_buffer [ del . vote_id ] ) ;
total_votes + = _vote_tally_buffer [ del . vote_id ] ;
}
// total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,
// then I want to keep the most significant 16 bits of what's left.
int8_t bits_to_drop = std : : max ( int ( boost : : multiprecision : : detail : : find_msb ( total_votes ) ) - 15 , 0 ) ;
for ( const auto & weight : weights )
{
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
uint16_t votes = std : : max ( ( weight . second > > bits_to_drop ) , uint64_t ( 1 ) ) ;
a . active . account_auths [ weight . first ] + = votes ;
a . active . weight_threshold + = votes ;
}
a . active . weight_threshold / = 2 ;
a . active . weight_threshold + = 1 ;
2016-01-22 20:57:53 +00:00
}
else
{
vote_counter vc ;
for ( const committee_member_object & cm : committee_members )
vc . add ( cm . committee_member_account , _vote_tally_buffer [ cm . vote_id ] ) ;
vc . finish ( a . active ) ;
}
} ) ;
2015-06-22 19:03:18 +00:00
modify ( get ( GRAPHENE_RELAXED_COMMITTEE_ACCOUNT ) , [ & ] ( account_object & a ) {
2015-06-18 19:51:11 +00:00
a . active = get ( GRAPHENE_COMMITTEE_ACCOUNT ) . active ;
} ) ;
}
2015-06-22 19:03:18 +00:00
modify ( get_global_properties ( ) , [ & ] ( global_property_object & gp ) {
2015-07-13 20:06:02 +00:00
gp . active_committee_members . clear ( ) ;
std : : transform ( committee_members . begin ( ) , committee_members . end ( ) ,
std : : inserter ( gp . active_committee_members , gp . active_committee_members . begin ( ) ) ,
[ ] ( const committee_member_object & d ) { return d . id ; } ) ;
2015-06-08 15:50:35 +00:00
} ) ;
} FC_CAPTURE_AND_RETHROW ( ) }
2015-09-28 16:10:20 +00:00
void database : : initialize_budget_record ( fc : : time_point_sec now , budget_record & rec ) const
2015-06-08 15:50:35 +00:00
{
const dynamic_global_property_object & dpo = get_dynamic_global_properties ( ) ;
const asset_object & core = asset_id_type ( 0 ) ( * this ) ;
const asset_dynamic_data_object & core_dd = core . dynamic_asset_data_id ( * this ) ;
2015-09-28 16:10:20 +00:00
rec . from_initial_reserve = core . reserved ( * this ) ;
rec . from_accumulated_fees = core_dd . accumulated_fees ;
rec . from_unused_witness_budget = dpo . witness_budget ;
2015-06-08 15:50:35 +00:00
if ( ( dpo . last_budget_time = = fc : : time_point_sec ( ) )
| | ( now < = dpo . last_budget_time ) )
2015-09-28 16:10:20 +00:00
{
rec . time_since_last_budget = 0 ;
return ;
}
2015-06-08 15:50:35 +00:00
int64_t dt = ( now - dpo . last_budget_time ) . to_seconds ( ) ;
2015-09-28 16:10:20 +00:00
rec . time_since_last_budget = uint64_t ( dt ) ;
2015-06-08 15:50:35 +00:00
2015-07-01 18:05:42 +00:00
// We'll consider accumulated_fees to be reserved at the BEGINNING
2015-06-08 15:50:35 +00:00
// of the maintenance interval. However, for speed we only
// call modify() on the asset_dynamic_data_object once at the
// end of the maintenance interval. Thus the accumulated_fees
// are available for the budget at this point, but not included
2015-07-01 18:05:42 +00:00
// in core.reserved().
2015-09-28 16:10:20 +00:00
share_type reserve = rec . from_initial_reserve + core_dd . accumulated_fees ;
2015-06-18 20:06:42 +00:00
// Similarly, we consider leftover witness_budget to be burned
// at the BEGINNING of the maintenance interval.
reserve + = dpo . witness_budget ;
2015-06-08 15:50:35 +00:00
fc : : uint128_t budget_u128 = reserve . value ;
budget_u128 * = uint64_t ( dt ) ;
budget_u128 * = GRAPHENE_CORE_ASSET_CYCLE_RATE ;
//round up to the nearest satoshi -- this is necessary to ensure
// there isn't an "untouchable" reserve, and we will eventually
// be able to use the entire reserve
budget_u128 + = ( ( uint64_t ( 1 ) < < GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS ) - 1 ) ;
budget_u128 > > = GRAPHENE_CORE_ASSET_CYCLE_RATE_BITS ;
share_type budget ;
if ( budget_u128 < reserve . value )
2015-09-28 16:10:20 +00:00
rec . total_budget = share_type ( budget_u128 . to_uint64 ( ) ) ;
2015-06-08 15:50:35 +00:00
else
2015-09-28 16:10:20 +00:00
rec . total_budget = reserve ;
2015-06-08 15:50:35 +00:00
2015-09-28 16:10:20 +00:00
return ;
2015-06-08 15:50:35 +00:00
}
/**
* Update the budget for witnesses and workers .
*/
void database : : process_budget ( )
{
try
{
const global_property_object & gpo = get_global_properties ( ) ;
const dynamic_global_property_object & dpo = get_dynamic_global_properties ( ) ;
const asset_dynamic_data_object & core =
asset_id_type ( 0 ) ( * this ) . dynamic_asset_data_id ( * this ) ;
2015-09-16 19:50:09 +00:00
fc : : time_point_sec now = head_block_time ( ) ;
2015-06-08 15:50:35 +00:00
int64_t time_to_maint = ( dpo . next_maintenance_time - now ) . to_seconds ( ) ;
//
// The code that generates the next maintenance time should
// only produce a result in the future. If this assert
// fails, then the next maintenance time algorithm is buggy.
//
assert ( time_to_maint > 0 ) ;
//
// Code for setting chain parameters should validate
// block_interval > 0 (as well as the humans proposing /
// voting on changes to block interval).
//
assert ( gpo . parameters . block_interval > 0 ) ;
uint64_t blocks_to_maint = ( uint64_t ( time_to_maint ) + gpo . parameters . block_interval - 1 ) / gpo . parameters . block_interval ;
// blocks_to_maint > 0 because time_to_maint > 0,
// which means numerator is at least equal to block_interval
2015-09-28 16:10:20 +00:00
budget_record rec ;
initialize_budget_record ( now , rec ) ;
share_type available_funds = rec . total_budget ;
2015-06-08 15:50:35 +00:00
share_type witness_budget = gpo . parameters . witness_pay_per_block . value * blocks_to_maint ;
2015-09-28 16:10:20 +00:00
rec . requested_witness_budget = witness_budget ;
2015-06-10 18:17:13 +00:00
witness_budget = std : : min ( witness_budget , available_funds ) ;
2015-09-28 16:10:20 +00:00
rec . witness_budget = witness_budget ;
2015-06-08 15:50:35 +00:00
available_funds - = witness_budget ;
fc : : uint128_t worker_budget_u128 = gpo . parameters . worker_budget_per_day . value ;
worker_budget_u128 * = uint64_t ( time_to_maint ) ;
worker_budget_u128 / = 60 * 60 * 24 ;
share_type worker_budget ;
if ( worker_budget_u128 > = available_funds . value )
worker_budget = available_funds ;
else
worker_budget = worker_budget_u128 . to_uint64 ( ) ;
2015-09-28 16:10:20 +00:00
rec . worker_budget = worker_budget ;
2015-06-08 15:50:35 +00:00
available_funds - = worker_budget ;
share_type leftover_worker_funds = worker_budget ;
2015-06-10 18:17:13 +00:00
pay_workers ( leftover_worker_funds ) ;
2015-09-28 16:10:20 +00:00
rec . leftover_worker_funds = leftover_worker_funds ;
2015-06-08 15:50:35 +00:00
available_funds + = leftover_worker_funds ;
2015-09-28 16:10:20 +00:00
rec . supply_delta = rec . witness_budget
+ rec . worker_budget
- rec . leftover_worker_funds
- rec . from_accumulated_fees
- rec . from_unused_witness_budget ;
2015-06-10 18:17:13 +00:00
modify ( core , [ & ] ( asset_dynamic_data_object & _core )
2015-06-08 15:50:35 +00:00
{
2015-09-28 16:10:20 +00:00
_core . current_supply = ( _core . current_supply + rec . supply_delta ) ;
assert ( rec . supply_delta = =
witness_budget
2015-06-18 20:06:42 +00:00
+ worker_budget
- leftover_worker_funds
- _core . accumulated_fees
2015-10-05 13:46:02 +00:00
- dpo . witness_budget
2015-06-18 20:06:42 +00:00
) ;
2015-06-08 15:50:35 +00:00
_core . accumulated_fees = 0 ;
2015-06-10 18:17:13 +00:00
} ) ;
2015-10-05 13:46:02 +00:00
2015-06-10 18:17:13 +00:00
modify ( dpo , [ & ] ( dynamic_global_property_object & _dpo )
2015-06-08 15:50:35 +00:00
{
2015-06-18 20:06:42 +00:00
// Since initial witness_budget was rolled into
// available_funds, we replace it with witness_budget
// instead of adding it.
_dpo . witness_budget = witness_budget ;
2015-06-08 15:50:35 +00:00
_dpo . last_budget_time = now ;
2015-06-10 18:17:13 +00:00
} ) ;
2015-06-08 15:50:35 +00:00
2015-09-28 16:10:20 +00:00
create < budget_record_object > ( [ & ] ( budget_record_object & _rec )
{
_rec . time = head_block_time ( ) ;
_rec . record = rec ;
} ) ;
2015-06-08 15:50:35 +00:00
// available_funds is money we could spend, but don't want to.
// we simply let it evaporate back into the reserve.
}
FC_CAPTURE_AND_RETHROW ( )
}
2016-01-27 15:30:32 +00:00
template < typename Visitor >
void visit_special_authorities ( const database & db , Visitor visit )
{
const auto & sa_idx = db . get_index_type < special_authority_index > ( ) . indices ( ) . get < by_id > ( ) ;
for ( const special_authority_object & sao : sa_idx )
{
const account_object & acct = sao . account ( db ) ;
if ( acct . owner_special_authority . which ( ) ! = special_authority : : tag < no_special_authority > : : value )
{
visit ( acct , true , acct . owner_special_authority ) ;
}
if ( acct . active_special_authority . which ( ) ! = special_authority : : tag < no_special_authority > : : value )
{
visit ( acct , false , acct . active_special_authority ) ;
}
}
}
void update_top_n_authorities ( database & db )
{
visit_special_authorities ( db ,
[ & ] ( const account_object & acct , bool is_owner , const special_authority & auth )
{
if ( auth . which ( ) = = special_authority : : tag < top_holders_special_authority > : : value )
{
// use index to grab the top N holders of the asset and vote_counter to obtain the weights
const top_holders_special_authority & tha = auth . get < top_holders_special_authority > ( ) ;
vote_counter vc ;
const auto & bal_idx = db . get_index_type < account_balance_index > ( ) . indices ( ) . get < by_asset_balance > ( ) ;
uint8_t num_needed = tha . num_top_holders ;
if ( num_needed = = 0 )
return ;
// find accounts
const auto range = bal_idx . equal_range ( boost : : make_tuple ( tha . asset ) ) ;
for ( const account_balance_object & bal : boost : : make_iterator_range ( range . first , range . second ) )
{
assert ( bal . asset_type = = tha . asset ) ;
if ( bal . owner = = acct . id )
continue ;
vc . add ( bal . owner , bal . balance . value ) ;
- - num_needed ;
if ( num_needed = = 0 )
break ;
}
db . modify ( acct , [ & ] ( account_object & a )
{
vc . finish ( is_owner ? a . owner : a . active ) ;
} ) ;
}
} ) ;
}
2016-02-11 09:59:28 +00:00
void create_buyback_orders ( database & db )
{
const auto & bbo_idx = db . get_index_type < buyback_index > ( ) . indices ( ) . get < by_id > ( ) ;
const auto & bal_idx = db . get_index_type < account_balance_index > ( ) . indices ( ) . get < by_account_asset > ( ) ;
for ( const buyback_object & bbo : bbo_idx )
{
const asset_object & asset_to_buy = bbo . asset_to_buy ( db ) ;
assert ( asset_to_buy . buyback_account . valid ( ) ) ;
const account_object & buyback_account = ( * ( asset_to_buy . buyback_account ) ) ( db ) ;
asset_id_type next_asset = asset_id_type ( ) ;
if ( ! buyback_account . allowed_assets . valid ( ) )
{
wlog ( " skipping buyback account ${b} at block ${n} because allowed_assets does not exist " , ( " b " , buyback_account ) ( " n " , db . head_block_num ( ) ) ) ;
continue ;
}
while ( true )
{
auto it = bal_idx . lower_bound ( boost : : make_tuple ( buyback_account . id , next_asset ) ) ;
if ( it = = bal_idx . end ( ) )
break ;
if ( it - > owner ! = buyback_account . id )
break ;
asset_id_type asset_to_sell = it - > asset_type ;
share_type amount_to_sell = it - > balance ;
next_asset = asset_to_sell + 1 ;
if ( asset_to_sell = = asset_to_buy . id )
continue ;
if ( amount_to_sell = = 0 )
continue ;
if ( buyback_account . allowed_assets - > find ( asset_to_sell ) = = buyback_account . allowed_assets - > end ( ) )
{
wlog ( " buyback account ${b} not selling disallowed holdings of asset ${a} at block ${n} " , ( " b " , buyback_account ) ( " a " , asset_to_sell ) ( " n " , db . head_block_num ( ) ) ) ;
continue ;
}
try
{
transaction_evaluation_state buyback_context ( & db ) ;
buyback_context . skip_fee_schedule_check = true ;
limit_order_create_operation create_vop ;
create_vop . fee = asset ( 0 , asset_id_type ( ) ) ;
create_vop . seller = buyback_account . id ;
create_vop . amount_to_sell = asset ( amount_to_sell , asset_to_sell ) ;
create_vop . min_to_receive = asset ( 1 , asset_to_buy . id ) ;
create_vop . expiration = time_point_sec : : maximum ( ) ;
create_vop . fill_or_kill = false ;
limit_order_id_type order_id = db . apply_operation ( buyback_context , create_vop ) . get < object_id_type > ( ) ;
if ( db . find ( order_id ) ! = nullptr )
{
limit_order_cancel_operation cancel_vop ;
cancel_vop . fee = asset ( 0 , asset_id_type ( ) ) ;
cancel_vop . order = order_id ;
cancel_vop . fee_paying_account = buyback_account . id ;
db . apply_operation ( buyback_context , cancel_vop ) ;
}
}
catch ( const fc : : exception & e )
{
// we can in fact get here, e.g. if asset issuer of buy/sell asset blacklists/whitelists the buyback account
wlog ( " Skipping buyback processing selling ${as} for ${ab} for buyback account ${b} at block ${n}; exception was ${e} " ,
( " as " , asset_to_sell ) ( " ab " , asset_to_buy ) ( " b " , buyback_account ) ( " n " , db . head_block_num ( ) ) ( " e " , e . to_detail_string ( ) ) ) ;
continue ;
}
}
}
return ;
}
2015-06-08 15:50:35 +00:00
void database : : perform_chain_maintenance ( const signed_block & next_block , const global_property_object & global_props )
{
2015-06-09 20:46:00 +00:00
const auto & gpo = get_global_properties ( ) ;
2016-02-11 09:59:28 +00:00
create_buyback_orders ( * this ) ;
2015-06-09 20:46:00 +00:00
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 =
2015-06-16 18:42:02 +00:00
( stake_account . options . voting_account = =
2015-08-26 19:35:36 +00:00
GRAPHENE_PROXY_TO_SELF_ACCOUNT ) ? stake_account
2015-06-16 18:42:02 +00:00
: d . get ( stake_account . options . voting_account ) ;
2015-06-09 20:46:00 +00:00
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 ;
2015-06-16 18:42:02 +00:00
for ( vote_id_type id : opinion_account . options . votes )
2015-06-09 20:46:00 +00:00
{
uint32_t offset = id . instance ( ) ;
// if they somehow managed to specify an illegal offset, ignore it.
if ( offset < d . _vote_tally_buffer . size ( ) )
2015-07-08 20:00:22 +00:00
d . _vote_tally_buffer [ offset ] + = voting_stake ;
2015-06-09 20:46:00 +00:00
}
2015-06-16 18:42:02 +00:00
if ( opinion_account . options . num_witness < = props . parameters . maximum_witness_count )
2015-06-09 20:46:00 +00:00
{
2015-06-16 18:42:02 +00:00
uint16_t offset = std : : min ( size_t ( opinion_account . options . num_witness / 2 ) ,
2015-06-09 20:46:00 +00:00
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.
2015-07-08 20:00:22 +00:00
d . _witness_count_histogram_buffer [ offset ] + = voting_stake ;
2015-06-09 20:46:00 +00:00
}
2015-06-16 18:42:02 +00:00
if ( opinion_account . options . num_committee < = props . parameters . maximum_committee_count )
2015-06-09 20:46:00 +00:00
{
2015-06-16 18:42:02 +00:00
uint16_t offset = std : : min ( size_t ( opinion_account . options . num_committee / 2 ) ,
2015-06-09 20:46:00 +00:00
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
2015-07-08 20:00:22 +00:00
d . _committee_count_histogram_buffer [ offset ] + = voting_stake ;
2015-06-09 20:46:00 +00:00
}
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 ) { }
2015-06-11 17:54:42 +00:00
void operator ( ) ( const account_object & a ) {
2015-06-15 21:28:20 +00:00
a . statistics ( d ) . process_fees ( a , d ) ;
2015-06-09 20:46:00 +00:00
}
2015-06-11 17:54:42 +00:00
} fee_helper ( * this , gpo ) ;
2015-06-09 20:46:00 +00:00
2016-01-27 15:30:32 +00:00
perform_account_maintenance ( std : : tie (
tally_helper ,
fee_helper
) ) ;
2015-06-08 15:50:35 +00:00
struct clear_canary {
clear_canary ( vector < uint64_t > & target ) : target ( target ) { }
~ clear_canary ( ) { target . clear ( ) ; }
private :
vector < uint64_t > & target ;
} ;
clear_canary a ( _witness_count_histogram_buffer ) ,
b ( _committee_count_histogram_buffer ) ,
c ( _vote_tally_buffer ) ;
2016-01-27 15:30:32 +00:00
update_top_n_authorities ( * this ) ;
2015-06-08 15:50:35 +00:00
update_active_witnesses ( ) ;
2015-07-13 20:06:02 +00:00
update_active_committee_members ( ) ;
2015-08-14 21:56:21 +00:00
update_worker_votes ( ) ;
2015-06-08 15:50:35 +00:00
2015-06-26 20:43:39 +00:00
modify ( gpo , [ this ] ( global_property_object & p ) {
// Remove scaling of account registration fee
const auto & dgpo = get_dynamic_global_properties ( ) ;
2015-08-10 20:39:09 +00:00
p . parameters . current_fees - > get < account_create_operation > ( ) . basic_fee > > = p . parameters . account_fee_scale_bitshifts *
2015-06-26 20:43:39 +00:00
( dgpo . accounts_registered_this_interval / p . parameters . accounts_per_fee_scale ) ;
if ( p . pending_parameters )
{
2015-06-08 15:50:35 +00:00
p . parameters = std : : move ( * p . pending_parameters ) ;
p . pending_parameters . reset ( ) ;
2015-06-26 20:43:39 +00:00
}
} ) ;
2015-06-08 15:50:35 +00:00
auto next_maintenance_time = get < dynamic_global_property_object > ( dynamic_global_property_id_type ( ) ) . next_maintenance_time ;
2015-06-11 17:54:42 +00:00
auto maintenance_interval = gpo . parameters . maintenance_interval ;
2015-06-08 15:50:35 +00:00
if ( next_maintenance_time < = next_block . timestamp )
{
if ( next_block . block_num ( ) = = 1 )
next_maintenance_time = time_point_sec ( ) +
( ( ( next_block . timestamp . sec_since_epoch ( ) / maintenance_interval ) + 1 ) * maintenance_interval ) ;
else
2015-09-16 15:07:36 +00:00
{
// We want to find the smallest k such that next_maintenance_time + k * maintenance_interval > head_block_time()
// This implies k > ( head_block_time() - next_maintenance_time ) / maintenance_interval
//
// Let y be the right-hand side of this inequality, i.e.
// y = ( head_block_time() - next_maintenance_time ) / maintenance_interval
//
// and let the fractional part f be y-floor(y). Clearly 0 <= f < 1.
// We can rewrite f = y-floor(y) as floor(y) = y-f.
//
// Clearly k = floor(y)+1 has k > y as desired. Now we must
// show that this is the least such k, i.e. k-1 <= y.
//
// But k-1 = floor(y)+1-1 = floor(y) = y-f <= y.
// So this k suffices.
//
auto y = ( head_block_time ( ) - next_maintenance_time ) . to_seconds ( ) / maintenance_interval ;
next_maintenance_time + = ( y + 1 ) * maintenance_interval ;
}
2015-06-08 15:50:35 +00:00
}
modify ( get_dynamic_global_properties ( ) , [ next_maintenance_time ] ( dynamic_global_property_object & d ) {
d . next_maintenance_time = next_maintenance_time ;
2015-06-26 20:43:39 +00:00
d . accounts_registered_this_interval = 0 ;
2015-06-08 15:50:35 +00:00
} ) ;
// Reset all BitAsset force settlement volumes to zero
for ( const asset_bitasset_data_object * d : get_index_type < asset_bitasset_data_index > ( ) )
modify ( * d , [ ] ( asset_bitasset_data_object & d ) { d . force_settled_volume = 0 ; } ) ;
// process_budget needs to run at the bottom because
// it needs to know the next_maintenance_time
process_budget ( ) ;
}
} }