From f414fe04ada5493d529b7de825c093e9497e069b Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 6 Apr 2017 12:02:32 -0500 Subject: [PATCH] Unify db_*.cpp files All the db_*.cpp sources are now merged together in database.cpp again. --- libraries/chain/CMakeLists.txt | 22 +- libraries/chain/database.cpp | 1047 ++++++++++++++++- libraries/chain/db_block.cpp | 595 ---------- libraries/chain/db_debug.cpp | 51 - libraries/chain/db_getter.cpp | 92 -- libraries/chain/db_init.cpp | 140 --- libraries/chain/db_management.cpp | 157 --- libraries/chain/db_producer_schedule.cpp | 80 -- libraries/chain/db_update.cpp | 151 --- .../chain/include/eos/chain/database.hpp | 10 +- 10 files changed, 1046 insertions(+), 1299 deletions(-) delete mode 100644 libraries/chain/db_block.cpp delete mode 100644 libraries/chain/db_debug.cpp delete mode 100644 libraries/chain/db_getter.cpp delete mode 100644 libraries/chain/db_init.cpp delete mode 100644 libraries/chain/db_management.cpp delete mode 100644 libraries/chain/db_producer_schedule.cpp delete mode 100644 libraries/chain/db_update.cpp diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index f5d1c54a673..9a34d8b71e4 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -2,29 +2,9 @@ file(GLOB HEADERS "include/eos/chain/*.hpp") file(GLOB PROTOCOL_HEADERS "include/eos/chain/protocol/*.hpp") -if( EOS_DISABLE_UNITY_BUILD ) - set( EOS_DB_FILES - db_block.cpp - db_debug.cpp - db_getter.cpp - db_init.cpp - db_maint.cpp - db_management.cpp - db_update.cpp - db_producer_schedule.cpp - ) - message( STATUS "Eos database unity build disabled" ) -else( EOS_DISABLE_UNITY_BUILD ) - set( EOS_DB_FILES - database.cpp ) - message( STATUS "Eos database unity build enabled" ) -endif( EOS_DISABLE_UNITY_BUILD ) - ## SORT .cpp by most likely to change / break compile add_library( eos_chain - - # As database takes the longest to compile, start it first - ${EOS_DB_FILES} + database.cpp fork_database.cpp protocol/types.cpp diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 9cf61689ff2..01451401491 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -21,11 +21,1044 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + #include -#include "db_block.cpp" -#include "db_debug.cpp" -#include "db_getter.cpp" -#include "db_init.cpp" -#include "db_management.cpp" -#include "db_update.cpp" -#include "db_producer_schedule.cpp" +#include +#include +#include + +#include + +#include +#include +#include + +namespace eos { namespace chain { + +bool database::is_known_block( const block_id_type& id )const +{ + return _fork_db.is_known_block(id) || _block_id_to_block.contains(id); +} +/** + * Only return true *if* the transaction has not expired or been invalidated. If this + * method is called with a VERY old transaction we will return false, they should + * query things by blocks if they are that old. + */ +bool database::is_known_transaction( const transaction_id_type& id )const +{ + const auto& trx_idx = get_index(); + return trx_idx.find( id ) != trx_idx.end(); +} + +block_id_type database::get_block_id_for_num( uint32_t block_num )const +{ try { + return _block_id_to_block.fetch_block_id( block_num ); +} FC_CAPTURE_AND_RETHROW( (block_num) ) } + +optional database::fetch_block_by_id( const block_id_type& id )const +{ + auto b = _fork_db.fetch_block( id ); + if( !b ) + return _block_id_to_block.fetch_optional(id); + return b->data; +} + +optional database::fetch_block_by_number( uint32_t num )const +{ + auto results = _fork_db.fetch_block_by_number(num); + if( results.size() == 1 ) + return results[0]->data; + else + return _block_id_to_block.fetch_by_number(num); + return optional(); +} + +const signed_transaction& database::get_recent_transaction(const transaction_id_type& trx_id) const +{ + auto& index = get_index(); + auto itr = index.find(trx_id); + FC_ASSERT(itr != index.end()); + return itr->trx; +} + +std::vector database::get_block_ids_on_fork(block_id_type head_of_fork) const +{ + pair branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork); + if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) ) + { + edump( (head_of_fork) + (head_block_id()) + (branches.first.size()) + (branches.second.size()) ); + assert(branches.first.back()->previous_id() == branches.second.back()->previous_id()); + } + std::vector result; + for (const item_ptr& fork_block : branches.second) + result.emplace_back(fork_block->id); + result.emplace_back(branches.first.back()->previous_id()); + return result; +} + +/** + * Push block "may fail" in which case every partial change is unwound. After + * push block is successful the block is appended to the chain database on disk. + * + * @return true if we switched forks as a result of this push. + */ +bool database::push_block(const signed_block& new_block, uint32_t skip) +{ +// idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous)); + bool result; + detail::with_skip_flags( *this, skip, [&]() + { + detail::without_pending_transactions( *this, std::move(_pending_tx), + [&]() + { + result = _push_block(new_block); + }); + }); + return result; +} + +bool database::_push_block(const signed_block& new_block) +{ try { + uint32_t skip = get_node_properties().skip_flags; + if( !(skip&skip_fork_db) ) + { + /// TODO: if the block is greater than the head block and before the next maitenance interval + // verify that the block signer is in the current set of active producers. + + shared_ptr new_head = _fork_db.push_block(new_block); + //If the head block from the longest chain does not build off of the current head, we need to switch forks. + if( new_head->data.previous != head_block_id() ) + { + //If the newly pushed block is the same height as head, we get head back in new_head + //Only switch forks if new_head is actually higher than head + if( new_head->data.block_num() > head_block_num() ) + { + wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) ); + auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id()); + + // pop blocks until we hit the forked block + while( head_block_id() != branches.second.back()->data.previous ) + pop_block(); + + // push all blocks on the new fork + for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) + { + ilog( "pushing blocks from fork ${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->data.id()) ); + optional except; + try { + auto session = start_undo_session(true); + apply_block( (*ritr)->data, skip ); + _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); + session.push(); + } + catch ( const fc::exception& e ) { except = e; } + if( except ) + { + wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) ); + // remove the rest of branches.first from the fork_db, those blocks are invalid + while( ritr != branches.first.rend() ) + { + _fork_db.remove( (*ritr)->data.id() ); + ++ritr; + } + _fork_db.set_head( branches.second.front() ); + + // pop all blocks from the bad fork + while( head_block_id() != branches.second.back()->data.previous ) + pop_block(); + + // restore all blocks from the good fork + for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) + { + auto session = start_undo_session(true); + apply_block( (*ritr)->data, skip ); + _block_id_to_block.store( new_block.id(), (*ritr)->data ); + session.push(); + } + throw *except; + } + } + return true; + } + else return false; + } + } + + try { + auto session = start_undo_session(true); + apply_block(new_block, skip); + _block_id_to_block.store(new_block.id(), new_block); + session.push(); + } catch ( const fc::exception& e ) { + elog("Failed to push new block:\n${e}", ("e", e.to_detail_string())); + _fork_db.remove(new_block.id()); + throw; + } + + return false; +} FC_CAPTURE_AND_RETHROW( (new_block) ) } + +/** + * Attempts to push the transaction into the pending queue + * + * When called to push a locally generated transaction, set the skip_block_size_check bit on the skip argument. This + * will allow the transaction to be pushed even if it causes the pending block size to exceed the maximum block size. + * Although the transaction will probably not propagate further now, as the peers are likely to have their pending + * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending + * queues. + */ +signed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) +{ try { + signed_transaction result; + detail::with_skip_flags( *this, skip, [&]() + { + result = _push_transaction( trx ); + } ); + return result; +} FC_CAPTURE_AND_RETHROW( (trx) ) } + +signed_transaction database::_push_transaction( const signed_transaction& trx ) +{ + auto temp_session = start_undo_session(true); + auto processed_trx = _apply_transaction( trx ); + _pending_tx.push_back(processed_trx); + + // notify_changed_objects(); + // The transaction applied successfully. Merge its changes into the pending block session. + temp_session.squash(); + + // notify anyone listening to pending transactions + on_pending_transaction( trx ); + return processed_trx; +} + +signed_transaction database::validate_transaction( const signed_transaction& trx ) +{ + auto session = start_undo_session(true); + return _apply_transaction( trx ); +} + +signed_block database::generate_block( + fc::time_point_sec when, + producer_id_type producer_id, + const fc::ecc::private_key& block_signing_private_key, + uint32_t skip /* = 0 */ + ) +{ try { + signed_block result; + detail::with_skip_flags( *this, skip, [&]() + { + result = _generate_block( when, producer_id, block_signing_private_key ); + } ); + return result; +} FC_CAPTURE_AND_RETHROW() } + +signed_block database::_generate_block( + fc::time_point_sec when, + producer_id_type producer_id, + const fc::ecc::private_key& block_signing_private_key + ) +{ + try { + uint32_t skip = get_node_properties().skip_flags; + uint32_t slot_num = get_slot_at_time( when ); + FC_ASSERT( slot_num > 0 ); + producer_id_type scheduled_producer = get_scheduled_producer( slot_num ); + FC_ASSERT( scheduled_producer == producer_id ); + + const auto& producer_obj = get(scheduled_producer); + + if( !(skip & skip_producer_signature) ) + FC_ASSERT( producer_obj.signing_key == block_signing_private_key.get_public_key() ); + + static const size_t max_block_header_size = fc::raw::pack_size( signed_block_header() ) + 4; + auto maximum_block_size = get_global_properties().parameters.maximum_block_size; + size_t total_block_size = max_block_header_size; + + signed_block pending_block; + + // + // The following code throws away existing pending_tx_session and + // rebuilds it by re-applying pending transactions. + // + // This rebuild is necessary because pending transactions' validity + // and semantics may have changed since they were received, because + // time-based semantics are evaluated based on the current block + // time. These changes can only be reflected in the database when + // the value of the "when" variable is known, which means we need to + // re-apply pending transactions in this method. + // + _pending_tx_session.reset(); + _pending_tx_session = start_undo_session(true); + + uint64_t postponed_tx_count = 0; + // pop pending state (reset to head block state) + for( const signed_transaction& tx : _pending_tx ) + { + size_t new_total_size = total_block_size + fc::raw::pack_size( tx ); + + // postpone transaction if it would make block too big + if( new_total_size >= maximum_block_size ) + { + postponed_tx_count++; + continue; + } + + try + { + auto temp_session = start_undo_session(true); + signed_transaction ptx = _apply_transaction( tx ); + temp_session.squash(); + + // We have to recompute pack_size(ptx) because it may be different + // than pack_size(tx) (i.e. if one or more results increased + // their size) + total_block_size += fc::raw::pack_size( ptx ); + pending_block.transactions.push_back( ptx ); + } + catch ( const fc::exception& e ) + { + // Do nothing, transaction will not be re-applied + wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) ); + wlog( "The transaction was ${t}", ("t", tx) ); + } + } + if( postponed_tx_count > 0 ) + { + wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) ); + } + + _pending_tx_session.reset(); + + // We have temporarily broken the invariant that + // _pending_tx_session is the result of applying _pending_tx, as + // _pending_tx now consists of the set of postponed transactions. + // However, the push_block() call below will re-create the + // _pending_tx_session. + + pending_block.previous = head_block_id(); + pending_block.timestamp = when; + pending_block.transaction_merkle_root = pending_block.calculate_merkle_root(); + pending_block.producer = producer_id; + + if( !(skip & skip_producer_signature) ) + pending_block.sign( block_signing_private_key ); + + // TODO: Move this to _push_block() so session is restored. + if( !(skip & skip_block_size_check) ) + { + FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size ); + } + + push_block( pending_block, skip ); + + return pending_block; +} FC_CAPTURE_AND_RETHROW( (producer_id) ) } + +/** + * Removes the most recent block from the database and + * undoes any changes it made. + */ +void database::pop_block() +{ try { + _pending_tx_session.reset(); + auto head_id = head_block_id(); + optional head_block = fetch_block_by_id( head_id ); + EOS_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" ); + + _fork_db.pop_block(); + _block_id_to_block.remove( head_id ); + undo(); + + _popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() ); + +} FC_CAPTURE_AND_RETHROW() } + +void database::clear_pending() +{ try { + assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() ); + _pending_tx.clear(); + _pending_tx_session.reset(); +} FC_CAPTURE_AND_RETHROW() } + +uint32_t database::push_applied_operation( const operation& op ) +{ + _applied_ops.emplace_back(op); + operation_history_object& oh = *(_applied_ops.back()); + oh.block_num = _current_block_num; + oh.trx_in_block = _current_trx_in_block; + oh.op_in_trx = _current_op_in_trx; + oh.virtual_op = _current_virtual_op++; + return _applied_ops.size() - 1; +} + +//////////////////// private methods //////////////////// + +void database::apply_block( const signed_block& next_block, uint32_t skip ) +{ + auto block_num = next_block.block_num(); + if( _checkpoints.size() && _checkpoints.rbegin()->second != block_id_type() ) + { + auto itr = _checkpoints.find( block_num ); + if( itr != _checkpoints.end() ) + FC_ASSERT( next_block.id() == itr->second, "Block did not match checkpoint", ("checkpoint",*itr)("block_id",next_block.id()) ); + + if( _checkpoints.rbegin()->first >= block_num ) + skip = ~0;// WE CAN SKIP ALMOST EVERYTHING + } + + detail::with_skip_flags( *this, skip, [&]() + { + _apply_block( next_block ); + } ); + return; +} + +void database::_apply_block( const signed_block& next_block ) +{ try { + uint32_t next_block_num = next_block.block_num(); + uint32_t skip = get_node_properties().skip_flags; + _applied_ops.clear(); + + FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) ); + + const producer_object& signing_producer = validate_block_header(skip, next_block); + const auto& global_props = get_global_properties(); + + _current_block_num = next_block_num; + _current_trx_in_block = 0; + + for( const auto& trx : next_block.transactions ) + { + /* We do not need to push the undo state for each transaction + * because they either all apply and are valid or the + * entire block fails to apply. We only need an "undo" state + * for transactions when validating broadcast transactions or + * when building a block. + */ + apply_transaction( trx, skip | skip_transaction_signatures ); + ++_current_trx_in_block; + } + + update_global_dynamic_data(next_block); + update_signing_producer(signing_producer, next_block); + update_last_irreversible_block(); + + create_block_summary(next_block); + clear_expired_transactions(); + + // TODO: figure out if we could collapse this function into + // update_global_dynamic_data() as perhaps these methods only need + // to be called for header validation? + update_producer_schedule(); + if( !_node_property_object.debug_updates.empty() ) + apply_debug_updates(); + + // notify observers that the block has been applied + applied_block( next_block ); //emit + _applied_ops.clear(); + +} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) } + +signed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip) +{ + signed_transaction result; + detail::with_skip_flags( *this, skip, [&]() + { + result = _apply_transaction(trx); + }); + return result; +} + +signed_transaction database::_apply_transaction(const signed_transaction& trx) +{ try { + uint32_t skip = get_node_properties().skip_flags; + + if( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */ + trx.validate(); + + auto& trx_idx = get_mutable_index(); + const chain_id_type& chain_id = get_chain_id(); + auto trx_id = trx.id(); + FC_ASSERT( (skip & skip_transaction_dupe_check) || + trx_idx.indices().get().find(trx_id) == trx_idx.indices().get().end() ); + transaction_evaluation_state eval_state(this); + const chain_parameters& chain_parameters = get_global_properties().parameters; + eval_state._trx = &trx; + + //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is + //expired, and TaPoS makes no sense as no blocks exist. + if( BOOST_LIKELY(head_block_num() > 0) ) + { + if( !(skip & skip_tapos_check) ) + { + const auto& tapos_block_summary = get( trx.ref_block_num ); + + //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration + FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1] ); + } + + fc::time_point_sec now = head_block_time(); + + FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "", + ("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration)); + FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) ); + } + + //Insert transaction into unique transactions database. + if( !(skip & skip_transaction_dupe_check) ) + { + create([&](transaction_object& transaction) { + transaction.trx_id = trx_id; + transaction.trx = trx; + }); + } + + //Finally process the operations + signed_transaction ptrx(trx); + _current_op_in_trx = 0; + for( const auto& op : ptrx.operations ) + { + apply_operation(eval_state, op); + ++_current_op_in_trx; + } + + return ptrx; +} FC_CAPTURE_AND_RETHROW( (trx) ) } + +void database::apply_operation(transaction_evaluation_state& eval_state, const operation& op) +{ try { + int i_which = op.which(); + uint64_t u_which = uint64_t( i_which ); + if( i_which < 0 ) + assert( "Negative operation tag" && false ); + if( u_which >= _operation_evaluators.size() ) + assert( "No registered evaluator for this operation" && false ); + unique_ptr& eval = _operation_evaluators[ u_which ]; + if( !eval ) + assert( "No registered evaluator for this operation" && false ); + auto op_id = push_applied_operation( op ); + eval->evaluate( eval_state, op, true ); +} FC_CAPTURE_AND_RETHROW( (op) ) } + +const producer_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const +{ + FC_ASSERT( head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous) ); + FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()) ); + const producer_object& producer = get(get_scheduled_producer(get_slot_at_time(next_block.timestamp))); + + if( !(skip&skip_producer_signature) ) + FC_ASSERT( next_block.validate_signee( producer.signing_key ), + "Incorrect block producer key: expected ${e} but got ${a}", + ("e", producer.signing_key)("a", public_key_type(next_block.signee()))); + + if( !(skip&skip_producer_schedule_check) ) + { + uint32_t slot_num = get_slot_at_time( next_block.timestamp ); + FC_ASSERT( slot_num > 0 ); + + producer_id_type scheduled_producer = get_scheduled_producer( slot_num ); + + FC_ASSERT( next_block.producer == scheduled_producer, "Producer produced block at wrong time", + ("block producer",next_block.producer)("scheduled",scheduled_producer)("slot_num",slot_num) ); + } + + return producer; +} + +void database::create_block_summary(const signed_block& next_block) +{ + //TODO: Figure out how to do this +// block_summary_id_type sid(next_block.block_num() & 0xffff ); +// modify( sid(*this), [&](block_summary_object& p) { +// p.block_id = next_block.id(); +// }); +} + +void database::add_checkpoints( const flat_map& checkpts ) +{ + for( const auto& i : checkpts ) + _checkpoints[i.first] = i.second; +} + +bool database::before_last_checkpoint()const +{ + return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num()); +} + +/** + * This method dumps the state of the blockchain in a semi-human readable form for the + * purpose of tracking down funds and mismatches in currency allocation + */ +void database::debug_dump() +{ +} + +void debug_apply_update( database& db, const fc::variant_object& vo ) +{ +} + +void database::apply_debug_updates() +{ +} + +void database::debug_update( const fc::variant_object& update ) +{ +} + +const global_property_object& database::get_global_properties()const +{ + return get(); +} + +const dynamic_global_property_object&database::get_dynamic_global_properties() const +{ + return get(); +} + +time_point_sec database::head_block_time()const +{ + return get_dynamic_global_properties().time; +} + +uint32_t database::head_block_num()const +{ + return get_dynamic_global_properties().head_block_number; +} + +block_id_type database::head_block_id()const +{ + return get_dynamic_global_properties().head_block_id; +} + +producer_id_type database::head_block_producer() const +{ + if (auto head_block = fetch_block_by_id(head_block_id())) + return head_block->producer; + return {}; +} + +decltype( chain_parameters::block_interval ) database::block_interval( )const +{ + return get_global_properties().parameters.block_interval; +} + +const chain_id_type& database::get_chain_id( )const +{ + return get().chain_id; +} + +const node_property_object& database::get_node_properties()const +{ + return _node_property_object; +} + +node_property_object& database::node_properties() +{ + return _node_property_object; +} + +uint32_t database::last_non_undoable_block_num() const +{ +#warning TODO: Figure out how to do this + return 1; //head_block_num() - _undo_db.size(); +} + +// C++ requires that static class variables declared and initialized +// in headers must also have a definition in a single source file, +// else linker errors will occur [1]. +// +// The purpose of this source file is to collect such definitions in +// a single place. +// +// [1] http://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char + +void database::initialize_evaluators() +{ + _operation_evaluators.resize(255); + // TODO: Figure out how to do this +} + +void database::initialize_indexes() +{ + add_index(); + add_index(); + add_index(); + add_index(); + add_index(); + add_index(); + add_index(); +} + +void database::init_genesis(const genesis_state_type& genesis_state) +{ try { + FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); + FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % EOS_DEFAULT_BLOCK_INTERVAL == 0, + "Genesis timestamp must be divisible by EOS_DEFAULT_BLOCK_INTERVAL." ); + FC_ASSERT(genesis_state.initial_producer_count >= genesis_state.initial_producers.size(), + "Initial producer count is ${c} but only ${w} producers were defined.", + ("c", genesis_state.initial_producer_count)("w", genesis_state.initial_producers.size())); + + struct auth_inhibitor { + auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) + { db.node_properties().skip_flags |= skip_authority_check; } + ~auth_inhibitor() + { db.node_properties().skip_flags = old_flags; } + private: + database& db; + uint32_t old_flags; + } inhibitor(*this); + + // Create initial accounts + for (const auto& acct : genesis_state.initial_accounts) { + create([&acct](account_object& a) { + a.name = acct.name.c_str(); + a.active_key = acct.active_key; + a.owner_key = acct.owner_key; + }); + } + // Create initial producers + std::vector initialProducers; + for (const auto& producer : genesis_state.initial_producers) { + auto owner = find(producer.owner_name); + FC_ASSERT(owner != nullptr, "Producer belongs to an unknown account: ${acct}", ("acct", producer.owner_name)); + auto id = create([&producer](producer_object& w) { + w.signing_key = producer.block_signing_key; + w.owner_name = producer.owner_name.c_str(); + }).id; + initialProducers.push_back(id); + } + + transaction_evaluation_state genesis_eval_state(this); + + // Initialize block summary index + + chain_id_type chain_id = genesis_state.compute_chain_id(); + + // Create global properties + create([&](global_property_object& p) { + p.parameters = genesis_state.initial_parameters; + p.active_producers = initialProducers; + }); + create([&](dynamic_global_property_object& p) { + p.time = genesis_state.initial_timestamp; + p.dynamic_flags = 0; + p.recent_slots_filled = fc::uint128::max_value(); + }); + + FC_ASSERT( (genesis_state.immutable_parameters.min_producer_count & 1) == 1, "min_producer_count must be odd" ); + + create([&](chain_property_object& p) + { + p.chain_id = chain_id; + p.immutable_parameters = genesis_state.immutable_parameters; + } ); + create([&](block_summary_object&) {}); + + //TODO: Figure out how to do this + // Create initial accounts + // Create initial producers + // Set active producers +} FC_CAPTURE_AND_RETHROW() } + +database::database() +{} + +database::~database() +{ + clear_pending(); +} + +void database::reindex(fc::path data_dir, uint64_t shared_file_size, const genesis_state_type& initial_allocation) +{ try { + ilog( "reindexing blockchain" ); + wipe(data_dir, false); + open(data_dir, shared_file_size, [&initial_allocation]{return initial_allocation;}); + + auto start = fc::time_point::now(); + auto last_block = _block_id_to_block.last(); + if( !last_block ) { + elog( "!no last block" ); + edump((last_block)); + return; + } + + const auto last_block_num = last_block->block_num(); + + ilog( "Replaying blocks..." ); +// _undo_db.disable(); + for( uint32_t i = 1; i <= last_block_num; ++i ) + { + if( i % 5000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "< block = _block_id_to_block.fetch_by_number(i); + if( !block.valid() ) + { + wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) ); + uint32_t dropped_count = 0; + while( true ) + { + fc::optional< block_id_type > last_id = _block_id_to_block.last_id(); + // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1 + if( !last_id.valid() ) + break; + // we've caught up to the gap + if( block_header::num_from_id( *last_id ) <= i ) + break; + _block_id_to_block.remove( *last_id ); + dropped_count++; + } + wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) ); + break; + } + apply_block(*block, skip_producer_signature | + skip_transaction_signatures | + skip_transaction_dupe_check | + skip_tapos_check | + skip_producer_schedule_check | + skip_authority_check); + } +// _undo_db.enable(); + auto end = fc::time_point::now(); + ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) ); +} FC_CAPTURE_AND_RETHROW( (data_dir) ) } + +void database::wipe(const fc::path& data_dir, bool include_blocks) +{ + ilog("Wiping database", ("include_blocks", include_blocks)); + close(); + chainbase::database::wipe(data_dir); + if( include_blocks ) + fc::remove_all( data_dir / "database" ); +} + +void database::open(const fc::path& data_dir, uint64_t shared_file_size, + std::function genesis_loader) +{ + try + { + chainbase::database::open(data_dir, read_write, shared_file_size); + + initialize_indexes(); + initialize_evaluators(); + + _block_id_to_block.open(data_dir / "database" / "block_num_to_block"); + + if( !find() ) + init_genesis(genesis_loader()); + + fc::optional last_block = _block_id_to_block.last(); + if( last_block.valid() ) + { + _fork_db.start_block( *last_block ); + idump((last_block->id())(last_block->block_num())); + idump((head_block_id())(head_block_num())); + if( last_block->id() != head_block_id() ) + { + FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state", + ("last_block->id", last_block->id())("head_block_num",head_block_num()) ); + } + } + } + FC_CAPTURE_LOG_AND_RETHROW( (data_dir) ) +} + +void database::close() +{ + // TODO: Save pending tx's on close() + clear_pending(); + + // Since pop_block() will move tx's in the popped blocks into pending, + // we have to clear_pending() after we're done popping to get a clean + // DB state (issue #336). + clear_pending(); + + chainbase::database::flush(); + chainbase::database::close(); + + if( _block_id_to_block.is_open() ) + _block_id_to_block.close(); + + _fork_db.reset(); +} + +void database::update_global_dynamic_data( const signed_block& b ) +{ + const dynamic_global_property_object& _dgp = get(); + + uint32_t missed_blocks = get_slot_at_time( b.timestamp ); + assert( missed_blocks != 0 ); + missed_blocks--; + for(uint32_t i = 0; i < missed_blocks; ++i) { + const auto& producer_missed = get(get_scheduled_producer(i+1)); + if(producer_missed.id != b.producer) { + /* + const auto& producer_account = producer_missed.producer_account(*this); + if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) ) + wlog( "Producer ${name} missed block ${n} around ${t}", ("name",producer_account.name)("n",b.block_num())("t",b.timestamp) ); + */ + + modify( producer_missed, [&]( producer_object& w ) { + w.total_missed++; + }); + } + } + + // dynamic global properties updating + modify( _dgp, [&]( dynamic_global_property_object& dgp ){ + if( BOOST_UNLIKELY( b.block_num() == 1 ) ) + dgp.recently_missed_count = 0; + else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() ) + dgp.recently_missed_count = 0; + else if( missed_blocks ) + dgp.recently_missed_count += EOS_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks; + else if( dgp.recently_missed_count > EOS_RECENTLY_MISSED_COUNT_INCREMENT ) + dgp.recently_missed_count -= EOS_RECENTLY_MISSED_COUNT_DECREMENT; + 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_producer = b.producer; + 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) ) + { + EOS_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < EOS_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.", + ("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number) + ("recently_missed",_dgp.recently_missed_count)("max_undo",EOS_MAX_UNDO_HISTORY) ); + } + + _fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 ); +} + +void database::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block) +{ + 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 ); + + modify( signing_producer, [&]( producer_object& _wit ) + { + _wit.last_aslot = new_block_aslot; + _wit.last_confirmed_block_num = new_block.block_num(); + } ); +} + +void database::update_last_irreversible_block() +{ + const global_property_object& gpo = get_global_properties(); + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + + vector< const producer_object* > wit_objs; + wit_objs.reserve( gpo.active_producers.size() ); + for( const producer_id_type& wid : gpo.active_producers ) + wit_objs.push_back( &(get(wid)) ); + + static_assert( EOS_IRREVERSIBLE_THRESHOLD > 0, "irreversible threshold must be nonzero" ); + + // 1 1 1 2 2 2 2 2 2 2 -> 2 .7*10 = 7 + // 1 1 1 1 1 1 1 2 2 2 -> 1 + // 3 3 3 3 3 3 3 3 3 3 -> 3 + + size_t offset = ((EOS_100_PERCENT - EOS_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / EOS_100_PERCENT); + + std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(), + []( const producer_object* a, const producer_object* b ) + { + return a->last_confirmed_block_num < b->last_confirmed_block_num; + } ); + + uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num; + + if( new_last_irreversible_block_num > dpo.last_irreversible_block_num ) + { + modify( dpo, [&]( dynamic_global_property_object& _dpo ) + { + _dpo.last_irreversible_block_num = new_last_irreversible_block_num; + } ); + } +} + +void database::clear_expired_transactions() +{ try { + //Look for expired transactions in the deduplication list, and remove them. + //Transactions must have expired by at least two forking windows in order to be removed. + auto& transaction_idx = get_mutable_index(); + const auto& dedupe_index = transaction_idx.indices().get(); + while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->trx.expiration) ) + transaction_idx.remove(*dedupe_index.rbegin()); +} FC_CAPTURE_AND_RETHROW() } + +using boost::container::flat_set; + +producer_id_type database::get_scheduled_producer(uint32_t slot_num)const +{ + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + uint64_t current_aslot = dpo.current_aslot + slot_num; + const auto& gpo = get(); + return gpo.active_producers[ current_aslot % gpo.active_producers.size() ]; +} + +fc::time_point_sec database::get_slot_time(uint32_t slot_num)const +{ + if( slot_num == 0 ) + return fc::time_point_sec(); + + 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; + } + + 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); +} + +uint32_t database::get_slot_at_time(fc::time_point_sec when)const +{ + 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; +} + +uint32_t database::producer_participation_rate()const +{ + const dynamic_global_property_object& dpo = get_dynamic_global_properties(); + return uint64_t(EOS_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128; +} + +void database::update_producer_schedule() +{ +} + +} } diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp deleted file mode 100644 index 759c25e66b5..00000000000 --- a/libraries/chain/db_block.cpp +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace eos { namespace chain { - -bool database::is_known_block( const block_id_type& id )const -{ - return _fork_db.is_known_block(id) || _block_id_to_block.contains(id); -} -/** - * Only return true *if* the transaction has not expired or been invalidated. If this - * method is called with a VERY old transaction we will return false, they should - * query things by blocks if they are that old. - */ -bool database::is_known_transaction( const transaction_id_type& id )const -{ - const auto& trx_idx = get_index(); - return trx_idx.find( id ) != trx_idx.end(); -} - -block_id_type database::get_block_id_for_num( uint32_t block_num )const -{ try { - return _block_id_to_block.fetch_block_id( block_num ); -} FC_CAPTURE_AND_RETHROW( (block_num) ) } - -optional database::fetch_block_by_id( const block_id_type& id )const -{ - auto b = _fork_db.fetch_block( id ); - if( !b ) - return _block_id_to_block.fetch_optional(id); - return b->data; -} - -optional database::fetch_block_by_number( uint32_t num )const -{ - auto results = _fork_db.fetch_block_by_number(num); - if( results.size() == 1 ) - return results[0]->data; - else - return _block_id_to_block.fetch_by_number(num); - return optional(); -} - -const signed_transaction& database::get_recent_transaction(const transaction_id_type& trx_id) const -{ - auto& index = get_index(); - auto itr = index.find(trx_id); - FC_ASSERT(itr != index.end()); - return itr->trx; -} - -std::vector database::get_block_ids_on_fork(block_id_type head_of_fork) const -{ - pair branches = _fork_db.fetch_branch_from(head_block_id(), head_of_fork); - if( !((branches.first.back()->previous_id() == branches.second.back()->previous_id())) ) - { - edump( (head_of_fork) - (head_block_id()) - (branches.first.size()) - (branches.second.size()) ); - assert(branches.first.back()->previous_id() == branches.second.back()->previous_id()); - } - std::vector result; - for (const item_ptr& fork_block : branches.second) - result.emplace_back(fork_block->id); - result.emplace_back(branches.first.back()->previous_id()); - return result; -} - -/** - * Push block "may fail" in which case every partial change is unwound. After - * push block is successful the block is appended to the chain database on disk. - * - * @return true if we switched forks as a result of this push. - */ -bool database::push_block(const signed_block& new_block, uint32_t skip) -{ -// idump((new_block.block_num())(new_block.id())(new_block.timestamp)(new_block.previous)); - bool result; - detail::with_skip_flags( *this, skip, [&]() - { - detail::without_pending_transactions( *this, std::move(_pending_tx), - [&]() - { - result = _push_block(new_block); - }); - }); - return result; -} - -bool database::_push_block(const signed_block& new_block) -{ try { - uint32_t skip = get_node_properties().skip_flags; - if( !(skip&skip_fork_db) ) - { - /// TODO: if the block is greater than the head block and before the next maitenance interval - // verify that the block signer is in the current set of active producers. - - shared_ptr new_head = _fork_db.push_block(new_block); - //If the head block from the longest chain does not build off of the current head, we need to switch forks. - if( new_head->data.previous != head_block_id() ) - { - //If the newly pushed block is the same height as head, we get head back in new_head - //Only switch forks if new_head is actually higher than head - if( new_head->data.block_num() > head_block_num() ) - { - wlog( "Switching to fork: ${id}", ("id",new_head->data.id()) ); - auto branches = _fork_db.fetch_branch_from(new_head->data.id(), head_block_id()); - - // pop blocks until we hit the forked block - while( head_block_id() != branches.second.back()->data.previous ) - pop_block(); - - // push all blocks on the new fork - for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) - { - ilog( "pushing blocks from fork ${n} ${id}", ("n",(*ritr)->data.block_num())("id",(*ritr)->data.id()) ); - optional except; - try { - auto session = start_undo_session(true); - apply_block( (*ritr)->data, skip ); - _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); - session.push(); - } - catch ( const fc::exception& e ) { except = e; } - if( except ) - { - wlog( "exception thrown while switching forks ${e}", ("e",except->to_detail_string() ) ); - // remove the rest of branches.first from the fork_db, those blocks are invalid - while( ritr != branches.first.rend() ) - { - _fork_db.remove( (*ritr)->data.id() ); - ++ritr; - } - _fork_db.set_head( branches.second.front() ); - - // pop all blocks from the bad fork - while( head_block_id() != branches.second.back()->data.previous ) - pop_block(); - - // restore all blocks from the good fork - for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) - { - auto session = start_undo_session(true); - apply_block( (*ritr)->data, skip ); - _block_id_to_block.store( new_block.id(), (*ritr)->data ); - session.push(); - } - throw *except; - } - } - return true; - } - else return false; - } - } - - try { - auto session = start_undo_session(true); - apply_block(new_block, skip); - _block_id_to_block.store(new_block.id(), new_block); - session.push(); - } catch ( const fc::exception& e ) { - elog("Failed to push new block:\n${e}", ("e", e.to_detail_string())); - _fork_db.remove(new_block.id()); - throw; - } - - return false; -} FC_CAPTURE_AND_RETHROW( (new_block) ) } - -/** - * Attempts to push the transaction into the pending queue - * - * When called to push a locally generated transaction, set the skip_block_size_check bit on the skip argument. This - * will allow the transaction to be pushed even if it causes the pending block size to exceed the maximum block size. - * Although the transaction will probably not propagate further now, as the peers are likely to have their pending - * queues full as well, it will be kept in the queue to be propagated later when a new block flushes out the pending - * queues. - */ -signed_transaction database::push_transaction( const signed_transaction& trx, uint32_t skip ) -{ try { - signed_transaction result; - detail::with_skip_flags( *this, skip, [&]() - { - result = _push_transaction( trx ); - } ); - return result; -} FC_CAPTURE_AND_RETHROW( (trx) ) } - -signed_transaction database::_push_transaction( const signed_transaction& trx ) -{ - auto temp_session = start_undo_session(true); - auto processed_trx = _apply_transaction( trx ); - _pending_tx.push_back(processed_trx); - - // notify_changed_objects(); - // The transaction applied successfully. Merge its changes into the pending block session. - temp_session.squash(); - - // notify anyone listening to pending transactions - on_pending_transaction( trx ); - return processed_trx; -} - -signed_transaction database::validate_transaction( const signed_transaction& trx ) -{ - auto session = start_undo_session(true); - return _apply_transaction( trx ); -} - -signed_block database::generate_block( - fc::time_point_sec when, - producer_id_type producer_id, - const fc::ecc::private_key& block_signing_private_key, - uint32_t skip /* = 0 */ - ) -{ try { - signed_block result; - detail::with_skip_flags( *this, skip, [&]() - { - result = _generate_block( when, producer_id, block_signing_private_key ); - } ); - return result; -} FC_CAPTURE_AND_RETHROW() } - -signed_block database::_generate_block( - fc::time_point_sec when, - producer_id_type producer_id, - const fc::ecc::private_key& block_signing_private_key - ) -{ - try { - uint32_t skip = get_node_properties().skip_flags; - uint32_t slot_num = get_slot_at_time( when ); - FC_ASSERT( slot_num > 0 ); - producer_id_type scheduled_producer = get_scheduled_producer( slot_num ); - FC_ASSERT( scheduled_producer == producer_id ); - - const auto& producer_obj = get(scheduled_producer); - - if( !(skip & skip_producer_signature) ) - FC_ASSERT( producer_obj.signing_key == block_signing_private_key.get_public_key() ); - - static const size_t max_block_header_size = fc::raw::pack_size( signed_block_header() ) + 4; - auto maximum_block_size = get_global_properties().parameters.maximum_block_size; - size_t total_block_size = max_block_header_size; - - signed_block pending_block; - - // - // The following code throws away existing pending_tx_session and - // rebuilds it by re-applying pending transactions. - // - // This rebuild is necessary because pending transactions' validity - // and semantics may have changed since they were received, because - // time-based semantics are evaluated based on the current block - // time. These changes can only be reflected in the database when - // the value of the "when" variable is known, which means we need to - // re-apply pending transactions in this method. - // - _pending_tx_session.reset(); - _pending_tx_session = start_undo_session(true); - - uint64_t postponed_tx_count = 0; - // pop pending state (reset to head block state) - for( const signed_transaction& tx : _pending_tx ) - { - size_t new_total_size = total_block_size + fc::raw::pack_size( tx ); - - // postpone transaction if it would make block too big - if( new_total_size >= maximum_block_size ) - { - postponed_tx_count++; - continue; - } - - try - { - auto temp_session = start_undo_session(true); - signed_transaction ptx = _apply_transaction( tx ); - temp_session.squash(); - - // We have to recompute pack_size(ptx) because it may be different - // than pack_size(tx) (i.e. if one or more results increased - // their size) - total_block_size += fc::raw::pack_size( ptx ); - pending_block.transactions.push_back( ptx ); - } - catch ( const fc::exception& e ) - { - // Do nothing, transaction will not be re-applied - wlog( "Transaction was not processed while generating block due to ${e}", ("e", e) ); - wlog( "The transaction was ${t}", ("t", tx) ); - } - } - if( postponed_tx_count > 0 ) - { - wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) ); - } - - _pending_tx_session.reset(); - - // We have temporarily broken the invariant that - // _pending_tx_session is the result of applying _pending_tx, as - // _pending_tx now consists of the set of postponed transactions. - // However, the push_block() call below will re-create the - // _pending_tx_session. - - pending_block.previous = head_block_id(); - pending_block.timestamp = when; - pending_block.transaction_merkle_root = pending_block.calculate_merkle_root(); - pending_block.producer = producer_id; - - if( !(skip & skip_producer_signature) ) - pending_block.sign( block_signing_private_key ); - - // TODO: Move this to _push_block() so session is restored. - if( !(skip & skip_block_size_check) ) - { - FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size ); - } - - push_block( pending_block, skip ); - - return pending_block; -} FC_CAPTURE_AND_RETHROW( (producer_id) ) } - -/** - * Removes the most recent block from the database and - * undoes any changes it made. - */ -void database::pop_block() -{ try { - _pending_tx_session.reset(); - auto head_id = head_block_id(); - optional head_block = fetch_block_by_id( head_id ); - EOS_ASSERT( head_block.valid(), pop_empty_chain, "there are no blocks to pop" ); - - _fork_db.pop_block(); - _block_id_to_block.remove( head_id ); - undo(); - - _popped_tx.insert( _popped_tx.begin(), head_block->transactions.begin(), head_block->transactions.end() ); - -} FC_CAPTURE_AND_RETHROW() } - -void database::clear_pending() -{ try { - assert( (_pending_tx.size() == 0) || _pending_tx_session.valid() ); - _pending_tx.clear(); - _pending_tx_session.reset(); -} FC_CAPTURE_AND_RETHROW() } - -uint32_t database::push_applied_operation( const operation& op ) -{ - _applied_ops.emplace_back(op); - operation_history_object& oh = *(_applied_ops.back()); - oh.block_num = _current_block_num; - oh.trx_in_block = _current_trx_in_block; - oh.op_in_trx = _current_op_in_trx; - oh.virtual_op = _current_virtual_op++; - return _applied_ops.size() - 1; -} - -//////////////////// private methods //////////////////// - -void database::apply_block( const signed_block& next_block, uint32_t skip ) -{ - auto block_num = next_block.block_num(); - if( _checkpoints.size() && _checkpoints.rbegin()->second != block_id_type() ) - { - auto itr = _checkpoints.find( block_num ); - if( itr != _checkpoints.end() ) - FC_ASSERT( next_block.id() == itr->second, "Block did not match checkpoint", ("checkpoint",*itr)("block_id",next_block.id()) ); - - if( _checkpoints.rbegin()->first >= block_num ) - skip = ~0;// WE CAN SKIP ALMOST EVERYTHING - } - - detail::with_skip_flags( *this, skip, [&]() - { - _apply_block( next_block ); - } ); - return; -} - -void database::_apply_block( const signed_block& next_block ) -{ try { - uint32_t next_block_num = next_block.block_num(); - uint32_t skip = get_node_properties().skip_flags; - _applied_ops.clear(); - - FC_ASSERT( (skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root",next_block.transaction_merkle_root)("calc",next_block.calculate_merkle_root())("next_block",next_block)("id",next_block.id()) ); - - const producer_object& signing_producer = validate_block_header(skip, next_block); - const auto& global_props = get_global_properties(); - - _current_block_num = next_block_num; - _current_trx_in_block = 0; - - for( const auto& trx : next_block.transactions ) - { - /* We do not need to push the undo state for each transaction - * because they either all apply and are valid or the - * entire block fails to apply. We only need an "undo" state - * for transactions when validating broadcast transactions or - * when building a block. - */ - apply_transaction( trx, skip | skip_transaction_signatures ); - ++_current_trx_in_block; - } - - update_global_dynamic_data(next_block); - update_signing_producer(signing_producer, next_block); - update_last_irreversible_block(); - - create_block_summary(next_block); - clear_expired_transactions(); - - // TODO: figure out if we could collapse this function into - // update_global_dynamic_data() as perhaps these methods only need - // to be called for header validation? - update_producer_schedule(); - if( !_node_property_object.debug_updates.empty() ) - apply_debug_updates(); - - // notify observers that the block has been applied - applied_block( next_block ); //emit - _applied_ops.clear(); - -} FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) } - -signed_transaction database::apply_transaction(const signed_transaction& trx, uint32_t skip) -{ - signed_transaction result; - detail::with_skip_flags( *this, skip, [&]() - { - result = _apply_transaction(trx); - }); - return result; -} - -signed_transaction database::_apply_transaction(const signed_transaction& trx) -{ try { - uint32_t skip = get_node_properties().skip_flags; - - if( true || !(skip&skip_validate) ) /* issue #505 explains why this skip_flag is disabled */ - trx.validate(); - - auto& trx_idx = get_mutable_index(); - const chain_id_type& chain_id = get_chain_id(); - auto trx_id = trx.id(); - FC_ASSERT( (skip & skip_transaction_dupe_check) || - trx_idx.indices().get().find(trx_id) == trx_idx.indices().get().end() ); - transaction_evaluation_state eval_state(this); - const chain_parameters& chain_parameters = get_global_properties().parameters; - eval_state._trx = &trx; - - //Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is - //expired, and TaPoS makes no sense as no blocks exist. - if( BOOST_LIKELY(head_block_num() > 0) ) - { - if( !(skip & skip_tapos_check) ) - { - const auto& tapos_block_summary = get( trx.ref_block_num ); - - //Verify TaPoS block summary has correct ID prefix, and that this block's time is not past the expiration - FC_ASSERT( trx.ref_block_prefix == tapos_block_summary.block_id._hash[1] ); - } - - fc::time_point_sec now = head_block_time(); - - FC_ASSERT( trx.expiration <= now + chain_parameters.maximum_time_until_expiration, "", - ("trx.expiration",trx.expiration)("now",now)("max_til_exp",chain_parameters.maximum_time_until_expiration)); - FC_ASSERT( now <= trx.expiration, "", ("now",now)("trx.exp",trx.expiration) ); - } - - //Insert transaction into unique transactions database. - if( !(skip & skip_transaction_dupe_check) ) - { - create([&](transaction_object& transaction) { - transaction.trx_id = trx_id; - transaction.trx = trx; - }); - } - - //Finally process the operations - signed_transaction ptrx(trx); - _current_op_in_trx = 0; - for( const auto& op : ptrx.operations ) - { - apply_operation(eval_state, op); - ++_current_op_in_trx; - } - - return ptrx; -} FC_CAPTURE_AND_RETHROW( (trx) ) } - -void database::apply_operation(transaction_evaluation_state& eval_state, const operation& op) -{ try { - int i_which = op.which(); - uint64_t u_which = uint64_t( i_which ); - if( i_which < 0 ) - assert( "Negative operation tag" && false ); - if( u_which >= _operation_evaluators.size() ) - assert( "No registered evaluator for this operation" && false ); - unique_ptr& eval = _operation_evaluators[ u_which ]; - if( !eval ) - assert( "No registered evaluator for this operation" && false ); - auto op_id = push_applied_operation( op ); - eval->evaluate( eval_state, op, true ); -} FC_CAPTURE_AND_RETHROW( (op) ) } - -const producer_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const -{ - FC_ASSERT( head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous) ); - FC_ASSERT( head_block_time() < next_block.timestamp, "", ("head_block_time",head_block_time())("next",next_block.timestamp)("blocknum",next_block.block_num()) ); - const producer_object& producer = get(get_scheduled_producer(get_slot_at_time(next_block.timestamp))); - - if( !(skip&skip_producer_signature) ) - FC_ASSERT( next_block.validate_signee( producer.signing_key ), - "Incorrect block producer key: expected ${e} but got ${a}", - ("e", producer.signing_key)("a", public_key_type(next_block.signee()))); - - if( !(skip&skip_producer_schedule_check) ) - { - uint32_t slot_num = get_slot_at_time( next_block.timestamp ); - FC_ASSERT( slot_num > 0 ); - - producer_id_type scheduled_producer = get_scheduled_producer( slot_num ); - - FC_ASSERT( next_block.producer == scheduled_producer, "Producer produced block at wrong time", - ("block producer",next_block.producer)("scheduled",scheduled_producer)("slot_num",slot_num) ); - } - - return producer; -} - -void database::create_block_summary(const signed_block& next_block) -{ - //TODO: Figure out how to do this -// block_summary_id_type sid(next_block.block_num() & 0xffff ); -// modify( sid(*this), [&](block_summary_object& p) { -// p.block_id = next_block.id(); -// }); -} - -void database::add_checkpoints( const flat_map& checkpts ) -{ - for( const auto& i : checkpts ) - _checkpoints[i.first] = i.second; -} - -bool database::before_last_checkpoint()const -{ - return (_checkpoints.size() > 0) && (_checkpoints.rbegin()->first >= head_block_num()); -} - -} } diff --git a/libraries/chain/db_debug.cpp b/libraries/chain/db_debug.cpp deleted file mode 100644 index de5f8d849db..00000000000 --- a/libraries/chain/db_debug.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include - -#include - -namespace eos { namespace chain { - -/** - * This method dumps the state of the blockchain in a semi-human readable form for the - * purpose of tracking down funds and mismatches in currency allocation - */ -void database::debug_dump() -{ -} - -void debug_apply_update( database& db, const fc::variant_object& vo ) -{ -} - -void database::apply_debug_updates() -{ -} - -void database::debug_update( const fc::variant_object& update ) -{ -} - -} } diff --git a/libraries/chain/db_getter.cpp b/libraries/chain/db_getter.cpp deleted file mode 100644 index b96b0998b06..00000000000 --- a/libraries/chain/db_getter.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include - -#include -#include - -#include - -namespace eos { namespace chain { - -const global_property_object& database::get_global_properties()const -{ - return get(); -} - -const dynamic_global_property_object&database::get_dynamic_global_properties() const -{ - return get(); -} - -time_point_sec database::head_block_time()const -{ - return get_dynamic_global_properties().time; -} - -uint32_t database::head_block_num()const -{ - return get_dynamic_global_properties().head_block_number; -} - -block_id_type database::head_block_id()const -{ - return get_dynamic_global_properties().head_block_id; -} - -producer_id_type database::head_block_producer() const -{ - if (auto head_block = fetch_block_by_id(head_block_id())) - return head_block->producer; - return {}; -} - -decltype( chain_parameters::block_interval ) database::block_interval( )const -{ - return get_global_properties().parameters.block_interval; -} - -const chain_id_type& database::get_chain_id( )const -{ - return get().chain_id; -} - -const node_property_object& database::get_node_properties()const -{ - return _node_property_object; -} - -node_property_object& database::node_properties() -{ - return _node_property_object; -} - -uint32_t database::last_non_undoable_block_num() const -{ -#warning TODO: Figure out how to do this - return 1; //head_block_num() - _undo_db.size(); -} - -} } diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp deleted file mode 100644 index 35633c4592f..00000000000 --- a/libraries/chain/db_init.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace eos { namespace chain { - -// C++ requires that static class variables declared and initialized -// in headers must also have a definition in a single source file, -// else linker errors will occur [1]. -// -// The purpose of this source file is to collect such definitions in -// a single place. -// -// [1] http://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char - -void database::initialize_evaluators() -{ - _operation_evaluators.resize(255); - // TODO: Figure out how to do this -} - -void database::initialize_indexes() -{ - add_index(); - add_index(); - add_index(); - add_index(); - add_index(); - add_index(); - add_index(); -} - -void database::init_genesis(const genesis_state_type& genesis_state) -{ try { - FC_ASSERT( genesis_state.initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); - FC_ASSERT( genesis_state.initial_timestamp.sec_since_epoch() % EOS_DEFAULT_BLOCK_INTERVAL == 0, - "Genesis timestamp must be divisible by EOS_DEFAULT_BLOCK_INTERVAL." ); - FC_ASSERT(genesis_state.initial_producer_count >= genesis_state.initial_producers.size(), - "Initial producer count is ${c} but only ${w} producers were defined.", - ("c", genesis_state.initial_producer_count)("w", genesis_state.initial_producers.size())); - - struct auth_inhibitor { - auth_inhibitor(database& db) : db(db), old_flags(db.node_properties().skip_flags) - { db.node_properties().skip_flags |= skip_authority_check; } - ~auth_inhibitor() - { db.node_properties().skip_flags = old_flags; } - private: - database& db; - uint32_t old_flags; - } inhibitor(*this); - - // Create initial accounts - for (const auto& acct : genesis_state.initial_accounts) { - create([&acct](account_object& a) { - a.name = acct.name.c_str(); - a.active_key = acct.active_key; - a.owner_key = acct.owner_key; - }); - } - // Create initial producers - std::vector initialProducers; - for (const auto& producer : genesis_state.initial_producers) { - auto owner = find(producer.owner_name); - FC_ASSERT(owner != nullptr, "Producer belongs to an unknown account: ${acct}", ("acct", producer.owner_name)); - auto id = create([&producer](producer_object& w) { - w.signing_key = producer.block_signing_key; - w.owner_name = producer.owner_name.c_str(); - }).id; - initialProducers.push_back(id); - } - - transaction_evaluation_state genesis_eval_state(this); - - // Initialize block summary index - - chain_id_type chain_id = genesis_state.compute_chain_id(); - - // Create global properties - create([&](global_property_object& p) { - p.parameters = genesis_state.initial_parameters; - p.active_producers = initialProducers; - }); - create([&](dynamic_global_property_object& p) { - p.time = genesis_state.initial_timestamp; - p.dynamic_flags = 0; - p.recent_slots_filled = fc::uint128::max_value(); - }); - - FC_ASSERT( (genesis_state.immutable_parameters.min_producer_count & 1) == 1, "min_producer_count must be odd" ); - - create([&](chain_property_object& p) - { - p.chain_id = chain_id; - p.immutable_parameters = genesis_state.immutable_parameters; - } ); - create([&](block_summary_object&) {}); - - //TODO: Figure out how to do this - // Create initial accounts - // Create initial producers - // Set active producers -} FC_CAPTURE_AND_RETHROW() } - -} } diff --git a/libraries/chain/db_management.cpp b/libraries/chain/db_management.cpp deleted file mode 100644 index bc7f047e9a5..00000000000 --- a/libraries/chain/db_management.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include - -#include - -#include - -#include -#include -#include - -namespace eos { namespace chain { - -database::database() -{} - -database::~database() -{ - clear_pending(); -} - -void database::reindex(fc::path data_dir, uint64_t shared_file_size, const genesis_state_type& initial_allocation) -{ try { - ilog( "reindexing blockchain" ); - wipe(data_dir, false); - open(data_dir, shared_file_size, [&initial_allocation]{return initial_allocation;}); - - auto start = fc::time_point::now(); - auto last_block = _block_id_to_block.last(); - if( !last_block ) { - elog( "!no last block" ); - edump((last_block)); - return; - } - - const auto last_block_num = last_block->block_num(); - - ilog( "Replaying blocks..." ); -// _undo_db.disable(); - for( uint32_t i = 1; i <= last_block_num; ++i ) - { - if( i % 5000 == 0 ) std::cerr << " " << double(i*100)/last_block_num << "% "< block = _block_id_to_block.fetch_by_number(i); - if( !block.valid() ) - { - wlog( "Reindexing terminated due to gap: Block ${i} does not exist!", ("i", i) ); - uint32_t dropped_count = 0; - while( true ) - { - fc::optional< block_id_type > last_id = _block_id_to_block.last_id(); - // this can trigger if we attempt to e.g. read a file that has block #2 but no block #1 - if( !last_id.valid() ) - break; - // we've caught up to the gap - if( block_header::num_from_id( *last_id ) <= i ) - break; - _block_id_to_block.remove( *last_id ); - dropped_count++; - } - wlog( "Dropped ${n} blocks from after the gap", ("n", dropped_count) ); - break; - } - apply_block(*block, skip_producer_signature | - skip_transaction_signatures | - skip_transaction_dupe_check | - skip_tapos_check | - skip_producer_schedule_check | - skip_authority_check); - } -// _undo_db.enable(); - auto end = fc::time_point::now(); - ilog( "Done reindexing, elapsed time: ${t} sec", ("t",double((end-start).count())/1000000.0 ) ); -} FC_CAPTURE_AND_RETHROW( (data_dir) ) } - -void database::wipe(const fc::path& data_dir, bool include_blocks) -{ - ilog("Wiping database", ("include_blocks", include_blocks)); - close(); - chainbase::database::wipe(data_dir); - if( include_blocks ) - fc::remove_all( data_dir / "database" ); -} - -void database::open(const fc::path& data_dir, uint64_t shared_file_size, - std::function genesis_loader) -{ - try - { - chainbase::database::open(data_dir, read_write, shared_file_size); - - initialize_indexes(); - initialize_evaluators(); - - _block_id_to_block.open(data_dir / "database" / "block_num_to_block"); - - if( !find() ) - init_genesis(genesis_loader()); - - fc::optional last_block = _block_id_to_block.last(); - if( last_block.valid() ) - { - _fork_db.start_block( *last_block ); - idump((last_block->id())(last_block->block_num())); - idump((head_block_id())(head_block_num())); - if( last_block->id() != head_block_id() ) - { - FC_ASSERT( head_block_num() == 0, "last block ID does not match current chain state", - ("last_block->id", last_block->id())("head_block_num",head_block_num()) ); - } - } - } - FC_CAPTURE_LOG_AND_RETHROW( (data_dir) ) -} - -void database::close() -{ - // TODO: Save pending tx's on close() - clear_pending(); - - // Since pop_block() will move tx's in the popped blocks into pending, - // we have to clear_pending() after we're done popping to get a clean - // DB state (issue #336). - clear_pending(); - - chainbase::database::flush(); - chainbase::database::close(); - - if( _block_id_to_block.is_open() ) - _block_id_to_block.close(); - - _fork_db.reset(); -} - -} } diff --git a/libraries/chain/db_producer_schedule.cpp b/libraries/chain/db_producer_schedule.cpp deleted file mode 100644 index 292c69b5b50..00000000000 --- a/libraries/chain/db_producer_schedule.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include -#include -#include - -namespace eos { namespace chain { - -using boost::container::flat_set; - -producer_id_type database::get_scheduled_producer(uint32_t slot_num)const -{ - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - uint64_t current_aslot = dpo.current_aslot + slot_num; - const auto& gpo = get(); - return gpo.active_producers[ current_aslot % gpo.active_producers.size() ]; -} - -fc::time_point_sec database::get_slot_time(uint32_t slot_num)const -{ - if( slot_num == 0 ) - return fc::time_point_sec(); - - 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; - } - - 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); -} - -uint32_t database::get_slot_at_time(fc::time_point_sec when)const -{ - 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; -} - -uint32_t database::producer_participation_rate()const -{ - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - return uint64_t(EOS_100_PERCENT) * dpo.recent_slots_filled.popcount() / 128; -} - -void database::update_producer_schedule() -{ -} - -} } diff --git a/libraries/chain/db_update.cpp b/libraries/chain/db_update.cpp deleted file mode 100644 index 93748eaaff6..00000000000 --- a/libraries/chain/db_update.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2015 Cryptonomex, Inc., and contributors. - * - * The MIT License - * - * 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: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * 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. - */ - -#include -#include - -#include -#include -#include - -#include - -namespace eos { namespace chain { - -void database::update_global_dynamic_data( const signed_block& b ) -{ - const dynamic_global_property_object& _dgp = get(); - - uint32_t missed_blocks = get_slot_at_time( b.timestamp ); - assert( missed_blocks != 0 ); - missed_blocks--; - for(uint32_t i = 0; i < missed_blocks; ++i) { - const auto& producer_missed = get(get_scheduled_producer(i+1)); - if(producer_missed.id != b.producer) { - /* - const auto& producer_account = producer_missed.producer_account(*this); - if( (fc::time_point::now() - b.timestamp) < fc::seconds(30) ) - wlog( "Producer ${name} missed block ${n} around ${t}", ("name",producer_account.name)("n",b.block_num())("t",b.timestamp) ); - */ - - modify( producer_missed, [&]( producer_object& w ) { - w.total_missed++; - }); - } - } - - // dynamic global properties updating - modify( _dgp, [&]( dynamic_global_property_object& dgp ){ - if( BOOST_UNLIKELY( b.block_num() == 1 ) ) - dgp.recently_missed_count = 0; - else if( _checkpoints.size() && _checkpoints.rbegin()->first >= b.block_num() ) - dgp.recently_missed_count = 0; - else if( missed_blocks ) - dgp.recently_missed_count += EOS_RECENTLY_MISSED_COUNT_INCREMENT*missed_blocks; - else if( dgp.recently_missed_count > EOS_RECENTLY_MISSED_COUNT_INCREMENT ) - dgp.recently_missed_count -= EOS_RECENTLY_MISSED_COUNT_DECREMENT; - 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_producer = b.producer; - 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) ) - { - EOS_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < EOS_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.", - ("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number) - ("recently_missed",_dgp.recently_missed_count)("max_undo",EOS_MAX_UNDO_HISTORY) ); - } - - _fork_db.set_max_size( _dgp.head_block_number - _dgp.last_irreversible_block_num + 1 ); -} - -void database::update_signing_producer(const producer_object& signing_producer, const signed_block& new_block) -{ - 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 ); - - modify( signing_producer, [&]( producer_object& _wit ) - { - _wit.last_aslot = new_block_aslot; - _wit.last_confirmed_block_num = new_block.block_num(); - } ); -} - -void database::update_last_irreversible_block() -{ - const global_property_object& gpo = get_global_properties(); - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - - vector< const producer_object* > wit_objs; - wit_objs.reserve( gpo.active_producers.size() ); - for( const producer_id_type& wid : gpo.active_producers ) - wit_objs.push_back( &(get(wid)) ); - - static_assert( EOS_IRREVERSIBLE_THRESHOLD > 0, "irreversible threshold must be nonzero" ); - - // 1 1 1 2 2 2 2 2 2 2 -> 2 .7*10 = 7 - // 1 1 1 1 1 1 1 2 2 2 -> 1 - // 3 3 3 3 3 3 3 3 3 3 -> 3 - - size_t offset = ((EOS_100_PERCENT - EOS_IRREVERSIBLE_THRESHOLD) * wit_objs.size() / EOS_100_PERCENT); - - std::nth_element( wit_objs.begin(), wit_objs.begin() + offset, wit_objs.end(), - []( const producer_object* a, const producer_object* b ) - { - return a->last_confirmed_block_num < b->last_confirmed_block_num; - } ); - - uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num; - - if( new_last_irreversible_block_num > dpo.last_irreversible_block_num ) - { - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.last_irreversible_block_num = new_last_irreversible_block_num; - } ); - } -} - -void database::clear_expired_transactions() -{ try { - //Look for expired transactions in the deduplication list, and remove them. - //Transactions must have expired by at least two forking windows in order to be removed. - auto& transaction_idx = get_mutable_index(); - const auto& dedupe_index = transaction_idx.indices().get(); - while( (!dedupe_index.empty()) && (head_block_time() > dedupe_index.rbegin()->trx.expiration) ) - transaction_idx.remove(*dedupe_index.rbegin()); -} FC_CAPTURE_AND_RETHROW() } - -} } diff --git a/libraries/chain/include/eos/chain/database.hpp b/libraries/chain/include/eos/chain/database.hpp index b11ef471389..731cb1cead1 100644 --- a/libraries/chain/include/eos/chain/database.hpp +++ b/libraries/chain/include/eos/chain/database.hpp @@ -59,7 +59,7 @@ namespace eos { namespace chain { enum validation_steps { skip_nothing = 0, - skip_producer_signature = 1 << 0, ///< used while reindexing + skip_producer_signature = 1 << 0, ///< used while reindexing skip_transaction_signatures = 1 << 1, ///< used by non-producer nodes skip_transaction_dupe_check = 1 << 2, ///< used while reindexing skip_fork_db = 1 << 3, ///< used while reindexing @@ -69,7 +69,7 @@ namespace eos { namespace chain { skip_merkle_check = 1 << 7, ///< used while reindexing skip_assert_evaluation = 1 << 8, ///< used while reindexing skip_undo_history_check = 1 << 9, ///< used while reindexing - skip_producer_schedule_check = 1 << 10, ///< used while reindexing + skip_producer_schedule_check= 1 << 10, ///< used while reindexing skip_validate = 1 << 11 ///< used prior to checkpoint, skips validate() call on transaction }; @@ -126,7 +126,7 @@ namespace eos { namespace chain { */ uint32_t producer_participation_rate()const; - void add_checkpoints( const flat_map& checkpts ); + void add_checkpoints(const flat_map& checkpts); const flat_map get_checkpoints()const { return _checkpoints; } bool before_last_checkpoint()const; @@ -271,7 +271,7 @@ namespace eos { namespace chain { signed_transaction apply_transaction( const signed_transaction& trx, uint32_t skip = skip_nothing ); void apply_operation( transaction_evaluation_state& eval_state, const operation& op ); private: - void _apply_block( const signed_block& next_block ); + void _apply_block( const signed_block& next_block ); signed_transaction _apply_transaction( const signed_transaction& trx ); ///Steps involved in applying a new block @@ -288,7 +288,7 @@ namespace eos { namespace chain { void clear_expired_transactions(); vector< signed_transaction > _pending_tx; - fork_database _fork_db; + fork_database _fork_db; /** * Note: we can probably store blocks by block num rather than