From ea5fa5e9b67c49ef7b69d4a244bf25d2ae0e008e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 17 Feb 2016 22:57:14 +0000 Subject: [PATCH 1/5] core: print "update needed" hard fork notifications in red --- src/cryptonote_core/cryptonote_core.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2c485d791..6f0fe88a4 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -943,14 +943,14 @@ namespace cryptonote HardFork::State state = m_blockchain_storage.get_hard_fork_state(); switch (state) { case HardFork::LikelyForked: - LOG_PRINT_L0(ENDL + LOG_PRINT_RED_L0(ENDL << "**********************************************************************" << ENDL << "Last scheduled hard fork is too far in the past." << ENDL << "We are most likely forked from the network. Daemon update needed now." << ENDL << "**********************************************************************" << ENDL); break; case HardFork::UpdateNeeded: - LOG_PRINT_L0(ENDL + LOG_PRINT_RED_L0(ENDL << "**********************************************************************" << ENDL << "Last scheduled hard fork time shows a daemon update is needed now." << ENDL << "**********************************************************************" << ENDL); From 86a7f2b1e709ba9b0ace0a85ec3661b17a26df38 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 17 Feb 2016 22:57:38 +0000 Subject: [PATCH 2/5] core: check whether an update is needed straight away --- src/cryptonote_core/cryptonote_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 3c6f56749..32f0b2ad4 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -198,7 +198,7 @@ namespace cryptonote std::string m_config_folder; cryptonote_protocol_stub m_protocol_stub; epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; - epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; + epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions friend class tx_validate_inputs; std::atomic m_starter_message_showed; From 8cc7a36f0be2f90b62a6ffce840817e046e928a0 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 18 Feb 2016 12:09:57 +0000 Subject: [PATCH 3/5] read txn/cursor stuff Could wrap more later. --- src/blockchain_db/berkeleydb/db_bdb.cpp | 2 +- src/blockchain_db/berkeleydb/db_bdb.h | 2 +- src/blockchain_db/blockchain_db.cpp | 11 +- src/blockchain_db/blockchain_db.h | 2 +- src/blockchain_db/lmdb/db_lmdb.cpp | 395 ++++++++++++++++-------- src/blockchain_db/lmdb/db_lmdb.h | 78 +++-- src/cryptonote_core/blockchain.cpp | 9 + src/cryptonote_core/hardfork.cpp | 6 +- 8 files changed, 339 insertions(+), 166 deletions(-) diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index f035bf4c4..55fd2e31f 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1835,7 +1835,7 @@ void BlockchainBDB::set_batch_transactions(bool batch_transactions) LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled")); } -void BlockchainBDB::block_txn_start() +void BlockchainBDB::block_txn_start(bool readonly) { // TODO } diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h index 42119da93..38bef3934 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.h +++ b/src/blockchain_db/berkeleydb/db_bdb.h @@ -328,7 +328,7 @@ public: virtual void batch_stop(); virtual void batch_abort(); - virtual void block_txn_start(); + virtual void block_txn_start(bool readonly); virtual void block_txn_stop(); virtual void block_txn_abort(); diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 68bef3892..a66f4a403 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -99,7 +99,7 @@ uint64_t BlockchainDB::add_block( const block& blk , const std::vector& txs ) { - block_txn_start(); + block_txn_start(false); TIME_MEASURE_START(time1); crypto::hash blk_hash = get_block_hash(blk); @@ -227,6 +227,9 @@ void BlockchainDB::fixup() static const char * const mainnet_genesis_hex = "418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3"; crypto::hash mainnet_genesis_hash; epee::string_tools::hex_to_pod(mainnet_genesis_hex, mainnet_genesis_hash ); + set_batch_transactions(true); + batch_start(); + if (get_block_hash_from_height(0) == mainnet_genesis_hash) { // block 202612 (511 key images in 511 transactions) @@ -762,9 +765,6 @@ void BlockchainDB::fixup() "633cdedeb3b96ec4f234c670254c6f721e0b368d00b48c6b26759db7d62cf52d", }; - set_batch_transactions(true); - batch_start(); - if (height() > 202612) { for (const auto &kis: key_images_202612) @@ -791,9 +791,8 @@ void BlockchainDB::fixup() } } } - - batch_stop(); } + batch_stop(); } } // namespace cryptonote diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h index 2aa4506e6..3396b8c20 100644 --- a/src/blockchain_db/blockchain_db.h +++ b/src/blockchain_db/blockchain_db.h @@ -381,7 +381,7 @@ public: virtual void batch_stop() = 0; virtual void set_batch_transactions(bool) = 0; - virtual void block_txn_start() = 0; + virtual void block_txn_start(bool readonly=false) = 0; virtual void block_txn_stop() = 0; virtual void block_txn_abort() = 0; diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index b43d5742f..f9459e8f1 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -227,11 +227,34 @@ const std::string lmdb_error(const std::string& error_string, int mdb_res) throw0(DB_ERROR(std::string("Failed to open cursor: ").append(mdb_strerror(result)).c_str())); \ } +#define RCURSOR(name) \ + if (!m_cur_ ## name) { \ + int result = mdb_cursor_open(m_txn, m_ ## name, (MDB_cursor **)&m_cur_ ## name); \ + if (result) \ + throw0(DB_ERROR(std::string("Failed to open cursor: ").append(mdb_strerror(result)).c_str())); \ + if (!m_write_txn) \ + m_tinfo->m_ti_rflags.m_rf_ ## name = true; \ + } else if (!m_write_txn && !m_tinfo->m_ti_rflags.m_rf_ ## name) { \ + mdb_cursor_renew(m_txn, m_cur_ ## name); \ + m_tinfo->m_ti_rflags.m_rf_ ## name = true; \ + } + namespace cryptonote { std::atomic mdb_txn_safe::num_active_txns{0}; std::atomic_flag mdb_txn_safe::creation_gate = ATOMIC_FLAG_INIT; +mdb_threadinfo::~mdb_threadinfo() +{ + MDB_cursor **cur = &m_ti_rcursors.m_txc_blocks; + unsigned i; + for (i=0; i val_h(blk_hash); @@ -637,6 +661,7 @@ void BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const tr { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; int result = 0; @@ -695,6 +720,7 @@ void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_ou { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; int result = 0; @@ -890,6 +916,7 @@ void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) { LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); + mdb_txn_cursors *m_cursors = &m_wcursors; CURSOR(spent_keys) @@ -1201,6 +1228,7 @@ void BlockchainLMDB::close() batch_abort(); } this->sync(); + m_tinfo.reset(); // FIXME: not yet thread safe!!! Use with care. mdb_env_close(m_env); @@ -1303,7 +1331,11 @@ void BlockchainLMDB::unlock() throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \ } \ -#define TXN_PREFIX_RDONLY(); TXN_PREFIX(MDB_RDONLY); +#define TXN_PREFIX_RDONLY() \ + bool my_rtxn = block_rtxn_start(); \ + MDB_txn *m_txn = m_write_txn ? m_write_txn->m_txn : m_tinfo->m_ti_rtxn +#define TXN_POSTFIX_RDONLY() \ + if (my_rtxn) block_rtxn_stop() #define TXN_POSTFIX_SUCCESS() \ do { \ @@ -1343,20 +1375,21 @@ bool BlockchainLMDB::block_exists(const crypto::hash& h) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_heights); MDB_val_copy key(h); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_heights, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_heights, &key, NULL, MDB_SET); if (get_result == MDB_NOTFOUND) { - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); LOG_PRINT_L3("Block with hash " << epee::string_tools::pod_to_hex(h) << " not found in db"); return false; } else if (get_result) throw0(DB_ERROR("DB error attempting to fetch block index from hash")); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return true; } @@ -1374,17 +1407,19 @@ uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_heights); MDB_val_copy key(h); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_heights, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_heights, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(BLOCK_DNE("Attempted to retrieve non-existent block height")); else if (get_result) throw0(DB_ERROR("Error attempting to retrieve a block height from the db")); uint64_t ret = *(const uint64_t *)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1403,10 +1438,12 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(blocks); MDB_val_copy key(height); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_blocks, &key, &result); + auto get_result = mdb_cursor_get(m_cur_blocks, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw0(BLOCK_DNE(std::string("Attempt to get block from height ").append(boost::lexical_cast(height)).append(" failed -- block not in db").c_str())); @@ -1421,7 +1458,7 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const if (!parse_and_validate_block_from_blob(bd, b)) throw0(DB_ERROR("Failed to parse block from blob retrieved from the db")); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return b; } @@ -1432,10 +1469,12 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_timestamps); MDB_val_copy key(height); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_timestamps, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_timestamps, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw0(BLOCK_DNE(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast(height)).append(" failed -- timestamp not in db").c_str())); @@ -1444,7 +1483,7 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const throw0(DB_ERROR("Error attempting to retrieve a timestamp from the db")); uint64_t ret = *(const uint64_t *)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1468,10 +1507,12 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_sizes); MDB_val_copy key(height); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_sizes, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_sizes, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw0(BLOCK_DNE(std::string("Attempt to get block size from height ").append(boost::lexical_cast(height)).append(" failed -- block size not in db").c_str())); @@ -1480,7 +1521,7 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const throw0(DB_ERROR("Error attempting to retrieve a block size from the db")); size_t ret = *(const size_t *)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1490,10 +1531,12 @@ difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_diffs); MDB_val_copy key(height); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_diffs, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_diffs, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw0(BLOCK_DNE(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast(height)).append(" failed -- difficulty not in db").c_str())); @@ -1502,7 +1545,7 @@ difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db")); difficulty_type ret = *(const difficulty_type*)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1529,10 +1572,12 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_coins); MDB_val_copy key(height); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_coins, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_coins, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw0(BLOCK_DNE(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast(height)).append(" failed -- block size not in db").c_str())); @@ -1541,7 +1586,7 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh throw0(DB_ERROR("Error attempting to retrieve a total generated coins from the db")); uint64_t ret = *(const uint64_t*)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1551,10 +1596,12 @@ crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(block_hashes); MDB_val_copy key(height); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_hashes, &key, &result); + auto get_result = mdb_cursor_get(m_cur_block_hashes, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw0(BLOCK_DNE(std::string("Attempt to get hash from height ").append(boost::lexical_cast(height)).append(" failed -- hash not in db").c_str())); @@ -1564,7 +1611,7 @@ crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) append(mdb_strerror(get_result)).c_str())); crypto::hash ret = *(const crypto::hash*)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1636,16 +1683,18 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(txs); MDB_val_copy key(h); MDB_val result; TIME_MEASURE_START(time1); - auto get_result = mdb_get(*txn_ptr, m_txs, &key, &result); + auto get_result = mdb_cursor_get(m_cur_txs, &key, &result, MDB_SET); TIME_MEASURE_FINISH(time1); time_tx_exists += time1; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); if (get_result == MDB_NOTFOUND) { @@ -1664,17 +1713,19 @@ uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(tx_unlocks); MDB_val_copy key(h); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_tx_unlocks, &key, &result); + auto get_result = mdb_cursor_get(m_cur_tx_unlocks, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(TX_DNE(std::string("tx unlock time with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str())); else if (get_result) throw0(DB_ERROR("DB error attempting to fetch tx unlock time from hash")); uint64_t ret = *(const uint64_t*)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1684,10 +1735,12 @@ transaction BlockchainLMDB::get_tx(const crypto::hash& h) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(txs); MDB_val_copy key(h); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_txs, &key, &result); + auto get_result = mdb_cursor_get(m_cur_txs, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str())); else if (get_result) @@ -1700,7 +1753,7 @@ transaction BlockchainLMDB::get_tx(const crypto::hash& h) const if (!parse_and_validate_tx_from_blob(bd, tx)) throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db")); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return tx; } @@ -1713,10 +1766,10 @@ uint64_t BlockchainLMDB::get_tx_count() const TXN_PREFIX_RDONLY(); MDB_stat db_stats; - if (mdb_stat(*txn_ptr, m_txs, &db_stats)) + if (mdb_stat(m_txn, m_txs, &db_stats)) throw0(DB_ERROR("Failed to query m_txs")); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return db_stats.ms_entries; } @@ -1741,10 +1794,12 @@ uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(tx_heights); MDB_val_copy key(h); MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_tx_heights, &key, &result); + auto get_result = mdb_cursor_get(m_cur_tx_heights, &key, &result, MDB_SET); if (get_result == MDB_NOTFOUND) { throw1(TX_DNE(std::string("tx height with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str())); @@ -1753,7 +1808,7 @@ uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const throw0(DB_ERROR("DB error attempting to fetch tx height from hash")); uint64_t ret = *(const uint64_t*)result.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1763,24 +1818,24 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const check_open(); TXN_PREFIX_RDONLY(); - - lmdb_cur cur(*txn_ptr, m_output_amounts); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(output_amounts); MDB_val_copy k(amount); MDB_val v; - auto result = mdb_cursor_get(cur, &k, &v, MDB_SET); + auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET); if (result == MDB_NOTFOUND) { - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return 0; } else if (result) throw0(DB_ERROR("DB error attempting to get number of outputs of an amount")); mdb_size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); + mdb_cursor_count(m_cur_output_amounts, &num_elems); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return num_elems; } @@ -1791,16 +1846,18 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(output_keys); MDB_val_copy k(global_index); MDB_val v; - auto get_result = mdb_get(*txn_ptr, m_output_keys, &k, &v); + auto get_result = mdb_cursor_get(m_cur_output_keys, &k, &v, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(OUTPUT_DNE("Attempting to get output pubkey by global index, but key does not exist")); else if (get_result) throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db")); output_data_t ret = *(const output_data_t *) v.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1819,11 +1876,14 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(output_txs); + RCURSOR(output_indices); MDB_val_copy k(index); MDB_val v; - auto get_result = mdb_get(*txn_ptr, m_output_txs, &k, &v); + auto get_result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(OUTPUT_DNE("output with given index not in db")); else if (get_result) @@ -1831,14 +1891,14 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& crypto::hash tx_hash = *(const crypto::hash*)v.mv_data; - get_result = mdb_get(*txn_ptr, m_output_indices, &k, &v); + get_result = mdb_cursor_get(m_cur_output_indices, &k, &v, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(OUTPUT_DNE("output with given index not in db")); else if (get_result) throw0(DB_ERROR("DB error attempting to fetch output tx index")); tx_out_index ret = tx_out_index(tx_hash, *(const uint64_t *)v.mv_data); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -1862,31 +1922,30 @@ std::vector BlockchainLMDB::get_tx_output_indices(const crypto::hash& std::vector index_vec; TXN_PREFIX_RDONLY(); - - lmdb_cur cur(*txn_ptr, m_tx_outputs); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(tx_outputs); MDB_val_copy k(h); MDB_val v; - auto result = mdb_cursor_get(cur, &k, &v, MDB_SET); + auto result = mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_SET); if (result == MDB_NOTFOUND) throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found")); else if (result) throw0(DB_ERROR("DB error attempting to get an output")); mdb_size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); + mdb_cursor_count(m_cur_tx_outputs, &num_elems); - mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP); + mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_FIRST_DUP); for (uint64_t i = 0; i < num_elems; ++i) { - mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT); + mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_GET_CURRENT); index_vec.push_back(*(const uint64_t *)v.mv_data); - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); + mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_NEXT_DUP); } - cur.close(); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return index_vec; } @@ -1905,6 +1964,8 @@ std::vector BlockchainLMDB::get_tx_amount_output_indices(const crypto: transaction tx = get_tx(h); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(output_amounts); uint64_t i = 0; uint64_t global_index; @@ -1914,28 +1975,26 @@ std::vector BlockchainLMDB::get_tx_amount_output_indices(const crypto: global_index = index_vec[i]; - lmdb_cur cur(*txn_ptr, m_output_amounts); - MDB_val_copy k(amount); MDB_val v; - auto result = mdb_cursor_get(cur, &k, &v, MDB_SET); + auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET); if (result == MDB_NOTFOUND) throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found")); else if (result) throw0(DB_ERROR("DB error attempting to get an output")); mdb_size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); + mdb_cursor_count(m_cur_output_amounts, &num_elems); - mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_FIRST_DUP); uint64_t amount_output_index = 0; uint64_t output_index = 0; bool found_index = false; for (uint64_t j = 0; j < num_elems; ++j) { - mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_CURRENT); output_index = *(const uint64_t *)v.mv_data; if (output_index == global_index) { @@ -1943,7 +2002,7 @@ std::vector BlockchainLMDB::get_tx_amount_output_indices(const crypto: found_index = true; break; } - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP); } if (found_index) { @@ -1952,16 +2011,14 @@ std::vector BlockchainLMDB::get_tx_amount_output_indices(const crypto: else { // not found - cur.close(); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); throw1(OUTPUT_DNE("specified output not found in db")); } - cur.close(); ++i; } - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return index_vec2; } @@ -1974,16 +2031,17 @@ bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(spent_keys); MDB_val_copy val_key(img); - MDB_val unused; - if (mdb_get(*txn_ptr, m_spent_keys, &val_key, &unused) == 0) + if (mdb_cursor_get(m_cur_spent_keys, &val_key, NULL, MDB_SET) == 0) { - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return true; } - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return false; } @@ -1993,16 +2051,17 @@ bool BlockchainLMDB::for_all_key_images(std::functionm_ti_rcursors; + RCURSOR(spent_keys); MDB_val k; MDB_val v; bool ret = true; - lmdb_cur cur(*txn_ptr, m_spent_keys); MDB_cursor_op op = MDB_FIRST; while (1) { - int ret = mdb_cursor_get(cur, &k, &v, op); + int ret = mdb_cursor_get(m_cur_spent_keys, &k, &v, op); op = MDB_NEXT; if (ret == MDB_NOTFOUND) break; @@ -2015,8 +2074,7 @@ bool BlockchainLMDB::for_all_key_images(std::functionm_ti_rcursors; + RCURSOR(blocks); MDB_val k; MDB_val v; bool ret = true; - lmdb_cur cur(*txn_ptr, m_blocks); MDB_cursor_op op = MDB_FIRST; while (1) { - int ret = mdb_cursor_get(cur, &k, &v, op); + int ret = mdb_cursor_get(m_cur_blocks, &k, &v, op); op = MDB_NEXT; if (ret == MDB_NOTFOUND) break; @@ -2057,8 +2116,7 @@ bool BlockchainLMDB::for_all_blocks(std::functionm_ti_rcursors; + RCURSOR(txs); MDB_val k; MDB_val v; bool ret = true; - lmdb_cur cur(*txn_ptr, m_txs); MDB_cursor_op op = MDB_FIRST; while (1) { - int ret = mdb_cursor_get(cur, &k, &v, op); + int ret = mdb_cursor_get(m_cur_txs, &k, &v, op); op = MDB_NEXT; if (ret == MDB_NOTFOUND) break; @@ -2096,8 +2155,7 @@ bool BlockchainLMDB::for_all_transactions(std::functionm_ti_rcursors; + RCURSOR(output_amounts); MDB_val k; MDB_val v; bool ret = true; - lmdb_cur cur(*txn_ptr, m_output_amounts); MDB_cursor_op op = MDB_FIRST; while (1) { - int ret = mdb_cursor_get(cur, &k, &v, op); + int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op); op = MDB_NEXT; if (ret == MDB_NOTFOUND) break; @@ -2132,8 +2191,7 @@ bool BlockchainLMDB::for_all_outputs(std::functionm_batch_txn = true; m_write_txn = m_write_batch_txn; + m_batch_active = true; - memset(&m_cursors, 0, sizeof(m_cursors)); + memset(&m_wcursors, 0, sizeof(m_wcursors)); LOG_PRINT_L3("batch transaction: begin"); } @@ -2193,7 +2252,7 @@ void BlockchainLMDB::batch_commit() m_write_txn = nullptr; delete m_write_batch_txn; - memset(&m_cursors, 0, sizeof(m_cursors)); + memset(&m_wcursors, 0, sizeof(m_wcursors)); } void BlockchainLMDB::batch_stop() @@ -2216,7 +2275,7 @@ void BlockchainLMDB::batch_stop() delete m_write_batch_txn; m_write_batch_txn = nullptr; m_batch_active = false; - memset(&m_cursors, 0, sizeof(m_cursors)); + memset(&m_wcursors, 0, sizeof(m_wcursors)); LOG_PRINT_L3("batch transaction: end"); } @@ -2234,7 +2293,7 @@ void BlockchainLMDB::batch_abort() m_write_batch_txn->abort(); m_batch_active = false; m_write_batch_txn = nullptr; - memset(&m_cursors, 0, sizeof(m_cursors)); + memset(&m_wcursors, 0, sizeof(m_wcursors)); LOG_PRINT_L3("batch transaction: aborted"); } @@ -2245,8 +2304,67 @@ void BlockchainLMDB::set_batch_transactions(bool batch_transactions) LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled")); } -void BlockchainLMDB::block_txn_start() +// return true if we started the txn, false if already started +bool BlockchainLMDB::block_rtxn_start() const { + if (m_write_txn) + return false; + if (!m_tinfo.get()) + { + m_tinfo.reset(new mdb_threadinfo); + memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors)); + memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); + if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) + throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str())); + } else if (!m_tinfo->m_ti_rflags.m_rf_txn) + { + if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn)) + throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str())); + } else + { + return false; + } + m_tinfo->m_ti_rflags.m_rf_txn = true; + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + return true; +} + +void BlockchainLMDB::block_rtxn_stop() const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + mdb_txn_reset(m_tinfo->m_ti_rtxn); + memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); +} + +void BlockchainLMDB::block_txn_start(bool readonly) +{ + if (readonly) + { + bool didit = false; + if (m_write_txn) + return; + if (!m_tinfo.get()) + { + m_tinfo.reset(new mdb_threadinfo); + memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors)); + memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); + if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn)) + throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str())); + didit = true; + } else if (!m_tinfo->m_ti_rflags.m_rf_txn) + { + if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn)) + throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str())); + didit = true; + } + if (didit) + { + m_tinfo->m_ti_rflags.m_rf_txn = true; + LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " RO"); + } + return; + } + LOG_PRINT_L3("BlockchainLMDB::" << __func__); // Distinguish the exceptions here from exceptions that would be thrown while // using the txn and committing it. @@ -2266,7 +2384,7 @@ void BlockchainLMDB::block_txn_start() m_write_txn = nullptr; throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str())); } - memset(&m_cursors, 0, sizeof(m_cursors)); + memset(&m_wcursors, 0, sizeof(m_wcursors)); } } @@ -2275,14 +2393,22 @@ void BlockchainLMDB::block_txn_stop() LOG_PRINT_L3("BlockchainLMDB::" << __func__); if (! m_batch_active) { - TIME_MEASURE_START(time1); - m_write_txn->commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; + if (m_write_txn) + { + TIME_MEASURE_START(time1); + m_write_txn->commit(); + TIME_MEASURE_FINISH(time1); + time_commit1 += time1; - delete m_write_txn; - m_write_txn = nullptr; - memset(&m_cursors, 0, sizeof(m_cursors)); + delete m_write_txn; + m_write_txn = nullptr; + memset(&m_wcursors, 0, sizeof(m_wcursors)); + } + else if (m_tinfo->m_ti_rtxn) + { + mdb_txn_reset(m_tinfo->m_ti_rtxn); + memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); + } } } @@ -2295,8 +2421,13 @@ void BlockchainLMDB::block_txn_abort() { delete m_write_txn; m_write_txn = nullptr; - memset(&m_cursors, 0, sizeof(m_cursors)); + memset(&m_wcursors, 0, sizeof(m_wcursors)); } + else if (m_tinfo->m_ti_rtxn) + { + mdb_txn_reset(m_tinfo->m_ti_rtxn); + memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags)); + } else { // This would probably mean an earlier exception was caught, but then we @@ -2348,31 +2479,18 @@ void BlockchainLMDB::pop_block(block& blk, std::vector& txs) LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); - mdb_txn_safe txn; - if (! m_batch_active) - { - if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, txn)) - throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str())); - m_write_txn = &txn; - memset(&m_cursors, 0, sizeof(m_cursors)); - } + block_txn_start(false); uint64_t num_outputs = m_num_outputs; try { BlockchainDB::pop_block(blk, txs); - if (! m_batch_active) - { - m_write_txn = nullptr; - memset(&m_cursors, 0, sizeof(m_cursors)); - txn.commit(); - } + block_txn_stop(); } catch (...) { m_num_outputs = num_outputs; - m_write_txn = nullptr; - memset(&m_cursors, 0, sizeof(m_cursors)); + block_txn_abort(); throw; } @@ -2387,13 +2505,16 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vectorm_ti_rcursors; + RCURSOR(output_txs); + RCURSOR(output_indices); for (const uint64_t &index : global_indices) { MDB_val_copy k(index); MDB_val v; - auto get_result = mdb_get(*txn_ptr, m_output_txs, &k, &v); + auto get_result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(OUTPUT_DNE("output with given index not in db")); else if (get_result) @@ -2401,7 +2522,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector &offsets, @@ -2430,19 +2551,19 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std } TXN_PREFIX_RDONLY(); - - lmdb_cur cur(*txn_ptr, m_output_amounts); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(output_amounts); MDB_val_copy k(amount); MDB_val v; - auto result = mdb_cursor_get(cur, &k, &v, MDB_SET); + auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET); if (result == MDB_NOTFOUND) throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found")); else if (result) throw0(DB_ERROR("DB error attempting to get an output")); mdb_size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); + mdb_cursor_count(m_cur_output_amounts, &num_elems); if (max <= 1 && num_elems <= max) throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); @@ -2452,13 +2573,13 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std { for (const uint64_t& index : offsets) { - mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_FIRST_DUP); for (uint64_t i = 0; i < index; ++i) { - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP); } - mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_CURRENT); uint64_t glob_index = *(const uint64_t*) v.mv_data; LOG_PRINT_L3("Amount: " << amount << " M0->v: " << glob_index); global_indices.push_back(glob_index); @@ -2477,10 +2598,10 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std } if (!curcount && index > num_elems/2) { - mdb_cursor_get(cur, &k, &v, MDB_LAST_DUP); - mdb_cursor_get(cur, &k, &v, MDB_PREV); /* kludge to unset C_EOF */ - mdb_cursor_get(cur, &k, &v, MDB_NEXT); - mdb_cursor_get(cur, &k, &v, MDB_GET_MULTIPLE); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_LAST_DUP); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_PREV); /* kludge to unset C_EOF */ + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_MULTIPLE); curcount = num_elems; while(1) @@ -2490,7 +2611,7 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std curcount -= count; if (curcount > index) { - mdb_cursor_get(cur, &k, &v, MDB_PREV_MULTIPLE); + mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_PREV_MULTIPLE); } else { blockstart = curcount; @@ -2506,7 +2627,7 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std while (index >= curcount) { TIME_MEASURE_START(db1); - if (mdb_cursor_get(cur, &k, &v, curcount == 0 ? MDB_GET_MULTIPLE : MDB_NEXT_MULTIPLE) != 0) + if (mdb_cursor_get(m_cur_output_amounts, &k, &v, curcount == 0 ? MDB_GET_MULTIPLE : MDB_NEXT_MULTIPLE) != 0) { // allow partial results result = false; @@ -2536,8 +2657,7 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std } } - cur.close(); - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); TIME_MEASURE_FINISH(txx); LOG_PRINT_L3("txx: " << txx << " db1: " << t_dbmul << " db2: " << t_dbscan); @@ -2550,20 +2670,21 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vectorm_ti_rcursors; std::vector global_indices; get_output_global_indices(amount, offsets, global_indices); if (global_indices.size() > 0) { - TXN_PREFIX_RDONLY(); - lmdb_cur cur(*txn_ptr, m_output_keys); + RCURSOR(output_keys); for (const uint64_t &index : global_indices) { MDB_val_copy k(index); MDB_val v; - auto get_result = mdb_cursor_get(cur, &k, &v, MDB_SET); + auto get_result = mdb_cursor_get(m_cur_output_keys, &k, &v, MDB_SET); if (get_result == MDB_NOTFOUND) throw1(OUTPUT_DNE("Attempting to get output pubkey by global index, but key does not exist")); else if (get_result) @@ -2573,8 +2694,8 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector val_key(version); MDB_val val_ret; - auto result = mdb_get(*txn_ptr, m_hf_starting_heights, &val_key, &val_ret); + auto result = mdb_get(m_txn, m_hf_starting_heights, &val_key, &val_ret); if (result == MDB_NOTFOUND) return std::numeric_limits::max(); if (result) throw0(DB_ERROR("Error attempting to retrieve a hard fork starting height from the db")); uint64_t ret = *(const uint64_t*)val_ret.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } @@ -2698,15 +2819,17 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const check_open(); TXN_PREFIX_RDONLY(); + const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors; + RCURSOR(hf_versions); MDB_val_copy val_key(height); MDB_val val_ret; - auto result = mdb_get(*txn_ptr, m_hf_versions, &val_key, &val_ret); + auto result = mdb_cursor_get(m_cur_hf_versions, &val_key, &val_ret, MDB_SET); if (result == MDB_NOTFOUND || result) throw0(DB_ERROR("Error attempting to retrieve a hard fork version from the db")); uint8_t ret = *(const uint8_t*)val_ret.mv_data; - TXN_POSTFIX_SUCCESS(); + TXN_POSTFIX_RDONLY(); return ret; } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 150f59475..e58643efa 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -30,6 +30,7 @@ #include "blockchain_db/blockchain_db.h" #include "cryptonote_protocol/blobdatatype.h" // for type blobdata +#include #include @@ -38,7 +39,7 @@ namespace cryptonote { -struct mdb_txn_cursors +typedef struct mdb_txn_cursors { MDB_cursor *m_txc_blocks; MDB_cursor *m_txc_block_heights; @@ -59,24 +60,58 @@ struct mdb_txn_cursors MDB_cursor *m_txc_tx_outputs; MDB_cursor *m_txc_spent_keys; -}; -#define m_cur_blocks m_cursors.m_txc_blocks -#define m_cur_block_heights m_cursors.m_txc_block_heights -#define m_cur_block_hashes m_cursors.m_txc_block_hashes -#define m_cur_block_timestamps m_cursors.m_txc_block_timestamps -#define m_cur_block_sizes m_cursors.m_txc_block_sizes -#define m_cur_block_diffs m_cursors.m_txc_block_diffs -#define m_cur_block_coins m_cursors.m_txc_block_coins -#define m_cur_output_txs m_cursors.m_txc_output_txs -#define m_cur_output_indices m_cursors.m_txc_output_indices -#define m_cur_output_amounts m_cursors.m_txc_output_amounts -#define m_cur_output_keys m_cursors.m_txc_output_keys -#define m_cur_txs m_cursors.m_txc_txs -#define m_cur_tx_heights m_cursors.m_txc_tx_heights -#define m_cur_tx_unlocks m_cursors.m_txc_tx_unlocks -#define m_cur_tx_outputs m_cursors.m_txc_tx_outputs -#define m_cur_spent_keys m_cursors.m_txc_spent_keys + MDB_cursor *m_txc_hf_versions; +} mdb_txn_cursors; + +#define m_cur_blocks m_cursors->m_txc_blocks +#define m_cur_block_heights m_cursors->m_txc_block_heights +#define m_cur_block_hashes m_cursors->m_txc_block_hashes +#define m_cur_block_timestamps m_cursors->m_txc_block_timestamps +#define m_cur_block_sizes m_cursors->m_txc_block_sizes +#define m_cur_block_diffs m_cursors->m_txc_block_diffs +#define m_cur_block_coins m_cursors->m_txc_block_coins +#define m_cur_output_txs m_cursors->m_txc_output_txs +#define m_cur_output_indices m_cursors->m_txc_output_indices +#define m_cur_output_amounts m_cursors->m_txc_output_amounts +#define m_cur_output_keys m_cursors->m_txc_output_keys +#define m_cur_txs m_cursors->m_txc_txs +#define m_cur_tx_heights m_cursors->m_txc_tx_heights +#define m_cur_tx_unlocks m_cursors->m_txc_tx_unlocks +#define m_cur_tx_outputs m_cursors->m_txc_tx_outputs +#define m_cur_spent_keys m_cursors->m_txc_spent_keys +#define m_cur_hf_versions m_cursors->m_txc_hf_versions + +typedef struct mdb_rflags +{ + bool m_rf_txn; + bool m_rf_blocks; + bool m_rf_block_heights; + bool m_rf_block_hashes; + bool m_rf_block_timestamps; + bool m_rf_block_sizes; + bool m_rf_block_diffs; + bool m_rf_block_coins; + bool m_rf_output_txs; + bool m_rf_output_indices; + bool m_rf_output_amounts; + bool m_rf_output_keys; + bool m_rf_txs; + bool m_rf_tx_heights; + bool m_rf_tx_unlocks; + bool m_rf_tx_outputs; + bool m_rf_spent_keys; + bool m_rf_hf_versions; +} mdb_rflags; + +typedef struct mdb_threadinfo +{ + MDB_txn *m_ti_rtxn; // per-thread read txn + mdb_txn_cursors m_ti_rcursors; // per-thread read cursors + mdb_rflags m_ti_rflags; // per-thread read state + + ~mdb_threadinfo(); +} mdb_threadinfo; struct mdb_txn_safe { @@ -234,9 +269,11 @@ public: virtual void batch_stop(); virtual void batch_abort(); - virtual void block_txn_start(); + virtual void block_txn_start(bool readonly); virtual void block_txn_stop(); virtual void block_txn_abort(); + virtual bool block_rtxn_start() const; + virtual void block_rtxn_stop() const; virtual void pop_block(block& blk, std::vector& txs); @@ -355,7 +392,8 @@ private: bool m_batch_transactions; // support for batch transactions bool m_batch_active; // whether batch transaction is in progress - struct mdb_txn_cursors m_cursors; + mdb_txn_cursors m_wcursors; + mutable boost::thread_specific_ptr m_tinfo; #if defined(__arm__) // force a value so it can compile with 32-bit ARM diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index a3eb21187..7ff44963c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1403,6 +1403,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_db->block_txn_start(true); rsp.current_blockchain_height = get_current_blockchain_height(); std::list blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); @@ -1424,6 +1425,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO // as done below if any standalone transactions were requested // and missed. rsp.missed_ids.splice(rsp.missed_ids.end(), missed_tx_ids); + m_db->block_txn_stop(); return false; } @@ -1442,6 +1444,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO for (const auto& tx: txs) rsp.txs.push_back(t_serializable_object_to_blob(tx)); + m_db->block_txn_stop(); return true; } //------------------------------------------------------------------ @@ -1585,12 +1588,14 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc return false; } + m_db->block_txn_start(true); // make sure that the last block in the request's block list matches // the genesis block auto gen_hash = m_db->get_block_hash_from_height(0); if(qblock_ids.back() != gen_hash) { LOG_PRINT_L1("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << std::endl << "id: " << qblock_ids.back() << ", " << std::endl << "expected: " << gen_hash << "," << std::endl << " dropping connection"); + m_db->block_txn_abort(); return false; } @@ -1612,9 +1617,11 @@ bool Blockchain::find_blockchain_supplement(const std::list& qbloc catch (const std::exception& e) { LOG_PRINT_L1("Non-critical error trying to find block by hash in BlockchainDB, hash: " << *bl_it); + m_db->block_txn_abort(); return false; } } + m_db->block_txn_stop(); // this should be impossible, as we checked that we share the genesis block, // but just in case... @@ -2777,6 +2784,7 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor { const auto& pts = points.get_points(); + m_db->batch_start(); for (const auto& pt : pts) { // if the checkpoint is for a block we don't have yet, move on @@ -2800,6 +2808,7 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor } } } + m_db->batch_stop(); } //------------------------------------------------------------------ // returns false if any of the checkpoints loading returns false. diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_core/hardfork.cpp index e99736ff4..7e2e82c4a 100644 --- a/src/cryptonote_core/hardfork.cpp +++ b/src/cryptonote_core/hardfork.cpp @@ -254,8 +254,11 @@ bool HardFork::reorganize_from_chain_height(uint64_t height) bool HardFork::rescan_from_block_height(uint64_t height) { CRITICAL_REGION_LOCAL(lock); - if (height >= db.height()) + db.block_txn_start(true); + if (height >= db.height()) { + db.block_txn_stop(); return false; + } versions.clear(); @@ -273,6 +276,7 @@ bool HardFork::rescan_from_block_height(uint64_t height) current_fork_index = 0; while (current_fork_index + 1 < heights.size() && heights[current_fork_index].version != lastv) ++current_fork_index; + db.block_txn_stop(); return true; } From 5a07cefe7bfb0000dcb078f8cfcb05d1709af8d2 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 18 Feb 2016 06:41:19 -0800 Subject: [PATCH 4/5] Wrap some more actions in a larger read txn --- src/cryptonote_core/blockchain.cpp | 36 +++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 7ff44963c..a83f4bc9c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -334,6 +334,7 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te m_db->fixup(); } + m_db->block_txn_start(true); // check how far behind we are uint64_t top_block_timestamp = m_db->get_top_block_timestamp(); uint64_t timestamp_diff = time(NULL) - top_block_timestamp; @@ -354,6 +355,7 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te #endif LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); + m_db->block_txn_stop(); return true; } @@ -549,6 +551,7 @@ bool Blockchain::get_short_chain_history(std::list& ids) const if(!sz) return true; + m_db->block_txn_start(true); bool genesis_included = false; uint64_t current_back_offset = 1; while(current_back_offset < sz) @@ -575,6 +578,7 @@ bool Blockchain::get_short_chain_history(std::list& ids) const { ids.push_back(m_db->get_block_hash_from_height(0)); } + m_db->block_txn_stop(); return true; } @@ -996,12 +1000,14 @@ void Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) if(h == 0) return; + m_db->block_txn_start(true); // add size of last blocks to vector (or less, if blockchain size < count) size_t start_offset = h - std::min(h, count); for(size_t i = start_offset; i < h; i++) { sz.push_back(m_db->get_block_size(i)); } + m_db->block_txn_stop(); } //------------------------------------------------------------------ uint64_t Blockchain::get_current_cumulative_blocksize_limit() const @@ -2434,9 +2440,12 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& CRITICAL_REGION_LOCAL(m_blockchain_lock); TIME_MEASURE_START(t1); + m_db->block_txn_start(true); if(bl.prev_id != get_tail_id()) { LOG_PRINT_L1("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << get_tail_id()); +leave: + m_db->block_txn_stop(); return false; } @@ -2445,7 +2454,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)m_hardfork->get_current_version()); bvc.m_verifivation_failed = true; - return false; + goto leave; } TIME_MEASURE_FINISH(t1); @@ -2457,7 +2466,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_PRINT_L1("Block with id: " << id << std::endl << "has invalid timestamp: " << bl.timestamp); bvc.m_verifivation_failed = true; - return false; + goto leave; } TIME_MEASURE_FINISH(t2); @@ -2497,7 +2506,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_PRINT_L1("Block with id is INVALID: " << id); bvc.m_verifivation_failed = true; - return false; + goto leave; } fast_check = true; } @@ -2518,7 +2527,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_PRINT_L1("Block with id: " << id << std::endl << "does not have enough proof of work: " << proof_of_work << std::endl << "unexpected difficulty: " << current_diffic); bvc.m_verifivation_failed = true; - return false; + goto leave; } } @@ -2530,7 +2539,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_ERROR("CHECKPOINT VALIDATION FAILED"); bvc.m_verifivation_failed = true; - return false; + goto leave; } } @@ -2545,7 +2554,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { LOG_PRINT_L1("Block with id: " << id << " failed to pass prevalidation"); bvc.m_verifivation_failed = true; - return false; + goto leave; } size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); @@ -2581,7 +2590,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& LOG_PRINT_L1("Block with id: " << id << " attempting to add transaction already in blockchain with id: " << tx_id); bvc.m_verifivation_failed = true; return_tx_to_pool(txs); - return false; + goto leave; } TIME_MEASURE_FINISH(aa); @@ -2594,7 +2603,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& LOG_PRINT_L1("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); bvc.m_verifivation_failed = true; return_tx_to_pool(txs); - return false; + goto leave; } TIME_MEASURE_FINISH(bb); @@ -2631,7 +2640,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& LOG_PRINT_L1("Block with id " << id << " added as invalid because of wrong inputs in transactions"); bvc.m_verifivation_failed = true; return_tx_to_pool(txs); - return false; + goto leave; } } #if defined(PER_BLOCK_CHECKPOINT) @@ -2647,7 +2656,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& LOG_PRINT_L1("Block with id " << id << " added as invalid because of wrong inputs in transactions"); bvc.m_verifivation_failed = true; return_tx_to_pool(txs); - return false; + goto leave; } } #endif @@ -2667,7 +2676,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& LOG_PRINT_L1("Block with id: " << id << " has incorrect miner transaction"); bvc.m_verifivation_failed = true; return_tx_to_pool(txs); - return false; + goto leave; } TIME_MEASURE_FINISH(vmt); @@ -2685,6 +2694,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& if(precomputed) block_processing_time += m_fake_pow_calc_time; + m_db->block_txn_stop(); TIME_MEASURE_START(addblock); uint64_t new_height = 0; if (!bvc.m_verifivation_failed) @@ -2761,10 +2771,12 @@ bool Blockchain::add_new_block(const block& bl_, block_verification_context& bvc crypto::hash id = get_block_hash(bl); CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process CRITICAL_REGION_LOCAL1(m_blockchain_lock); + m_db->block_txn_start(true); if(have_block(id)) { LOG_PRINT_L3("block with id = " << id << " already exists"); bvc.m_already_exists = true; + m_db->block_txn_stop(); return false; } @@ -2773,10 +2785,12 @@ bool Blockchain::add_new_block(const block& bl_, block_verification_context& bvc { //chain switching or wrong block bvc.m_added_to_main_chain = false; + m_db->block_txn_stop(); return handle_alternative_block(bl, id, bvc); //never relay alternative blocks } + m_db->block_txn_stop(); return handle_block_to_main_chain(bl, id, bvc); } //------------------------------------------------------------------ From 7db89ed2eee2ce31f039783323d665fe7c24d441 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 25 Feb 2016 13:55:49 +0000 Subject: [PATCH 5/5] ARMv7: fix unaligned accesses And cleanup some key comparators --- src/blockchain_db/lmdb/db_lmdb.cpp | 35 +++++++++++++----------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index f9459e8f1..e6d20a9e0 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -38,6 +38,10 @@ #include "crypto/crypto.h" #include "profile_tools.h" +#if defined(__i386) || defined(__x86_64) +#define MISALIGNED_OK 1 +#endif + using epee::string_tools::pod_to_hex; // Increase when the DB changes in a non backward compatible way, and there @@ -143,22 +147,11 @@ private: std::unique_ptr data; }; -auto compare_uint64 = [](const MDB_val *a, const MDB_val *b) -{ - const uint64_t va = *(const uint64_t*)a->mv_data; - const uint64_t vb = *(const uint64_t*)b->mv_data; - if (va < vb) return -1; - else if (va == vb) return 0; - else return 1; -}; - -auto compare_uint8 = [](const MDB_val *a, const MDB_val *b) +int compare_uint8(const MDB_val *a, const MDB_val *b) { const uint8_t va = *(const uint8_t*)a->mv_data; const uint8_t vb = *(const uint8_t*)b->mv_data; - if (va < vb) return -1; - else if (va == vb) return 0; - else return 1; + return va - vb; }; int compare_hash32(const MDB_val *a, const MDB_val *b) @@ -1099,29 +1092,26 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags) lmdb_db_open(txn, LMDB_TXS, MDB_CREATE, m_txs, "Failed to open db handle for m_txs"); lmdb_db_open(txn, LMDB_TX_UNLOCKS, MDB_CREATE, m_tx_unlocks, "Failed to open db handle for m_tx_unlocks"); lmdb_db_open(txn, LMDB_TX_HEIGHTS, MDB_CREATE, m_tx_heights, "Failed to open db handle for m_tx_heights"); - lmdb_db_open(txn, LMDB_TX_OUTPUTS, MDB_DUPSORT | MDB_CREATE, m_tx_outputs, "Failed to open db handle for m_tx_outputs"); + lmdb_db_open(txn, LMDB_TX_OUTPUTS, MDB_DUPSORT | MDB_INTEGERDUP | MDB_CREATE, m_tx_outputs, "Failed to open db handle for m_tx_outputs"); lmdb_db_open(txn, LMDB_OUTPUT_TXS, MDB_INTEGERKEY | MDB_CREATE, m_output_txs, "Failed to open db handle for m_output_txs"); lmdb_db_open(txn, LMDB_OUTPUT_INDICES, MDB_INTEGERKEY | MDB_CREATE, m_output_indices, "Failed to open db handle for m_output_indices"); - lmdb_db_open(txn, LMDB_OUTPUT_AMOUNTS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_output_amounts, "Failed to open db handle for m_output_amounts"); + lmdb_db_open(txn, LMDB_OUTPUT_AMOUNTS, MDB_INTEGERKEY | MDB_INTEGERDUP| MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_output_amounts, "Failed to open db handle for m_output_amounts"); lmdb_db_open(txn, LMDB_OUTPUT_KEYS, MDB_INTEGERKEY | MDB_CREATE, m_output_keys, "Failed to open db handle for m_output_keys"); lmdb_db_open(txn, LMDB_SPENT_KEYS, MDB_CREATE, m_spent_keys, "Failed to open db handle for m_spent_keys"); lmdb_db_open(txn, LMDB_HF_STARTING_HEIGHTS, MDB_CREATE, m_hf_starting_heights, "Failed to open db handle for m_hf_starting_heights"); - lmdb_db_open(txn, LMDB_HF_VERSIONS, MDB_CREATE, m_hf_versions, "Failed to open db handle for m_hf_versions"); + lmdb_db_open(txn, LMDB_HF_VERSIONS, MDB_INTEGERKEY| MDB_CREATE, m_hf_versions, "Failed to open db handle for m_hf_versions"); lmdb_db_open(txn, LMDB_PROPERTIES, MDB_CREATE, m_properties, "Failed to open db handle for m_properties"); - mdb_set_dupsort(txn, m_output_amounts, compare_uint64); - mdb_set_dupsort(txn, m_tx_outputs, compare_uint64); mdb_set_compare(txn, m_spent_keys, compare_hash32); mdb_set_compare(txn, m_block_heights, compare_hash32); mdb_set_compare(txn, m_txs, compare_hash32); mdb_set_compare(txn, m_tx_unlocks, compare_hash32); mdb_set_compare(txn, m_tx_heights, compare_hash32); mdb_set_compare(txn, m_hf_starting_heights, compare_uint8); - mdb_set_compare(txn, m_hf_versions, compare_uint64); mdb_set_compare(txn, m_properties, compare_string); // get and keep current height @@ -2789,7 +2779,12 @@ uint64_t BlockchainLMDB::get_hard_fork_starting_height(uint8_t version) const if (result) throw0(DB_ERROR("Error attempting to retrieve a hard fork starting height from the db")); - uint64_t ret = *(const uint64_t*)val_ret.mv_data; + uint64_t ret; +#ifdef MISALIGNED_OK + ret = *(const uint64_t*)val_ret.mv_data; +#else + memcpy(&ret, val_ret.mv_data, sizeof(uint64_t)); +#endif TXN_POSTFIX_RDONLY(); return ret; }