Remove block randomness and rewrite witness scheduling

This commit is contained in:
theoreticalbts 2015-08-25 17:54:04 -04:00
parent dcc4f8076b
commit 6c052294e1
22 changed files with 123 additions and 1022 deletions

View file

@ -781,7 +781,6 @@ namespace graphene { namespace app {
break;
} case impl_block_summary_object_type:{
} case impl_account_transaction_history_object_type:{
} case impl_witness_schedule_object_type: {
} case impl_chain_property_object_type: {
}
}

View file

@ -267,24 +267,6 @@ signed_block database::_generate_block(
_pending_block.timestamp = when;
// Genesis witnesses start with a default initial secret
if( witness_obj.next_secret_hash == secret_hash_type::hash( secret_hash_type() ) )
{
_pending_block.previous_secret = secret_hash_type();
}
else
{
secret_hash_type::encoder last_enc;
fc::raw::pack( last_enc, block_signing_private_key );
fc::raw::pack( last_enc, witness_obj.previous_secret );
_pending_block.previous_secret = last_enc.result();
}
secret_hash_type::encoder next_enc;
fc::raw::pack( next_enc, block_signing_private_key );
fc::raw::pack( next_enc, _pending_block.previous_secret );
_pending_block.next_secret_hash = secret_hash_type::hash(next_enc.result());
_pending_block.transaction_merkle_root = _pending_block.calculate_merkle_root();
_pending_block.witness = witness_id;
@ -404,7 +386,6 @@ void database::_apply_block( const signed_block& next_block )
++_current_trx_in_block;
}
update_witness_schedule(next_block);
update_global_dynamic_data(next_block);
update_signing_witness(signing_witness, next_block);
@ -555,8 +536,6 @@ const witness_object& database::validate_block_header( uint32_t skip, const sign
FC_ASSERT( _pending_block.previous == next_block.previous, "", ("pending.prev",_pending_block.previous)("next.prev",next_block.previous) );
FC_ASSERT( _pending_block.timestamp <= next_block.timestamp, "", ("_pending_block.timestamp",_pending_block.timestamp)("next",next_block.timestamp)("blocknum",next_block.block_num()) );
const witness_object& witness = next_block.witness(*this);
FC_ASSERT( secret_hash_type::hash( next_block.previous_secret ) == witness.next_secret_hash, "",
("previous_secret", next_block.previous_secret)("next_secret_hash", witness.next_secret_hash));
if( !(skip&skip_witness_signature) )
FC_ASSERT( next_block.validate_signee( witness.signing_key ) );

View file

@ -30,7 +30,6 @@
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/withdraw_permission_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/account_evaluator.hpp>
#include <graphene/chain/asset_evaluator.hpp>
@ -107,9 +106,6 @@ const uint8_t withdraw_permission_object::type_id;
const uint8_t witness_object::space_id;
const uint8_t witness_object::type_id;
const uint8_t witness_schedule_object::space_id;
const uint8_t witness_schedule_object::type_id;
const uint8_t worker_object::space_id;
const uint8_t worker_object::type_id;
@ -193,7 +189,6 @@ void database::initialize_indexes()
add_index< primary_index<simple_index<account_statistics_object >> >();
add_index< primary_index<simple_index<asset_dynamic_data_object >> >();
add_index< primary_index<flat_index< block_summary_object >> >();
add_index< primary_index<simple_index<witness_schedule_object >> >();
add_index< primary_index<simple_index<chain_property_object > > >();
}
@ -317,6 +312,7 @@ void database::init_genesis(const genesis_state_type& genesis_state)
p.time = genesis_state.initial_timestamp;
p.dynamic_flags = 0;
p.witness_budget = 0;
p.recent_slots_filled = fc::uint128::max_value();
});
FC_ASSERT( (genesis_state.immutable_parameters.min_witness_count & 1) == 1, "min_witness_count must be odd" );
@ -538,31 +534,6 @@ void database::init_genesis(const genesis_state_type& genesis_state)
}
});
// Initialize witness schedule
#ifndef NDEBUG
const witness_schedule_object& wso =
#endif
create<witness_schedule_object>([&](witness_schedule_object& _wso)
{
memset(_wso.rng_seed.begin(), 0, _wso.rng_seed.size());
witness_scheduler_rng rng(_wso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
auto init_witnesses = get_global_properties().active_witnesses;
_wso.scheduler = witness_scheduler();
_wso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
_wso.scheduler.update(init_witnesses);
for( size_t i=0; i<init_witnesses.size(); ++i )
_wso.scheduler.produce_schedule(rng);
_wso.last_scheduling_block = 0;
_wso.recent_slots_filled = fc::uint128::max_value();
});
assert( wso.id == witness_schedule_id_type() );
// Enable fees
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;

View file

@ -16,6 +16,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/multiprecision/integer.hpp>
#include <fc/smart_ref_impl.hpp>
#include <fc/uint128.hpp>
@ -27,7 +29,6 @@
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/worker_evaluator.hpp>
namespace graphene { namespace chain {
@ -206,11 +207,6 @@ void database::update_active_witnesses()
});
});
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
modify(wso, [&](witness_schedule_object& _wso)
{
_wso.scheduler.update(gpo.active_witnesses);
});
} FC_CAPTURE_AND_RETHROW() }
void database::update_active_committee_members()

View file

