diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 8fa9eaba..dccabbb0 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace graphene { namespace chain { @@ -305,7 +306,9 @@ signed_block database::_generate_block( _pending_block.transactions.clear(); bool failed = false; - try { push_block( tmp, skip ); } catch ( const fc::exception& e ) { failed = true; } + try { push_block( tmp, skip ); } + catch ( const undo_database_exception& e ) { throw; } + catch ( const fc::exception& e ) { failed = true; } if( failed ) { for( const auto& trx : tmp.transactions ) @@ -378,6 +381,14 @@ void database::apply_block( const signed_block& next_block, uint32_t skip ) { // WE CAN SKIP ALMOST EVERYTHING skip = ~0; + + /** clear the recently missed count because the checkpoint indicates that + * we will never have to go back further than this. + */ + const auto& _dgp = dynamic_global_property_id_type(0)(*this); + modify( _dgp, [&]( dynamic_global_property_object& dgp ){ + dgp.recently_missed_count = 0; + }); } } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 287a810e..ccbd7945 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -156,6 +156,7 @@ void database::initialize_evaluators() void database::initialize_indexes() { reset_indexes(); + _undo_db.set_max_size( GRAPHENE_MIN_UNDO_HISTORY ); //Protocol object indexes add_index< primary_index >(); diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp index d76db321..84456326 100644 --- a/libraries/chain/db_update.cpp +++ b/libraries/chain/db_update.cpp @@ -36,6 +36,10 @@ 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; + // // dynamic global properties updating // @@ -44,11 +48,27 @@ void database::update_global_dynamic_data( const signed_block& b ) fc::raw::pack( enc, dgp.random ); fc::raw::pack( enc, b.previous_secret ); dgp.random = enc.result(); + + if( missed_blocks ) + dgp.recently_missed_count += 2*missed_blocks; + else if( dgp.recently_missed_count > 0 ) + dgp.recently_missed_count--; + dgp.head_block_number = b.block_num(); dgp.head_block_id = b.id(); dgp.time = b.timestamp; dgp.current_witness = b.witness; }); + + if( !(get_node_properties().skip_flags & skip_undo_history_check) ) + { + GRAPHENE_ASSERT( _dgp.recently_missed_count < GRAPHENE_MAX_UNDO_HISTORY, undo_database_exception, + "The database does not have enough undo history to support a blockchain with so many missed blocks. " + "Please add a checkpoint if you would like to continue applying blocks beyond this point.", + ("recently_missed",_dgp.recently_missed_count)("max_undo",GRAPHENE_MAX_UNDO_HISTORY) ); + } + + _undo_db.set_max_size( _dgp.recently_missed_count + GRAPHENE_MIN_UNDO_HISTORY ); } void database::update_signing_witness(const witness_object& signing_witness, const signed_block& new_block) @@ -88,9 +108,8 @@ void database::clear_expired_transactions() auto& transaction_idx = static_cast(get_mutable_index(implementation_ids, impl_transaction_object_type)); const auto& dedupe_index = transaction_idx.indices().get(); const auto& global_parameters = get_global_properties().parameters; - auto forking_window_time = global_parameters.maximum_undo_history * global_parameters.block_interval; while( !dedupe_index.empty() - && head_block_time() - dedupe_index.rbegin()->trx.expiration >= fc::seconds(forking_window_time) ) + && head_block_time() - dedupe_index.rbegin()->trx.expiration >= fc::seconds(global_parameters.maximum_expiration) ) transaction_idx.remove(*dedupe_index.rbegin()); } diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 61add015..5fa13169 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -46,7 +46,10 @@ #define GRAPHENE_DEFAULT_MAX_BLOCK_SIZE (GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE*GRAPHENE_DEFAULT_BLOCK_INTERVAL*200000) #define GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION (60*60*24) // seconds, aka: 1 day #define GRAPHENE_DEFAULT_MAINTENANCE_INTERVAL (60*60*24) // seconds, aka: 1 day -#define GRAPHENE_DEFAULT_MAX_UNDO_HISTORY 1024 +#define GRAPHENE_DEFAULT_MAX_EXPIRATION_SEC (60*60) // 1 hour + +#define GRAPHENE_MIN_UNDO_HISTORY 10 +#define GRAPHENE_MAX_UNDO_HISTORY 1000 #define GRAPHENE_MIN_BLOCK_SIZE_LIMIT (GRAPHENE_MIN_TRANSACTION_SIZE_LIMIT*5) // 5 transactions per block #define GRAPHENE_MIN_TRANSACTION_EXPIRATION_LIMIT (GRAPHENE_MAX_BLOCK_INTERVAL * 5) // 5 transactions per block diff --git a/libraries/chain/include/graphene/chain/database.hpp b/libraries/chain/include/graphene/chain/database.hpp index 0500f834..13d4a8ff 100644 --- a/libraries/chain/include/graphene/chain/database.hpp +++ b/libraries/chain/include/graphene/chain/database.hpp @@ -90,7 +90,8 @@ namespace graphene { namespace chain { skip_tapos_check = 1 << 7, ///< used while reindexing -- note this skips expiration check as well skip_authority_check = 1 << 8, ///< used while reindexing -- disables any checking of authority on transactions skip_merkle_check = 1 << 9, ///< used while reindexing - skip_assert_evaluation = 1 << 10 ///< used while reindexing + skip_assert_evaluation = 1 << 10, ///< used while reindexing + skip_undo_history_check = 1 << 11 ///< used while reindexing }; /** diff --git a/libraries/chain/include/graphene/chain/exceptions.hpp b/libraries/chain/include/graphene/chain/exceptions.hpp index b18859a2..76648bef 100644 --- a/libraries/chain/include/graphene/chain/exceptions.hpp +++ b/libraries/chain/include/graphene/chain/exceptions.hpp @@ -68,6 +68,7 @@ namespace graphene { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( operation_validate_exception, graphene::chain::chain_exception, 3040000, "operation validation exception" ) FC_DECLARE_DERIVED_EXCEPTION( operation_evaluate_exception, graphene::chain::chain_exception, 3050000, "operation evaluation exception" ) FC_DECLARE_DERIVED_EXCEPTION( utility_exception, graphene::chain::chain_exception, 3060000, "utility method exception" ) + FC_DECLARE_DERIVED_EXCEPTION( undo_database_exception, graphene::chain::chain_exception, 3070000, "undo database exception" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_active_auth, graphene::chain::transaction_exception, 3030001, "missing required active authority" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_owner_auth, graphene::chain::transaction_exception, 3030002, "missing required owner authority" ) diff --git a/libraries/chain/include/graphene/chain/global_property_object.hpp b/libraries/chain/include/graphene/chain/global_property_object.hpp index 4b83b993..1ba5dc07 100644 --- a/libraries/chain/include/graphene/chain/global_property_object.hpp +++ b/libraries/chain/include/graphene/chain/global_property_object.hpp @@ -77,7 +77,15 @@ namespace graphene { namespace chain { time_point_sec next_maintenance_time; time_point_sec last_budget_time; share_type witness_budget; - uint32_t accounts_registered_this_interval; + 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 + * + * If the recently_missed_count hits 2*UNDO_HISTORY then no ew blocks may be pushed. + */ + uint32_t recently_missed_count = 0; + /** if the interval changes then how we calculate witness participation will * also change. Normally witness participation is defined as % of blocks * produced in the last round which is calculated by dividing the delta @@ -97,6 +105,7 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene:: (next_maintenance_time) (witness_budget) (accounts_registered_this_interval) + (recently_missed_count) (first_maintenance_block_with_current_interval) ) diff --git a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp index 55a068c0..3baca598 100644 --- a/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp +++ b/libraries/chain/include/graphene/chain/protocol/chain_parameters.hpp @@ -39,7 +39,7 @@ namespace graphene { namespace chain { uint32_t committee_proposal_review_period = GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC; ///< minimum time in seconds that a proposed transaction requiring committee authority may not be signed, prior to expiration uint32_t maximum_transaction_size = GRAPHENE_DEFAULT_MAX_TRANSACTION_SIZE; ///< maximum allowable size in bytes for a transaction uint32_t maximum_block_size = GRAPHENE_DEFAULT_MAX_BLOCK_SIZE; ///< maximum allowable size in bytes for a block - uint32_t maximum_undo_history = GRAPHENE_DEFAULT_MAX_UNDO_HISTORY; ///< maximum number of undo states to keep in RAM + uint32_t maximum_expiration = GRAPHENE_DEFAULT_MAX_EXPIRATION_SEC; ///< maximum number of seconds in the future a transaction may expire uint32_t maximum_time_until_expiration = GRAPHENE_DEFAULT_MAX_TIME_UNTIL_EXPIRATION; ///< maximum lifetime in seconds for transactions to be valid, before expiring uint32_t maximum_proposal_lifetime = GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC; ///< maximum lifetime in seconds for proposed transactions to be kept, before expiring uint8_t maximum_asset_whitelist_authorities = GRAPHENE_DEFAULT_MAX_ASSET_WHITELIST_AUTHORITIES; ///< maximum number of accounts which an asset may list as authorities for its whitelist OR blacklist @@ -102,7 +102,7 @@ FC_REFLECT( graphene::chain::chain_parameters, (committee_proposal_review_period) (maximum_transaction_size) (maximum_block_size) - (maximum_undo_history) + (maximum_expiration) (maximum_time_until_expiration) (maximum_proposal_lifetime) (maximum_asset_whitelist_authorities) diff --git a/libraries/db/undo_database.cpp b/libraries/db/undo_database.cpp index 87be9846..9534f683 100644 --- a/libraries/db/undo_database.cpp +++ b/libraries/db/undo_database.cpp @@ -28,7 +28,7 @@ undo_database::session undo_database::start_undo_session() { if( _disabled ) return session(*this); - if( size() == max_size() ) + while( size() > max_size() ) _stack.pop_front(); _stack.emplace_back(); diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 69d557d8..6bccc542 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -296,6 +296,7 @@ signed_block database_fixture::generate_block(uint32_t skip, const fc::ecc::priv { open_database(); + skip |= database::skip_undo_history_check; // skip == ~0 will skip checks specified in database::validation_steps return db.generate_block(db.get_slot_time(miss_blocks + 1), db.get_scheduled_witness(miss_blocks + 1).first,