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 witness_object& signing_witness = validate_block_header(skip, next_block); const auto& global_props = get_global_properties(); const auto& dynamic_global_props = get<dynamic_global_property_object>(dynamic_global_property_id_type()); bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp); _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 ); ++_current_trx_in_block; } update_global_dynamic_data(next_block); update_signing_witness(signing_witness, next_block); update_last_irreversible_block(); // Are we at the maintenance interval? if( maint_needed ) perform_chain_maintenance(next_block, global_props); create_block_summary(next_block); clear_expired_transactions(); clear_expired_proposals(); clear_expired_orders(); update_expired_feeds(); update_withdraw_permissions(); // n.b., update_maintenance_flag() happens this late // because get_slot_time() / get_slot_at_time() is needed above // 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_maintenance_flag( maint_needed ); update_witness_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(); notify_changed_objects(); } FC_CAPTURE_AND_RETHROW( (next_block.block_num()) ) }
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 witnesses. shared_ptr<fork_item> 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<fc::exception> except; try { undo_database::session session = _undo_db.start_undo_session(); apply_block( (*ritr)->data, skip ); _block_id_to_block.store( (*ritr)->id, (*ritr)->data ); session.commit(); } 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 = _undo_db.start_undo_session(); apply_block( (*ritr)->data, skip ); _block_id_to_block.store( new_block.id(), (*ritr)->data ); session.commit(); } throw *except; } } return true; } else return false; } } try { auto session = _undo_db.start_undo_session(); apply_block(new_block, skip); _block_id_to_block.store(new_block.id(), new_block); session.commit(); } 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) ) }
/** * Pushes the block into the fork database and caches it if it doesn't link * */ shared_ptr<fork_item> fork_database::push_block(const signed_block& b) { auto item = std::make_shared<fork_item>(b); try { _push_block(item); } catch ( const unlinkable_block_exception& e ) { wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) ); wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) ); throw; _unlinked_index.insert( item ); } return _head; }
fork_item( signed_block d ) :num(d.block_num()),id(d.id()),data( std::move(d) ){}
block_message(const signed_block &blk) : block(blk), block_id(blk.id()) { }