diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index ffbb9663fbc831caf991e529e6dfd4861f575c95..0516d8f3f0fe6adc82b605551ddb980768124854 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -1079,7 +1079,7 @@ void chain_controller::require_account(const types::AccountName& name) const { const producer_object& chain_controller::validate_block_header(uint32_t skip, const signed_block& next_block)const { EOS_ASSERT(head_block_id() == next_block.previous, block_validate_exception, "", ("head_block_id",head_block_id())("next.prev",next_block.previous)); - EOS_ASSERT(head_block_time() < next_block.timestamp, block_validate_exception, "", + EOS_ASSERT(head_block_time() < (fc::time_point_sec)next_block.timestamp, block_validate_exception, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num())); if (next_block.block_num() % config::BlocksPerRound != 0) { EOS_ASSERT(next_block.producer_changes.empty(), block_validate_exception, @@ -1091,7 +1091,7 @@ const producer_object& chain_controller::validate_block_header(uint32_t skip, co block_validate_exception, "Producer changes are not sorted correctly", ("changes", next_block.producer_changes)); } - const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time(next_block.timestamp))); + const producer_object& producer = get_producer(get_scheduled_producer(get_slot_at_time((fc::time_point)next_block.timestamp))); if(!(skip&skip_producer_signature)) EOS_ASSERT(next_block.validate_signee(producer.signing_key), block_validate_exception, @@ -1157,6 +1157,10 @@ const dynamic_global_property_object&chain_controller::get_dynamic_global_proper } time_point_sec chain_controller::head_block_time()const { + return (time_point)head_block_timestamp(); +} + +block_timestamp_type chain_controller::head_block_timestamp()const { return get_dynamic_global_properties().time; } @@ -1355,8 +1359,9 @@ ProducerRound chain_controller::calculate_next_round(const signed_block& next_bl EOS_ASSERT(boost::range::equal(next_block.producer_changes, changes), block_validate_exception, "Unexpected round changes in new block header", ("expected changes", changes)("block changes", next_block.producer_changes)); - - utilities::rand::random rng(next_block.timestamp.sec_since_epoch()); + + fc::time_point tp = (fc::time_point)next_block.timestamp; + utilities::rand::random rng(tp.sec_since_epoch()); rng.shuffle(schedule); return schedule; } @@ -1364,7 +1369,7 @@ ProducerRound chain_controller::calculate_next_round(const signed_block& next_bl void chain_controller::update_global_dynamic_data(const signed_block& b) { const dynamic_global_property_object& _dgp = _db.get(); - uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time(b.timestamp); + uint32_t missed_blocks = head_block_num() == 0? 1 : get_slot_at_time((fc::time_point)b.timestamp); assert(missed_blocks != 0); missed_blocks--; @@ -1409,7 +1414,7 @@ void chain_controller::update_global_dynamic_data(const signed_block& b) { void chain_controller::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block) { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( new_block.timestamp ); + uint64_t new_block_aslot = dpo.current_absolute_slot + get_slot_at_time( (fc::time_point)new_block.timestamp ); _db.modify( signing_producer, [&]( producer_object& _wit ) { @@ -1490,36 +1495,38 @@ types::AccountName chain_controller::get_scheduled_producer(uint32_t slot_num)co const dynamic_global_property_object& dpo = get_dynamic_global_properties(); uint64_t current_aslot = dpo.current_absolute_slot + slot_num; const auto& gpo = _db.get(); - return gpo.active_producers[current_aslot % gpo.active_producers.size()]; + auto number_of_active_producers = gpo.active_producers.size(); + auto index = current_aslot % (number_of_active_producers * 4); //TODO configure number of repetitions by producer + index /= 4; + return gpo.active_producers[index]; } fc::time_point_sec chain_controller::get_slot_time(uint32_t slot_num)const { - if( slot_num == 0 ) - return fc::time_point_sec(); + if( slot_num == 0) + return (fc::time_point)block_timestamp_type(); - auto interval = block_interval(); const dynamic_global_property_object& dpo = get_dynamic_global_properties(); if( head_block_num() == 0 ) { // n.b. first block is at genesis_time plus one block interval - fc::time_point_sec genesis_time = dpo.time; - return genesis_time + slot_num * interval; + auto genesis_time = block_timestamp_type(dpo.time); + genesis_time.slot += slot_num; + return (fc::time_point)genesis_time; } - int64_t head_block_abs_slot = head_block_time().sec_since_epoch() / interval; - fc::time_point_sec head_slot_time(head_block_abs_slot * interval); - - return head_slot_time + (slot_num * interval); + auto head_block_abs_slot = head_block_timestamp(); + head_block_abs_slot.slot += slot_num; + return (fc::time_point)head_block_abs_slot; } uint32_t chain_controller::get_slot_at_time(fc::time_point_sec when)const { - fc::time_point_sec first_slot_time = get_slot_time( 1 ); + fc::time_point_sec first_slot_time = get_slot_time(1); if( when < first_slot_time ) return 0; - return (when - first_slot_time).to_seconds() / block_interval() + 1; + return block_timestamp_type(when).slot - block_timestamp_type(first_slot_time).slot + 1; } uint32_t chain_controller::producer_participation_rate()const diff --git a/libraries/chain/include/eos/chain/block.hpp b/libraries/chain/include/eos/chain/block.hpp index 2e8b783af5f14185cf704742b37d820ef6211a36..7fe2890790b44c34dd63308f6f8e4f854e3e7848 100644 --- a/libraries/chain/include/eos/chain/block.hpp +++ b/libraries/chain/include/eos/chain/block.hpp @@ -3,6 +3,7 @@ * @copyright defined in eos/LICENSE.txt */ #pragma once +#include #include namespace eosio { namespace chain { @@ -15,7 +16,7 @@ namespace eosio { namespace chain { block_id_type previous; - fc::time_point_sec timestamp; + block_timestamp_type timestamp; checksum_type transaction_merkle_root; AccountName producer; /** diff --git a/libraries/chain/include/eos/chain/block_timestamp.hpp b/libraries/chain/include/eos/chain/block_timestamp.hpp new file mode 100644 index 0000000000000000000000000000000000000000..07d3123f3c9d73f00e0f75cd56130cab7e4c8672 --- /dev/null +++ b/libraries/chain/include/eos/chain/block_timestamp.hpp @@ -0,0 +1,91 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace eosio { namespace chain { + + /** + * This class is used in the block headers to represent the block time + * It is a parameterised class that takes an Epoch in milliseconds and + * and an interval in milliseconds and computes the number of slots. + **/ + template + class block_timestamp { + public: + explicit block_timestamp( uint32_t s=0 ) :slot(s){} + + block_timestamp(const fc::time_point& t) { + set_time_point(t); + } + + block_timestamp(const fc::time_point_sec& t) { + set_time_point(t); + } + + static block_timestamp maximum() { return block_timestamp( 0xffff ); } + static block_timestamp min() { return block_timestamp(0); } + + operator fc::time_point() const { + int64_t msec = slot * (int64_t)Interval; + msec += EpochMs; + return fc::time_point(fc::microseconds(msec * 1000)); + } + + void operator = (const fc::time_point& t ) { + set_time_point(t); + } + + void operator = (const fc::time_point_sec& t ) { + set_time_point(t); + } + + bool operator > ( const block_timestamp& t )const { return slot > t.slot; } + bool operator >=( const block_timestamp& t )const { return slot >= t.slot; } + bool operator < ( const block_timestamp& t )const { return slot < t.slot; } + bool operator <=( const block_timestamp& t )const { return slot <= t.slot; } + bool operator ==( const block_timestamp& t )const { return slot == t.slot; } + bool operator !=( const block_timestamp& t )const { return slot != t.slot; } + uint32_t slot; + + private: + void set_time_point(const fc::time_point& t) { + auto micro_since_epoch = t.time_since_epoch(); + auto msec_since_epoch = micro_since_epoch.count() / 1000; + slot = ( msec_since_epoch - EpochMs ) / Interval; + } + + void set_time_point(const fc::time_point_sec& t) { + uint64_t sec_since_epoch = t.sec_since_epoch(); + slot = (sec_since_epoch * 1000 - EpochMs) / Interval; + } + }; // block_timestamp + + typedef block_timestamp<3000,946684800000ll> block_timestamp_type; // epoch is year 2000. + +} } /// eosio::chain + + +#include +FC_REFLECT(eosio::chain::block_timestamp_type, (slot)) + +namespace fc { + template + void to_variant(const eosio::chain::block_timestamp& t, fc::variant& v) { + auto tp = (fc::time_point)t; + to_variant(tp, v); + } + + template + void from_variant(const fc::variant& v, eosio::chain::block_timestamp& t) { + fc::microseconds mc; + from_variant(v, mc); + t = fc::time_point(mc); + } +} + +#ifdef _MSC_VER + #pragma warning (pop) +#endif /// #ifdef _MSC_VER diff --git a/libraries/chain/include/eos/chain/chain_controller.hpp b/libraries/chain/include/eos/chain/chain_controller.hpp index 84f561090523ed393a150580f97a5676482f09d3..d27f741213cc8115e3dbe95fd6c05eb0e5a8f2c1 100644 --- a/libraries/chain/include/eos/chain/chain_controller.hpp +++ b/libraries/chain/include/eos/chain/chain_controller.hpp @@ -252,10 +252,11 @@ namespace eosio { namespace chain { const dynamic_global_property_object& get_dynamic_global_properties()const; const producer_object& get_producer(const AccountName& ownerName)const; - time_point_sec head_block_time()const; - uint32_t head_block_num()const; - block_id_type head_block_id()const; - AccountName head_block_producer()const; + time_point_sec head_block_time()const; + block_timestamp_type head_block_timestamp() const; + uint32_t head_block_num()const; + block_id_type head_block_id()const; + AccountName head_block_producer()const; uint32_t block_interval()const { return config::BlockIntervalSeconds; } diff --git a/libraries/chain/include/eos/chain/config.hpp b/libraries/chain/include/eos/chain/config.hpp index d306db5168b68999170155dbd4163d263db80d65..9a376660ed859c341436c7b50910fb8fd8c4a7b3 100644 --- a/libraries/chain/include/eos/chain/config.hpp +++ b/libraries/chain/include/eos/chain/config.hpp @@ -53,7 +53,8 @@ const static UInt32 DefaultMaxInlineMsgSize = 4 * 1024; const static UInt32 DefaultMaxGenTrxSize = 64 * 1024; const static UInt32 ProducersAuthorityThreshold = 14; -const static int BlocksPerRound = 21; +const static int ProducerRepetitions = 4; +const static int BlocksPerRound = 21 * ProducerRepetitions; const static int VotedProducersPerRound = 20; const static int IrreversibleThresholdPercent = 70 * Percent1; const static int MaxProducerVotes = 30; diff --git a/libraries/chain/include/eos/chain/global_property_object.hpp b/libraries/chain/include/eos/chain/global_property_object.hpp index 6b4cc9c784941d5bf2af3ebb153afe46f88aeb09..d135efc399b80fb2265be40587fcbdeac581f9bd 100644 --- a/libraries/chain/include/eos/chain/global_property_object.hpp +++ b/libraries/chain/include/eos/chain/global_property_object.hpp @@ -7,10 +7,9 @@ #include #include +#include #include - #include - #include "multi_index_includes.hpp" namespace eosio { namespace chain { @@ -47,12 +46,12 @@ namespace eosio { namespace chain { { OBJECT_CTOR(dynamic_global_property_object) - id_type id; - uint32_t head_block_number = 0; - block_id_type head_block_id; - time_point_sec time; - AccountName current_producer; - uint32_t accounts_registered_this_interval = 0; + id_type id; + uint32_t head_block_number = 0; + block_id_type head_block_id; + fc::time_point time; + AccountName current_producer; + uint32_t accounts_registered_this_interval = 0; /** * The current absolute slot number. Equal to the total diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 14329df97f9a8537e2a4dd5cbda66b2e78c97e58..999110bc5e0706a67f73027129704c9ca674b1b6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,7 +26,6 @@ if(WASM_TOOLCHAIN) target_link_libraries( slow_test eos_native_contract eos_chain chainbase eos_utilities eos_egenesis_none chain_plugin producer_plugin fc ${PLATFORM_SPECIFIC_LIBS} ) target_include_directories( slow_test PUBLIC ${CMAKE_BINARY_DIR}/contracts ) add_dependencies(slow_test currency exchange) - add_subdirectory(api_tests/memory_test) add_subdirectory(api_tests/extended_memory_test) add_subdirectory(api_tests/table_abi_test) diff --git a/tests/tests/block_timestamp_tests.cpp b/tests/tests/block_timestamp_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80ed628c0d2bced1247f25e837b74e2997aab221 --- /dev/null +++ b/tests/tests/block_timestamp_tests.cpp @@ -0,0 +1,39 @@ +/** + * @file + * @copyright defined in eos/LICENSE.txt + */ + +#include +#include +#include +#include + +using namespace eosio; +using namespace chain; + + + +BOOST_AUTO_TEST_SUITE(block_timestamp_tests) + + +BOOST_AUTO_TEST_CASE(constructor_test) { + block_timestamp_type bt; + BOOST_TEST( bt.slot == 0, "Default constructor gives wrong value"); + + fc::time_point t(fc::seconds(978307200)); + block_timestamp_type bt2(t); + BOOST_TEST( bt2.slot == (978307200 - 946684800)*2, "Time point constructor gives wrong value"); +} + +BOOST_AUTO_TEST_CASE(conversion_test) { + block_timestamp_type bt; + fc::time_point t = (fc::time_point)bt; + BOOST_TEST(t.time_since_epoch().to_seconds() == 946684800ll, "Time point conversion failed"); + + block_timestamp_type bt1(200); + t = (fc::time_point)bt1; + BOOST_TEST(t.time_since_epoch().to_seconds() == 946684900ll, "Time point conversion failed"); + +} + +BOOST_AUTO_TEST_SUITE_END()