From 0ffad5a359eb18a21c559570b05d5507f0f31506 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Aug 2017 13:01:45 +0100 Subject: [PATCH 1/3] core: guard against exceptions in handle_incoming_{block,tx} When one happens, cleanup must be called or the incoming tx lock will stay locked --- src/cryptonote_core/cryptonote_core.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index c406dd0b..af0616af 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -585,6 +585,8 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_incoming_txs(const std::list& tx_blobs, std::vector& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) { + TRY_ENTRY(); + struct result { bool res; cryptonote::transaction tx; crypto::hash hash; crypto::hash prefix_hash; bool in_txpool; bool in_blockchain; }; std::vector results(tx_blobs.size()); @@ -638,6 +640,8 @@ namespace cryptonote MDEBUG("tx added: " << results[i].hash); } return ok; + + CATCH_ENTRY_L0("core::handle_incoming_txs()", false); } //----------------------------------------------------------------------------------------------- bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) @@ -1075,6 +1079,8 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) { + TRY_ENTRY(); + // load json & DNS checkpoints every 10min/hour respectively, // and verify them with respect to what blocks we already have CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints."); @@ -1098,6 +1104,8 @@ namespace cryptonote if(update_miner_blocktemplate && bvc.m_added_to_main_chain) update_miner_block_template(); return true; + + CATCH_ENTRY_L0("core::handle_incoming_block()", false); } //----------------------------------------------------------------------------------------------- // Used by the RPC server to check the size of an incoming From cf4aa65316263513b3be9143b0a5e498af2a1ed6 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Aug 2017 15:43:32 +0100 Subject: [PATCH 2/3] Fix blockchain_import wedge on exception in cleanup_handle_incoming_blocks --- src/blockchain_db/lmdb/db_lmdb.cpp | 31 +++++++++++++------ src/blockchain_db/lmdb/db_lmdb.h | 3 ++ .../blockchain_import.cpp | 6 +++- src/cryptonote_core/blockchain.cpp | 17 ++++++++-- src/cryptonote_core/cryptonote_core.cpp | 5 +-- 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 4100d9cc..b6978bdc 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -2604,6 +2604,16 @@ void BlockchainLMDB::batch_commit() memset(&m_wcursors, 0, sizeof(m_wcursors)); } +void BlockchainLMDB::cleanup_batch() +{ + // for destruction of batch transaction + m_write_txn = nullptr; + delete m_write_batch_txn; + m_write_batch_txn = nullptr; + m_batch_active = false; + memset(&m_wcursors, 0, sizeof(m_wcursors)); +} + void BlockchainLMDB::batch_stop() { LOG_PRINT_L3("BlockchainLMDB::" << __func__); @@ -2618,15 +2628,18 @@ void BlockchainLMDB::batch_stop() check_open(); LOG_PRINT_L3("batch transaction: committing..."); TIME_MEASURE_START(time1); - m_write_txn->commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; - // for destruction of batch transaction - m_write_txn = nullptr; - delete m_write_batch_txn; - m_write_batch_txn = nullptr; - m_batch_active = false; - memset(&m_wcursors, 0, sizeof(m_wcursors)); + try + { + m_write_txn->commit(); + TIME_MEASURE_FINISH(time1); + time_commit1 += time1; + cleanup_batch(); + } + catch (const std::exception &e) + { + cleanup_batch(); + throw; + } LOG_PRINT_L3("batch transaction: end"); } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 3a11ddf0..90274b90 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -368,6 +368,9 @@ private: // migrate from DB version 0 to 1 void migrate_0_1(); + void cleanup_batch(); + +private: MDB_env* m_env; MDB_dbi m_blocks; diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index ded854ca..14f31876 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -208,7 +208,8 @@ int check_flush(cryptonote::core &core, std::list &blocks, } } // each download block - core.cleanup_handle_incoming_blocks(); + if (!core.cleanup_handle_incoming_blocks()) + return 1; blocks.clear(); return 0; @@ -394,7 +395,10 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path blocks.push_back({block, txs}); int ret = check_flush(core, blocks, false); if (ret) + { + quit = 2; // make sure we don't commit partial block data break; + } } else { diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c1faa703..93a4e26f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -3582,12 +3582,23 @@ void Blockchain::block_longhash_worker(uint64_t height, const std::vector //------------------------------------------------------------------ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) { + bool success = false; + MTRACE("Blockchain::" << __func__); CRITICAL_REGION_BEGIN(m_blockchain_lock); TIME_MEASURE_START(t1); - m_db->batch_stop(); - if (m_sync_counter > 0) + try + { + m_db->batch_stop(); + success = true; + } + catch (const std::exception &e) + { + MERROR("Exception in cleanup_handle_incoming_blocks: " << e.what()); + } + + if (success && m_sync_counter > 0) { if (force_sync) { @@ -3622,7 +3633,7 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync) CRITICAL_REGION_END(); m_tx_pool.unlock(); - return true; + return success; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index af0616af..ac157906 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1068,12 +1068,13 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::cleanup_handle_incoming_blocks(bool force_sync) { + bool success = false; try { - m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync); + success = m_blockchain_storage.cleanup_handle_incoming_blocks(force_sync); } catch (...) {} m_incoming_tx_lock.unlock(); - return true; + return success; } //----------------------------------------------------------------------------------------------- From a3662baefb3ce33867fcb798cfae5e84f7d9af20 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 29 Aug 2017 16:10:53 +0100 Subject: [PATCH 3/3] cryptonote_protocol: error handling on cleanup_handle_incoming_blocks --- .../cryptonote_protocol_handler.inl | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index e762cf9c..c14e0e89 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -364,7 +364,12 @@ namespace cryptonote block_verification_context bvc = boost::value_initialized(); m_core.handle_incoming_block(arg.b.block, bvc); // got block from handle_notify_new_block - m_core.cleanup_handle_incoming_blocks(true); + if (!m_core.cleanup_handle_incoming_blocks(true)) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + m_core.resume_mine(); + return 1; + } m_core.resume_mine(); if(bvc.m_verifivation_failed) { @@ -623,7 +628,12 @@ namespace cryptonote block_verification_context bvc = boost::value_initialized(); m_core.handle_incoming_block(arg.b.block, bvc); // got block from handle_notify_new_block - m_core.cleanup_handle_incoming_blocks(true); + if (!m_core.cleanup_handle_incoming_blocks(true)) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + m_core.resume_mine(); + return 1; + } m_core.resume_mine(); if( bvc.m_verifivation_failed ) @@ -1055,7 +1065,11 @@ skip: })) LOG_ERROR_CCONTEXT("span connection id not found"); - m_core.cleanup_handle_incoming_blocks(); + if (!m_core.cleanup_handle_incoming_blocks()) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + return 1; + } // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it m_block_queue.remove_spans(span_connection_id, start_height); return 1; @@ -1080,7 +1094,12 @@ skip: })) LOG_ERROR_CCONTEXT("span connection id not found"); - m_core.cleanup_handle_incoming_blocks(); + if (!m_core.cleanup_handle_incoming_blocks()) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + return 1; + } + // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it m_block_queue.remove_spans(span_connection_id, start_height); return 1; @@ -1094,7 +1113,12 @@ skip: })) LOG_ERROR_CCONTEXT("span connection id not found"); - m_core.cleanup_handle_incoming_blocks(); + if (!m_core.cleanup_handle_incoming_blocks()) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + return 1; + } + // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it m_block_queue.remove_spans(span_connection_id, start_height); return 1; @@ -1107,7 +1131,11 @@ skip: MCINFO("sync-info", "Block process time (" << blocks.size() << " blocks, " << num_txs << " txs): " << block_process_time_full + transactions_process_time_full << " (" << transactions_process_time_full << "/" << block_process_time_full << ") ms"); - m_core.cleanup_handle_incoming_blocks(); + if (!m_core.cleanup_handle_incoming_blocks()) + { + LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks"); + return 1; + } m_block_queue.remove_spans(span_connection_id, start_height);