@ -22,6 +22,7 @@
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <functional>
#include <iostream>
namespace graphene { namespace chain {

View file

@ -36,25 +36,18 @@ void database::update_global_dynamic_data( const signed_block& b )
const dynamic_global_property_object& _dgp =
dynamic_global_property_id_type(0)(*this);
const auto& global_props = get_global_properties();
auto delta_time = b.timestamp - _dgp.time;
auto missed_blocks = (delta_time.to_seconds() / global_props.parameters.block_interval) - 1;
if( _dgp.head_block_number == 0 )
missed_blocks = 0;
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
assert( missed_blocks != 0 );
missed_blocks--;
// dynamic global properties updating
modify( _dgp, [&]( dynamic_global_property_object& dgp ){
secret_hash_type::encoder enc;
fc::raw::pack( enc, dgp.random );
fc::raw::pack( enc, b.previous_secret );
dgp.random = enc.result();
if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() )
dgp.recently_missed_count = 0;
else if( missed_blocks )
dgp.recently_missed_count += 4*missed_blocks;
else if( dgp.recently_missed_count > 4 )
dgp.recently_missed_count -= 3;
dgp.recently_missed_count += GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks;
else if( dgp.recently_missed_count > GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT )
dgp.recently_missed_count -= GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT;
else if( dgp.recently_missed_count > 0 )
dgp.recently_missed_count--;
@ -62,6 +55,10 @@ void database::update_global_dynamic_data( const signed_block& b )
dgp.head_block_id = b.id();
dgp.time = b.timestamp;
dgp.current_witness = b.witness;
dgp.recent_slots_filled = (
(dgp.recent_slots_filled << 1)
+ 1) << missed_blocks;
dgp.current_aslot += missed_blocks+1;
});
if( !(get_node_properties().skip_flags & skip_undo_history_check) )
@ -80,6 +77,7 @@ void database::update_signing_witness(const witness_object& signing_witness, con
{
const global_property_object& gpo = get_global_properties();
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
uint64_t new_block_aslot = dpo.current_aslot + get_slot_at_time( new_block.timestamp );
share_type witness_pay = std::min( gpo.parameters.witness_pay_per_block, dpo.witness_budget );
@ -92,8 +90,7 @@ void database::update_signing_witness(const witness_object& signing_witness, con
modify( signing_witness, [&]( witness_object& _wit )
{
_wit.previous_secret = new_block.previous_secret;
_wit.next_secret_hash = new_block.next_secret_hash;
_wit.last_aslot = new_block_aslot;
} );
}

View file

@ -15,45 +15,87 @@
* 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.
*/
#pragma once
#include <graphene/chain/database.hpp>
#include <graphene/chain/global_property_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/witness_object.hpp>
namespace graphene { namespace chain {
witness_id_type database::get_scheduled_witness(uint32_t slot_num)const
using boost::container::flat_set;
witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
{
if( slot_num == 0 )
return witness_id_type();
//
// Each witness gets an arbitration key H(time, witness_id).
// The witness with the smallest key is selected to go first.
//
// As opposed to just using H(time) to determine an index into
// an array of eligible witnesses, this has the following desirable
// properties:
//
// - Avoid dynamic memory allocation
// - Decreases (but does not eliminate) the probability that a
// missed block will change the witness assigned to a future slot
//
// The hash function is xorshift* as given in
// [1] https://en.wikipedia.org/wiki/Xorshift#Xorshift.2A
//
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
const flat_set< witness_id_type >& active_witnesses = get_global_properties().active_witnesses;
uint32_t n = active_witnesses.size();
uint64_t min_witness_separation = (n / 2)+1;
uint64_t current_aslot = get_dynamic_global_properties().current_aslot + slot_num;
// ask the near scheduler who goes in the given slot
witness_id_type wid;
bool slot_is_near = wso.scheduler.get_slot(slot_num-1, wid);
if( ! slot_is_near )
uint64_t start_of_current_round_aslot = current_aslot - (current_aslot % n);
uint64_t first_ineligible_aslot = std::min(
start_of_current_round_aslot, current_aslot - min_witness_separation );
//
// overflow analysis of above subtraction:
//
// we always have min_witness_separation <= n, so
// if current_aslot < min_witness_separation it follows that
// start_of_current_round_aslot == 0
//
// therefore result of above min() is 0 when subtraction overflows
//
first_ineligible_aslot = std::max( first_ineligible_aslot, uint64_t( 1 ) );
uint64_t best_k = 0;
witness_id_type best_wit;
bool success = false;
uint64_t now_hi = get_slot_time( slot_num ).sec_since_epoch();
now_hi <<= 32;
for( const witness_id_type& wit_id : active_witnesses )
{
// if the near scheduler doesn't know, we have to extend it to
// a far scheduler.
// n.b. instantiating it is slow, but block gaps long enough to
// need it are likely pretty rare.
const witness_object& wit = wit_id(*this);
if( wit.last_aslot >= first_ineligible_aslot )
continue;
witness_scheduler_rng far_rng(wso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV);
far_future_witness_scheduler far_scheduler =
far_future_witness_scheduler(wso.scheduler, far_rng);
if( !far_scheduler.get_slot(slot_num-1, wid) )
uint64_t k = now_hi + uint64_t(wit_id);
k ^= (k >> 12);
k ^= (k << 25);
k ^= (k >> 27);
k *= 2685821657736338717ULL;
if( k >= best_k )
{
// no scheduled witness -- somebody set up us the bomb
// n.b. this code path is impossible, the present
// implementation of far_future_witness_scheduler
// returns true unconditionally
assert( false );
best_k = k;
best_wit = wit_id;
success = true;
}
}
return wid;
// the above loop should choose at least 1 because
// at most K elements are susceptible to the filter,
// otherwise we have an inconsistent database (such as
// wit.last_aslot values that are non-unique or in the future)
assert( success );
return best_wit;
}
fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
@ -98,89 +140,10 @@ uint32_t database::get_slot_at_time(fc::time_point_sec when)const
return (when - first_slot_time).to_seconds() / block_interval() + 1;
}
vector<witness_id_type> database::get_near_witness_schedule()const
{
const witness_schedule_object& wso = witness_schedule_id_type()(*this);
vector<witness_id_type> result;
result.reserve(wso.scheduler.size());
uint32_t slot_num = 0;
witness_id_type wid;
while( wso.scheduler.get_slot(slot_num++, wid) )
result.emplace_back(wid);
return result;
}
void database::update_witness_schedule(const signed_block& next_block)
{
auto start = fc::time_point::now();
const global_property_object& gpo = get_global_properties();
const witness_schedule_object& wso = get(witness_schedule_id_type());
uint32_t schedule_needs_filled = gpo.active_witnesses.size();
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
// We shouldn't be able to generate _pending_block with timestamp
// in the past, and incoming blocks from the network with timestamp
// in the past shouldn't be able to make it this far without
// triggering FC_ASSERT elsewhere
assert( schedule_slot > 0 );
witness_id_type first_witness;
bool slot_is_near = wso.scheduler.get_slot( schedule_slot-1, first_witness );
witness_id_type wit;
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
assert( witness_scheduler_rng::seed_length == wso.rng_seed.size() );
modify(wso, [&](witness_schedule_object& _wso)
{
_wso.slots_since_genesis += schedule_slot;
witness_scheduler_rng rng(wso.rng_seed.data, _wso.slots_since_genesis);
_wso.scheduler._min_token_count = std::max(int(gpo.active_witnesses.size()) / 2, 1);
if( slot_is_near )
{
uint32_t drain = schedule_slot;
while( drain > 0 )
{
if( _wso.scheduler.size() == 0 )
break;
_wso.scheduler.consume_schedule();
--drain;
}
}
else
{
_wso.scheduler.reset_schedule( first_witness );
}
while( !_wso.scheduler.get_slot(schedule_needs_filled, wit) )
{
if( _wso.scheduler.produce_schedule(rng) & emit_turn )
memcpy(_wso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
}
_wso.last_scheduling_block = next_block.block_num();
_wso.recent_slots_filled = (
(_wso.recent_slots_filled << 1)
+ 1) << (schedule_slot - 1);
});
auto end = fc::time_point::now();
static uint64_t total_time = 0;
static uint64_t calls = 0;
total_time += (end - start).count();
if( ++calls % 1000 == 0 )
idump( ( double(total_time/1000000.0)/calls) );
}
uint32_t database::witness_participation_rate()const
{
const witness_schedule_object& wso = get(witness_schedule_id_type());
return uint64_t(GRAPHENE_100_PERCENT) * wso.recent_slots_filled.popcount() / 128;
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
return uint64_t(GRAPHENE_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128;
}
} }

View file

@ -139,6 +139,9 @@
#define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 )
#define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4
#define GRAPHENE_RECENTLY_MISSED_COUNT_DECREMENT 3
/**
* Reserved Account IDs with special meaning
*/

View file

@ -244,11 +244,6 @@ namespace graphene { namespace chain {
*/
uint32_t get_slot_at_time(fc::time_point_sec when)const;
/**
* Get the near schedule.
*/
vector<witness_id_type> get_near_witness_schedule()const;
//////////////////// db_getter.cpp ////////////////////
const chain_id_type& get_chain_id()const;
@ -451,9 +446,6 @@ namespace graphene { namespace chain {
void update_maintenance_flag( bool new_maintenance_flag );
void update_withdraw_permissions();
//////////////////// db_witness_schedule.cpp ////////////////////
void update_witness_schedule(const signed_block& next_block); /// no-op except for scheduling blocks
///Steps performed only at maintenance intervals
///@{

View file

@ -42,7 +42,7 @@ namespace graphene { namespace chain {
chain_parameters parameters;
optional<chain_parameters> pending_parameters;
uint32_t next_available_vote_id = 0;
uint32_t next_available_vote_id = 0;
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
// n.b. witness scheduling is done by witness_schedule object
@ -64,7 +64,6 @@ namespace graphene { namespace chain {
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_dynamic_global_property_object_type;
secret_hash_type random;
uint32_t head_block_number = 0;
block_id_type head_block_id;
time_point_sec time;
@ -74,13 +73,28 @@ namespace graphene { namespace chain {
share_type witness_budget;
uint32_t accounts_registered_this_interval = 0;
/**
* Every time a block is missed this increases by 2, every time a block is found it decreases by 1 it is
* never less than 0
* Every time a block is missed this increases by
* RECENTLY_MISSED_COUNT_INCREMENT,
* every time a block is found it decreases by
* RECENTLY_MISSED_COUNT_DECREMENT. It is
* never less than 0.
*
* If the recently_missed_count hits 2*UNDO_HISTORY then no ew blocks may be pushed.
* If the recently_missed_count hits 2*UNDO_HISTORY then no new blocks may be pushed.
*/
uint32_t recently_missed_count = 0;
/**
* The current absolute slot number. Equal to the total
* number of slots since genesis. Also equal to the total
* number of missed slots plus head_block_number.
*/
uint64_t current_aslot = 0;
/**
* used to compute witness participation.
*/
fc::uint128_t recent_slots_filled;
/**
* dynamic_flags specifies chain state properties that can be
* expressed in one bit.
@ -104,7 +118,6 @@ namespace graphene { namespace chain {
}}
FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::db::object),
(random)
(head_block_number)
(head_block_id)
(time)
@ -113,6 +126,8 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
(witness_budget)
(accounts_registered_this_interval)
(recently_missed_count)
(current_aslot)
(recent_slots_filled)
(dynamic_flags)
)

View file

@ -27,8 +27,6 @@ namespace graphene { namespace chain {
uint32_t block_num()const { return num_from_id(previous) + 1; }
fc::time_point_sec timestamp;
witness_id_type witness;
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
checksum_type transaction_merkle_root;
extensions_type extensions;
@ -53,7 +51,6 @@ namespace graphene { namespace chain {
} } // graphene::chain
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)
(next_secret_hash)(previous_secret)(transaction_merkle_root)(extensions) )
FC_REFLECT( graphene::chain::block_header, (previous)(timestamp)(witness)(transaction_merkle_root)(extensions) )
FC_REFLECT_DERIVED( graphene::chain::signed_block_header, (graphene::chain::block_header), (witness_signature) )
FC_REFLECT_DERIVED( graphene::chain::signed_block, (graphene::chain::signed_block_header), (transactions) )

View file

@ -140,7 +140,6 @@ namespace graphene { namespace chain {
impl_transaction_object_type,
impl_block_summary_object_type,
impl_account_transaction_history_object_type,
impl_witness_schedule_object_type,
impl_blinded_balance_object_type,
impl_chain_property_object_type
};
@ -165,7 +164,6 @@ namespace graphene { namespace chain {
class operation_history_object;
class withdraw_permission_object;
class vesting_balance_object;
class witness_schedule_object;
class worker_object;
class balance_object;
@ -209,7 +207,6 @@ namespace graphene { namespace chain {
typedef object_id< implementation_ids,
impl_account_transaction_history_object_type,
account_transaction_history_object> account_transaction_history_id_type;
typedef object_id< implementation_ids, impl_witness_schedule_object_type, witness_schedule_object > witness_schedule_id_type;
typedef object_id< implementation_ids, impl_chain_property_object_type, chain_property_object> chain_property_id_type;
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
@ -286,7 +283,6 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
(impl_transaction_object_type)
(impl_block_summary_object_type)
(impl_account_transaction_history_object_type)
(impl_witness_schedule_object_type)
(impl_blinded_balance_object_type)
(impl_chain_property_object_type)
)
@ -317,7 +313,6 @@ FC_REFLECT_TYPENAME( graphene::chain::account_statistics_id_type )
FC_REFLECT_TYPENAME( graphene::chain::transaction_obj_id_type )
FC_REFLECT_TYPENAME( graphene::chain::block_summary_id_type )
FC_REFLECT_TYPENAME( graphene::chain::account_transaction_history_id_type )
FC_REFLECT_TYPENAME( graphene::chain::witness_schedule_id_type )
FC_REFLECT( graphene::chain::void_t, )
FC_REFLECT_ENUM( graphene::chain::asset_issuer_permission_flags, (charge_market_fee)(white_list)(transfer_restricted)(override_authority)(disable_force_settle)(global_settle)(disable_confidential) )

View file

@ -32,6 +32,7 @@ namespace graphene { namespace chain {
static const uint8_t type_id = witness_object_type;
account_id_type witness_account;
uint64_t last_aslot = 0;
public_key_type signing_key;
secret_hash_type next_secret_hash;
secret_hash_type previous_secret;
@ -64,9 +65,8 @@ namespace graphene { namespace chain {
FC_REFLECT_DERIVED( graphene::chain::witness_object, (graphene::db::object),
(witness_account)
(last_aslot)
(signing_key)
(next_secret_hash)
(previous_secret)
(pay_vb)
(vote_id)
(total_votes)

View file

@ -1,88 +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.
*/
#pragma once
// needed to serialize witness_scheduler
#include <fc/container/deque.hpp>
#include <fc/uint128.hpp>
#include <graphene/chain/protocol/types.hpp>
#include <graphene/chain/witness_scheduler.hpp>
#include <graphene/chain/witness_scheduler_rng.hpp>
namespace graphene { namespace chain {
typedef hash_ctr_rng<
/* HashClass = */ fc::sha256,
/* SeedLength = */ GRAPHENE_RNG_SEED_LENGTH
> witness_scheduler_rng;
typedef generic_witness_scheduler<
/* WitnessID = */ witness_id_type,
/* RNG = */ witness_scheduler_rng,
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
/* OffsetType = */ uint32_t,
/* debug = */ true
> witness_scheduler;
typedef generic_far_future_witness_scheduler<
/* WitnessID = */ witness_id_type,
/* RNG = */ witness_scheduler_rng,
/* CountType = */ decltype( chain_parameters::maximum_witness_count ),
/* OffsetType = */ uint32_t,
/* debug = */ true
> far_future_witness_scheduler;
class witness_schedule_object : public abstract_object<witness_schedule_object>
{
public:
static const uint8_t space_id = implementation_ids;
static const uint8_t type_id = impl_witness_schedule_object_type;
witness_scheduler scheduler;
uint32_t last_scheduling_block;
uint64_t slots_since_genesis = 0;
fc::array< char, sizeof(secret_hash_type) > rng_seed;
/**
* Not necessary for consensus, but used for figuring out the participation rate.
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
*/
fc::uint128 recent_slots_filled;
};
} }
FC_REFLECT( graphene::chain::witness_scheduler,
(_turns)
(_tokens)
(_min_token_count)
(_ineligible_waiting_for_token)
(_ineligible_no_turn)
(_eligible)
(_schedule)
(_lame_duck)
)
FC_REFLECT_DERIVED( graphene::chain::witness_schedule_object, (graphene::chain::object),
(scheduler)
(last_scheduling_block)
(slots_since_genesis)
(rng_seed)
(recent_slots_filled)
)

View file

@ -1,417 +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.
*/
#pragma once
#include <algorithm>
#include <deque>
#include <memory>
#include <vector>
#include <boost/container/flat_set.hpp>
namespace graphene { namespace chain {
//using boost::container::flat_set;
enum witness_scheduler_relax_flags
{
emit_turn = 0x01,
emit_token = 0x02
};
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
class generic_witness_scheduler
{
public:
void check_invariant() const
{
#ifndef NDEBUG
CountType tokens = _ineligible_no_turn.size() + _eligible.size();
CountType turns = _eligible.size();
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
turns += (item.second ? 1 : 0 );
assert( _tokens == tokens );
assert( _turns == turns );
#endif
set< WitnessID > witness_set;
// make sure each witness_id occurs only once among the three states
auto process_id = [&]( WitnessID item )
{
assert( witness_set.find( item ) == witness_set.end() );
witness_set.insert( item );
} ;
for( const std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
process_id( item.first );
for( const WitnessID& item : _ineligible_no_turn )
process_id( item );
for( const WitnessID& item : _eligible )
process_id( item );
return;
}
/**
* Deterministically evolve over time
*/
uint32_t relax()
{
uint32_t relax_flags = 0;
if( debug ) check_invariant();
assert( _min_token_count > 0 );
// turn distribution
if( _turns == 0 )
{
relax_flags |= emit_turn;
for( const WitnessID& item : _ineligible_no_turn )
_eligible.push_back( item );
_turns += _ineligible_no_turn.size();
_ineligible_no_turn.clear();
if( debug ) check_invariant();
for( std::pair< WitnessID, bool >& item : _ineligible_waiting_for_token )
{
assert( item.second == false );
item.second = true;
}
_turns += _ineligible_waiting_for_token.size();
if( debug ) check_invariant();
}
// token distribution
while( true )
{
if( _ineligible_waiting_for_token.empty() )
{
// eligible must be non-empty
assert( !_eligible.empty() );
return relax_flags;
}
if( _tokens >= _min_token_count )
{
if( !_eligible.empty() )
return relax_flags;
}
const std::pair< WitnessID, bool >& item = _ineligible_waiting_for_token.front();
if( item.second )
_eligible.push_back( item.first );
else
_ineligible_no_turn.push_back( item.first );
_ineligible_waiting_for_token.pop_front();
relax_flags |= emit_token;
_tokens++;
if( debug ) check_invariant();
}
return relax_flags;
}
/**
* Add another element to _schedule
*/
uint32_t produce_schedule( RNG& rng )
{
uint32_t relax_flags = relax();
if( debug ) check_invariant();
if( _eligible.empty() )
return relax_flags;
decltype( rng( _eligible.size() ) ) pos = rng( _eligible.size() );
assert( (pos >= 0) && (pos < _eligible.size()) );
auto it = _eligible.begin() + pos;
_schedule.push_back( *it );
_ineligible_waiting_for_token.emplace_back( *it, false );
_eligible.erase( it );
_turns--;
_tokens--;
if( debug ) check_invariant();
return relax_flags;
}
/**
* Pull an element from _schedule
*/
WitnessID consume_schedule()
{
assert( _schedule.size() > 0 );
WitnessID result = _schedule.front();
_schedule.pop_front();
auto it = _lame_duck.find( result );
if( it != _lame_duck.end() )
_lame_duck.erase( it );
if( debug ) check_invariant();
return result;
}
/**
* Remove all witnesses in the removal_set from
* future scheduling (but not from the current schedule).
*/
template< typename T >
void remove_all( const T& removal_set )
{
if( debug ) check_invariant();
_ineligible_waiting_for_token.erase(
std::remove_if(
_ineligible_waiting_for_token.begin(),
_ineligible_waiting_for_token.end(),
[&]( const std::pair< WitnessID, bool >& item ) -> bool
{
bool found = removal_set.find( item.first ) != removal_set.end();
_turns -= (found & item.second) ? 1 : 0;
return found;
} ),
_ineligible_waiting_for_token.end() );
if( debug ) check_invariant();
_ineligible_no_turn.erase(
std::remove_if(
_ineligible_no_turn.begin(),
_ineligible_no_turn.end(),
[&]( WitnessID item ) -> bool
{
bool found = (removal_set.find( item ) != removal_set.end());
_tokens -= (found ? 1 : 0);
return found;
} ),
_ineligible_no_turn.end() );
if( debug ) check_invariant();
_eligible.erase(
std::remove_if(
_eligible.begin(),
_eligible.end(),
[&]( WitnessID item ) -> bool
{
bool found = (removal_set.find( item ) != removal_set.end());
_tokens -= (found ? 1 : 0);
_turns -= (found ? 1 : 0);
return found;
} ),
_eligible.end() );
if( debug ) check_invariant();
return;
}
/**
* Convenience function to call insert_all() and remove_all()
* as needed to update to the given revised_set.
*/
template< typename T >
void insert_all( const T& insertion_set )
{
if( debug ) check_invariant();
for( const WitnessID wid : insertion_set )
{
_eligible.push_back( wid );
}
_turns += insertion_set.size();
_tokens += insertion_set.size();
if( debug ) check_invariant();
return;
}
/**
* Convenience function to call insert_all() and remove_all()
* as needed to update to the given revised_set.
*
* This function calls find() on revised_set for all current
* witnesses. Running time is O(n*log(n)) if the revised_set
* implementation of find() is O(log(n)).
*
* TODO: Rewriting to use std::set_difference may marginally
* increase efficiency, but a benchmark is needed to justify this.
*/
template< typename T >
void update( const T& revised_set )
{
set< WitnessID > current_set;
set< WitnessID > schedule_set;
/* current_set.reserve(
_ineligible_waiting_for_token.size()
+ _ineligible_no_turn.size()
+ _eligible.size()
+ _schedule.size() );
*/
for( const auto& item : _ineligible_waiting_for_token )
current_set.insert( item.first );
for( const WitnessID& item : _ineligible_no_turn )
current_set.insert( item );
for( const WitnessID& item : _eligible )
current_set.insert( item );
for( const WitnessID& item : _schedule )
{
current_set.insert( item );
schedule_set.insert( item );
}
set< WitnessID > insertion_set;
//insertion_set.reserve( revised_set.size() );
for( const WitnessID& item : revised_set )
{
if( current_set.find( item ) == current_set.end() )
insertion_set.insert( item );
}
set< WitnessID > removal_set;
//removal_set.reserve( current_set.size() );
for( const WitnessID& item : current_set )
{
if( revised_set.find( item ) == revised_set.end() )
{
if( schedule_set.find( item ) == schedule_set.end() )
removal_set.insert( item );
else
_lame_duck.insert( item );
}
}
insert_all( insertion_set );
remove_all( removal_set );
return;
}
/**
* Get the number of scheduled witnesses
*/
size_t size( )const
{
return _schedule.size();
}
bool get_slot( OffsetType offset, WitnessID& wit )const
{
if( offset >= _schedule.size() )
return false;
wit = _schedule[ offset ];
return true;
}
/**
* Reset the schedule, then re-schedule the given witness as the
* first witness.
*/
void reset_schedule( WitnessID first_witness )
{
_schedule.clear();
for( const WitnessID& wid : _ineligible_no_turn )
{
_eligible.push_back( wid );
}
_turns += _ineligible_no_turn.size();
_ineligible_no_turn.clear();
for( const auto& item : _ineligible_waiting_for_token )
{
_eligible.push_back( item.first );
_turns += (item.second ? 0 : 1);
}
_tokens += _ineligible_waiting_for_token.size();
_ineligible_waiting_for_token.clear();
if( debug ) check_invariant();
auto it = std::find( _eligible.begin(), _eligible.end(), first_witness );
assert( it != _eligible.end() );
_schedule.push_back( *it );
_ineligible_waiting_for_token.emplace_back( *it, false );
_eligible.erase( it );
_turns--;
_tokens--;
if( debug ) check_invariant();
return;
}
// keep track of total turns / tokens in existence
CountType _turns = 0;
CountType _tokens = 0;
// new tokens handed out when _tokens < _min_token_count
CountType _min_token_count;
// WitnessID appears in exactly one of the following:
// has no token; second indicates whether we have a turn or not:
std::deque < std::pair< WitnessID, bool > > _ineligible_waiting_for_token; // ".." | "T."
// has token, but no turn
std::vector< WitnessID > _ineligible_no_turn; // ".t"
// has token and turn
std::vector< WitnessID > _eligible; // "Tt"
// scheduled
std::deque < WitnessID > _schedule;
// in _schedule, but not to be replaced
set< WitnessID > _lame_duck;
};
template< typename WitnessID, typename RNG, typename CountType, typename OffsetType, bool debug = true >
class generic_far_future_witness_scheduler
{
public:
generic_far_future_witness_scheduler(
const generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug >& base_scheduler,
RNG rng
)
{
generic_witness_scheduler< WitnessID, RNG, CountType, OffsetType, debug > extended_scheduler = base_scheduler;
_begin_offset = base_scheduler.size()+1;
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
_begin_offset++;
assert( _begin_offset == extended_scheduler.size() );
_end_offset = _begin_offset;
while( (extended_scheduler.produce_schedule( rng ) & emit_turn) == 0 )
_end_offset++;
assert( _end_offset == extended_scheduler.size()-1 );
_schedule.resize( extended_scheduler._schedule.size() );
std::copy( extended_scheduler._schedule.begin(),
extended_scheduler._schedule.end(),
_schedule.begin() );
return;
}
bool get_slot( OffsetType offset, WitnessID& wit )const
{
if( offset <= _end_offset )
wit = _schedule[ offset ];
else
wit = _schedule[ _begin_offset +
(
(offset - _begin_offset) %
(_end_offset + 1 - _begin_offset)
) ];
return true;
}
std::vector< WitnessID > _schedule;
OffsetType _begin_offset;
OffsetType _end_offset;
};
} }

View file

@ -1,124 +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.
*/
#pragma once
#include <boost/multiprecision/integer.hpp>
namespace graphene { namespace chain {
/**
* Always returns 0. Useful for testing.
*/
class nullary_rng
{
public:
nullary_rng() {}
virtual ~nullary_rng() {}
template< typename T > T operator()( T max )
{ return T(0); }
} ;
/**
* The sha256_ctr_rng generates bits using SHA256 in counter (CTR)
* mode.
*/
template< class HashClass, int SeedLength=sizeof(secret_hash_type) >
class hash_ctr_rng
{
public:
hash_ctr_rng( const char* seed, uint64_t counter = 0 )
: _counter( counter ), _current_offset( 0 )
{
memcpy( _seed, seed, SeedLength );
_reset_current_value();
return;
}
virtual ~hash_ctr_rng() {}
uint64_t get_bits( uint8_t count )
{
uint64_t result = 0;
uint64_t mask = 1;
// grab the requested number of bits
while( count > 0 )
{
result |=
(
(
(
_current_value.data()[ (_current_offset >> 3) & 0x1F ]
& ( 1 << (_current_offset & 0x07) )
)
!= 0
) ? mask : 0
);
mask += mask;
--count;
++_current_offset;
if( _current_offset == (_current_value.data_size() << 3) )
{
_counter++;
_current_offset = 0;
_reset_current_value();
}
}
return result;
}
uint64_t operator()( uint64_t bound )
{
if( bound <= 1 )
return 0;
uint8_t bitcount = boost::multiprecision::detail::find_msb( bound ) + 1;
// probability of loop exiting is >= 1/2, so probability of
// running N times is bounded above by (1/2)^N
while( true )
{
uint64_t result = get_bits( bitcount );
if( result < bound )
return result;
}
}
// convenience method which does casting for types other than uint64_t
template< typename T > T operator()( T bound )
{ return (T) ( (*this)(uint64_t( bound )) ); }
void _reset_current_value()
{
// internal implementation detail, called to update
// _current_value when _counter changes
typename HashClass::encoder enc;
enc.write( _seed , SeedLength );
enc.write( (char *) &_counter, 8 );
_current_value = enc.result();
return;
}
uint64_t _counter;
char _seed[ SeedLength ];
HashClass _current_value;
uint16_t _current_offset;
static const int seed_length = SeedLength;
} ;
} }

View file

@ -478,7 +478,6 @@ public:
result["chain_id"] = chain_props.chain_id;
result["active_witnesses"] = global_props.active_witnesses;
result["active_committee_members"] = global_props.active_committee_members;
result["entropy"] = dynamic_props.random;
return result;
}
chain_property_object get_chain_properties() const

View file

@ -85,6 +85,7 @@ int main( int argc, char** argv )
std::cout << "\n";
}
std::cout << "]\n";
std::cerr << "Size of block header: " << sizeof( block_header ) << " " << fc::raw::pack_size( block_header() ) << "\n";
}
catch ( const fc::exception& e ){ edump((e.to_detail_string())); }
idump((sizeof(signed_block)));

View file

@ -25,7 +25,6 @@
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/chain/vesting_balance_object.hpp>
#include <fc/crypto/digest.hpp>

View file

@ -23,7 +23,6 @@
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/witness_scheduler_rng.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/db/simple_index.hpp>
@ -166,7 +165,6 @@ BOOST_AUTO_TEST_CASE( price_test )
BOOST_CHECK(dummy == dummy2);
}
BOOST_AUTO_TEST_CASE( memo_test )
{ try {
memo_data m;
@ -186,134 +184,6 @@ BOOST_AUTO_TEST_CASE( memo_test )
BOOST_CHECK_EQUAL(m.get_message(receiver, sender.get_public_key()), "Hello, world!");
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( witness_rng_test_bits )
{
try
{
const uint64_t COUNT = 131072;
const uint64_t HASH_SIZE = 32;
string ref_bits = "";
ref_bits.reserve( COUNT * HASH_SIZE );
static const char seed_data[] = "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55";
for( uint64_t i=0; i<COUNT; i++ )
{
// grab the bits
fc::sha256::encoder enc;
enc.write( seed_data, HASH_SIZE );
enc.put( char((i ) & 0xFF) );
enc.put( char((i >> 0x08) & 0xFF) );
enc.put( char((i >> 0x10) & 0xFF) );
enc.put( char((i >> 0x18) & 0xFF) );
enc.put( char((i >> 0x20) & 0xFF) );
enc.put( char((i >> 0x28) & 0xFF) );
enc.put( char((i >> 0x30) & 0xFF) );
enc.put( char((i >> 0x38) & 0xFF) );
fc::sha256 result = enc.result();
auto result_data = result.data();
std::copy( result_data, result_data+HASH_SIZE, std::back_inserter( ref_bits ) );
}
fc::sha256 seed = fc::sha256::hash( string("") );
// seed = sha256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
BOOST_CHECK( memcmp( seed.data(), seed_data, HASH_SIZE ) == 0 );
hash_ctr_rng< fc::sha256, 32 > test_rng(seed.data(), 0);
// python2 -c 'import hashlib; import struct; h = lambda x : hashlib.sha256(x).digest(); i = lambda x : struct.pack("<Q", x); print( h( h("") + i(0) ) )' | hd
string ref_bits_hex =
"5c5d42dcf39f71c0226ca720d8d518db615b5773f038e5e491963f6f47621bbd" // h( h("") + i(0) )
"43fd6dae047c400060be262e6d443200eacd1fafcb77828638085c2e2341fd8d" // h( h("") + i(1) )
"d666330a7441dc7279b786e65aba32817275989cfc691b3901f000fb0f14cd05" // h( h("") + i(2) )
"34bd93f83d7bac4a667d62fee39bd5eb1991fbadc29a5f216ea746772ca31544" // h( h("") + i(3) )
"d3b41a093eab01cd25f987a909b2f4812b0f38475e0fe40f6f42a12c6e018aa7" // ...
"c8db17b946c5a6bceaa7b903c93e6ccb8cc6c09b0cfd2108d930de1a79c3a68e"
"cc1945b36c82e356b6d127057d036a150cb03b760e9c9e706c560f32a749e80d"
"872b28fe97e289d4f6f361f3427d454113e3b513892d129398dac4daf8a0e43e"
"8d7a5a2f3cbb245fa471e87e30a38d9c775c985c28db6e521e34cf1e88507c26"
"c662f230eed0f10899c3a74a2d1bfb88d732909b206a2aed3ae0bda728fac8fe"
"38eface8b1d473e45cbb40603bcef8bf2219e55669c7a2cfb5f8d52610689f14"
"3b1d1734273b069a7de7cc6dd2e80db09d1feff200c9bdaf033cd553ea40e05d"
"16653ca7aa7f790a95c6a8d41e5694b0c6bff806c3ce3e0e320253d408fb6f27"
"b55df71d265de0b86a1cdf45d1d9c53da8ebf0ceec136affa12228d0d372e698"
"37e9305ce57d386d587039b49b67104fd4d8467e87546237afc9a90cf8c677f9"
"fc26784c94f754cf7aeacb6189e705e2f1873ea112940560f11dbbebb22a8922"
;
char* ref_bits_chars = new char[ ref_bits_hex.length() / 2 ];
fc::from_hex( ref_bits_hex, ref_bits_chars, ref_bits_hex.length() / 2 );
string ref_bits_str( ref_bits_chars, ref_bits_hex.length() / 2 );
delete[] ref_bits_chars;
ref_bits_chars = nullptr;
BOOST_CHECK( ref_bits_str.length() < ref_bits.length() );
BOOST_CHECK( ref_bits_str == ref_bits.substr( 0, ref_bits_str.length() ) );
//std::cout << "ref_bits_str: " << fc::to_hex( ref_bits_str.c_str(), std::min( ref_bits_str.length(), size_t(256) ) ) << "\n";
//std::cout << "ref_bits : " << fc::to_hex( ref_bits .c_str(), std::min( ref_bits.length(), size_t(256) ) ) << "\n";
// when we get to this point, our code to generate the RNG byte output is working.
// now let's test get_bits() as follows:
uint64_t ref_get_bits_offset = 0;
auto ref_get_bits = [&]( uint8_t count ) -> uint64_t
{
uint64_t result = 0;
uint64_t i = ref_get_bits_offset;
uint64_t mask = 1;
while( count > 0 )
{
if( ref_bits[ i >> 3 ] & (1 << (i & 7)) )
result |= mask;
mask += mask;
i++;
count--;
}
ref_get_bits_offset = i;
return result;
};
// use PRNG to decide between 0-64 bits
std::minstd_rand rng;
rng.seed( 9999 );
std::uniform_int_distribution< uint16_t > bit_dist( 0, 64 );
for( int i=0; i<10000; i++ )
{
uint8_t bit_count = bit_dist( rng );
uint64_t ref_bits = ref_get_bits( bit_count );
uint64_t test_bits = test_rng.get_bits( bit_count );
//std::cout << i << ": get(" << int(bit_count) << ") -> " << test_bits << " (expect " << ref_bits << ")\n";
if( bit_count < 64 )
{
BOOST_CHECK( ref_bits < (uint64_t( 1 ) << bit_count ) );
BOOST_CHECK( test_bits < (uint64_t( 1 ) << bit_count ) );
}
BOOST_CHECK( ref_bits == test_bits );
if( ref_bits != test_bits )
break;
}
std::uniform_int_distribution< uint64_t > whole_dist(
0, std::numeric_limits<uint64_t>::max() );
for( int i=0; i<10000; i++ )
{
uint8_t bit_count = bit_dist( rng );
uint64_t bound = whole_dist( rng ) & ((uint64_t(1) << bit_count) - 1);
//std::cout << "bound:" << bound << "\n";
uint64_t rnum = test_rng( bound );
//std::cout << "rnum:" << rnum << "\n";
if( bound > 1 )
{
BOOST_CHECK( rnum < bound );
}
else
{
BOOST_CHECK( rnum == 0 );
}
}
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( exceptions )
{
GRAPHENE_CHECK_THROW(FC_THROW_EXCEPTION(balance_claim_invalid_claim_amount, "Etc"), balance_claim_invalid_claim_amount);

View file

@ -25,7 +25,6 @@
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/market_evaluator.hpp>
#include <graphene/chain/witness_schedule_object.hpp>
#include <graphene/utilities/tempdir.hpp>
@ -644,7 +643,6 @@ BOOST_FIXTURE_TEST_CASE( maintenance_interval, database_fixture )
initial_properties.parameters.maximum_transaction_size);
BOOST_CHECK_EQUAL(db.get_dynamic_global_properties().next_maintenance_time.sec_since_epoch(),
db.head_block_time().sec_since_epoch() + db.get_global_properties().parameters.block_interval);
// shuffling is now handled by the witness_schedule_object.
BOOST_CHECK(db.get_global_properties().active_witnesses == initial_properties.active_witnesses);
BOOST_CHECK(db.get_global_properties().active_committee_members == initial_properties.active_committee_members);
@ -870,32 +868,6 @@ BOOST_FIXTURE_TEST_CASE( pop_block_twice, database_fixture )
}
}
BOOST_FIXTURE_TEST_CASE( witness_scheduler_missed_blocks, database_fixture )
{ try {
db.get_near_witness_schedule();
generate_block();
auto near_schedule = db.get_near_witness_schedule();
std::for_each(near_schedule.begin(), near_schedule.end(), [&](witness_id_type id) {
generate_block(0);
BOOST_CHECK(db.get_dynamic_global_properties().current_witness == id);
});
near_schedule = db.get_near_witness_schedule();
generate_block(0, init_account_priv_key, 2);
BOOST_CHECK(db.get_dynamic_global_properties().current_witness == near_schedule[2]);
near_schedule.erase(near_schedule.begin(), near_schedule.begin() + 3);
auto new_schedule = db.get_near_witness_schedule();
new_schedule.erase(new_schedule.end() - 3, new_schedule.end());
BOOST_CHECK(new_schedule == near_schedule);
std::for_each(near_schedule.begin(), near_schedule.end(), [&](witness_id_type id) {
generate_block(0);
BOOST_CHECK(db.get_dynamic_global_properties().current_witness == id);
});
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture )
{
try
@ -904,7 +876,7 @@ BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, database_fixture )
auto rsf = [&]() -> string
{
fc::uint128 rsf = db.get( witness_schedule_id_type() ).recent_slots_filled;
fc::uint128 rsf = db.get_dynamic_global_properties().recent_slots_filled;
string result = "";
result.reserve(128);
for( int i=0; i<128; i++ )

View file

@ -443,34 +443,15 @@ BOOST_AUTO_TEST_CASE( witness_create )
auto itr = std::find(witnesses.begin(), witnesses.end(), nathan_witness_id);
BOOST_CHECK(itr != witnesses.end());
generate_blocks(witnesses.size());
// make sure we're scheduled to produce
vector<witness_id_type> near_witnesses = db.get_near_witness_schedule();
BOOST_CHECK( std::find( near_witnesses.begin(), near_witnesses.end(), nathan_witness_id )
!= near_witnesses.end() );
struct generator_helper {
database_fixture& f;
witness_id_type nathan_id;
fc::ecc::private_key nathan_key;
bool nathan_generated_block;
void operator()(witness_id_type id) {
if( id == nathan_id )
{
nathan_generated_block = true;
f.generate_block(0, nathan_key);
} else
f.generate_block(0);
BOOST_CHECK_EQUAL(f.db.get_dynamic_global_properties().current_witness.instance.value, id.instance.value);
f.db.get_near_witness_schedule();
}
};
generator_helper h = std::for_each(near_witnesses.begin(), near_witnesses.end(),
generator_helper{*this, nathan_witness_id, nathan_private_key, false});
BOOST_CHECK(h.nathan_generated_block);
int produced = 0;
// Make sure we get scheduled exactly once in witnesses.size() blocks
// TODO: intense_test that repeats this loop many times
for( size_t i=0; i<witnesses.size(); i++ )
{
if( generate_block().witness == nathan_witness_id )
produced++;
}
BOOST_CHECK_EQUAL( produced, 1 );
} FC_LOG_AND_RETHROW() }
/**