diff options
author | Thomas Winget <tewinget@gmail.com> | 2015-03-06 15:20:45 -0500 |
---|---|---|
committer | Thomas Winget <tewinget@gmail.com> | 2015-03-06 15:20:45 -0500 |
commit | 5eab480cb116b7c58fe020951995815baa7ccd8f (patch) | |
tree | 639114a6b9ba42054577e7558760d10e84d82360 /src/cryptonote_core | |
parent | Changed log level of debug message -- too spammy (diff) | |
download | monero-5eab480cb116b7c58fe020951995815baa7ccd8f.tar.xz |
Moved BlockchainDB into its own src/ subfolder
Ostensibly janitorial work, but should be more relevant later down the
line. Things that depend on core cryptonote things (i.e.
cryptonote_core) don't necessarily depend on BlockchainDB and thus
have no need to have BlockchainDB baked in with them.
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r-- | src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp | 1808 | ||||
-rw-r--r-- | src/cryptonote_core/BlockchainDB_impl/db_lmdb.h | 314 | ||||
-rw-r--r-- | src/cryptonote_core/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 4 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 2 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain_db.cpp | 180 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain_db.h | 487 |
7 files changed, 4 insertions, 2797 deletions
diff --git a/src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp b/src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp deleted file mode 100644 index feff4a272..000000000 --- a/src/cryptonote_core/BlockchainDB_impl/db_lmdb.cpp +++ /dev/null @@ -1,1808 +0,0 @@ -// Copyright (c) 2014, The Monero Project -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "db_lmdb.h" - -#include <boost/filesystem.hpp> -#include <memory> // std::unique_ptr -#include <cstring> // memcpy - -#include "cryptonote_core/cryptonote_format_utils.h" -#include "crypto/crypto.h" -#include "profile_tools.h" - -using epee::string_tools::pod_to_hex; - -namespace -{ - -template <typename T> -inline void throw0(const T &e) -{ - LOG_PRINT_L0(e.what()); - throw e; -} - -template <typename T> -inline void throw1(const T &e) -{ - LOG_PRINT_L1(e.what()); - throw e; -} - -// cursor needs to be closed when it goes out of scope, -// this helps if the function using it throws -struct lmdb_cur -{ - lmdb_cur(MDB_txn* txn, MDB_dbi dbi) - { - if (mdb_cursor_open(txn, dbi, &m_cur)) - throw0(cryptonote::DB_ERROR("Error opening db cursor")); - done = false; - } - - ~lmdb_cur() { close(); } - - operator MDB_cursor*() { return m_cur; } - operator MDB_cursor**() { return &m_cur; } - - void close() - { - if (!done) - { - mdb_cursor_close(m_cur); - done = true; - } - } - -private: - MDB_cursor* m_cur; - bool done; -}; - -template<typename T> -struct MDB_val_copy: public MDB_val -{ - MDB_val_copy(const T &t): t_copy(t) - { - mv_size = sizeof (T); - mv_data = &t_copy; - } -private: - T t_copy; -}; - -template<> -struct MDB_val_copy<cryptonote::blobdata>: public MDB_val -{ - MDB_val_copy(const cryptonote::blobdata &bd): data(new char[bd.size()]) - { - memcpy(data.get(), bd.data(), bd.size()); - mv_size = bd.size(); - mv_data = data.get(); - } -private: - std::unique_ptr<char[]> 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; -}; - -const char* const LMDB_BLOCKS = "blocks"; -const char* const LMDB_BLOCK_TIMESTAMPS = "block_timestamps"; -const char* const LMDB_BLOCK_HEIGHTS = "block_heights"; -const char* const LMDB_BLOCK_HASHES = "block_hashes"; -const char* const LMDB_BLOCK_SIZES = "block_sizes"; -const char* const LMDB_BLOCK_DIFFS = "block_diffs"; -const char* const LMDB_BLOCK_COINS = "block_coins"; - -const char* const LMDB_TXS = "txs"; -const char* const LMDB_TX_UNLOCKS = "tx_unlocks"; -const char* const LMDB_TX_HEIGHTS = "tx_heights"; -const char* const LMDB_TX_OUTPUTS = "tx_outputs"; - -const char* const LMDB_OUTPUT_TXS = "output_txs"; -const char* const LMDB_OUTPUT_INDICES = "output_indices"; -const char* const LMDB_OUTPUT_AMOUNTS = "output_amounts"; -const char* const LMDB_OUTPUT_KEYS = "output_keys"; -const char* const LMDB_OUTPUTS = "outputs"; -const char* const LMDB_OUTPUT_GINDICES = "output_gindices"; -const char* const LMDB_SPENT_KEYS = "spent_keys"; - -inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi, const std::string& error_string) -{ - if (mdb_dbi_open(txn, name, flags, &dbi)) - throw0(cryptonote::DB_OPEN_FAILURE(error_string.c_str())); -} - -} // anonymous namespace - -namespace cryptonote -{ - -void BlockchainLMDB::add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const crypto::hash& blk_hash - ) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<crypto::hash> val_h(blk_hash); - MDB_val unused; - if (mdb_get(*m_write_txn, m_block_heights, &val_h, &unused) == 0) - throw1(BLOCK_EXISTS("Attempting to add block that's already in the db")); - - if (m_height > 0) - { - MDB_val_copy<crypto::hash> parent_key(blk.prev_id); - MDB_val parent_h; - if (mdb_get(*m_write_txn, m_block_heights, &parent_key, &parent_h)) - { - LOG_PRINT_L3("m_height: " << m_height); - LOG_PRINT_L3("parent_key: " << blk.prev_id); - throw0(DB_ERROR("Failed to get top block hash to check for new block's parent")); - } - uint64_t parent_height = *(const uint64_t *)parent_h.mv_data; - if (parent_height != m_height - 1) - throw0(BLOCK_PARENT_DNE("Top block is not new block's parent")); - } - - MDB_val_copy<uint64_t> key(m_height); - - MDB_val_copy<blobdata> blob(block_to_blob(blk)); - auto res = mdb_put(*m_write_txn, m_blocks, &key, &blob, 0); - if (res) - throw0(DB_ERROR(std::string("Failed to add block blob to db transaction: ").append(mdb_strerror(res)).c_str())); - - MDB_val_copy<size_t> sz(block_size); - if (mdb_put(*m_write_txn, m_block_sizes, &key, &sz, 0)) - throw0(DB_ERROR("Failed to add block size to db transaction")); - - MDB_val_copy<uint64_t> ts(blk.timestamp); - if (mdb_put(*m_write_txn, m_block_timestamps, &key, &ts, 0)) - throw0(DB_ERROR("Failed to add block timestamp to db transaction")); - - MDB_val_copy<difficulty_type> diff(cumulative_difficulty); - if (mdb_put(*m_write_txn, m_block_diffs, &key, &diff, 0)) - throw0(DB_ERROR("Failed to add block cumulative difficulty to db transaction")); - - MDB_val_copy<uint64_t> coinsgen(coins_generated); - if (mdb_put(*m_write_txn, m_block_coins, &key, &coinsgen, 0)) - throw0(DB_ERROR("Failed to add block total generated coins to db transaction")); - - if (mdb_put(*m_write_txn, m_block_heights, &val_h, &key, 0)) - throw0(DB_ERROR("Failed to add block height by hash to db transaction")); - - if (mdb_put(*m_write_txn, m_block_hashes, &key, &val_h, 0)) - throw0(DB_ERROR("Failed to add block hash to db transaction")); - -} - -void BlockchainLMDB::remove_block() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - if (m_height == 0) - throw0(BLOCK_DNE ("Attempting to remove block from an empty blockchain")); - - MDB_val_copy<uint64_t> k(m_height - 1); - MDB_val h; - if (mdb_get(*m_write_txn, m_block_hashes, &k, &h)) - throw1(BLOCK_DNE("Attempting to remove block that's not in the db")); - - if (mdb_del(*m_write_txn, m_blocks, &k, NULL)) - throw1(DB_ERROR("Failed to add removal of block to db transaction")); - - if (mdb_del(*m_write_txn, m_block_sizes, &k, NULL)) - throw1(DB_ERROR("Failed to add removal of block size to db transaction")); - - if (mdb_del(*m_write_txn, m_block_diffs, &k, NULL)) - throw1(DB_ERROR("Failed to add removal of block cumulative difficulty to db transaction")); - - if (mdb_del(*m_write_txn, m_block_coins, &k, NULL)) - throw1(DB_ERROR("Failed to add removal of block total generated coins to db transaction")); - - if (mdb_del(*m_write_txn, m_block_timestamps, &k, NULL)) - throw1(DB_ERROR("Failed to add removal of block timestamp to db transaction")); - - if (mdb_del(*m_write_txn, m_block_heights, &h, NULL)) - throw1(DB_ERROR("Failed to add removal of block height by hash to db transaction")); - - if (mdb_del(*m_write_txn, m_block_hashes, &k, NULL)) - throw1(DB_ERROR("Failed to add removal of block hash to db transaction")); -} - -void BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<crypto::hash> val_h(tx_hash); - MDB_val unused; - if (mdb_get(*m_write_txn, m_txs, &val_h, &unused) == 0) - throw1(TX_EXISTS("Attempting to add transaction that's already in the db")); - - MDB_val_copy<blobdata> blob(tx_to_blob(tx)); - if (mdb_put(*m_write_txn, m_txs, &val_h, &blob, 0)) - throw0(DB_ERROR("Failed to add tx blob to db transaction")); - - MDB_val_copy<uint64_t> height(m_height); - if (mdb_put(*m_write_txn, m_tx_heights, &val_h, &height, 0)) - throw0(DB_ERROR("Failed to add tx block height to db transaction")); - - MDB_val_copy<uint64_t> unlock_time(tx.unlock_time); - if (mdb_put(*m_write_txn, m_tx_unlocks, &val_h, &unlock_time, 0)) - throw0(DB_ERROR("Failed to add tx unlock time to db transaction")); -} - -void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<crypto::hash> val_h(tx_hash); - MDB_val unused; - if (mdb_get(*m_write_txn, m_txs, &val_h, &unused)) - throw1(TX_DNE("Attempting to remove transaction that isn't in the db")); - - if (mdb_del(*m_write_txn, m_txs, &val_h, NULL)) - throw1(DB_ERROR("Failed to add removal of tx to db transaction")); - if (mdb_del(*m_write_txn, m_tx_unlocks, &val_h, NULL)) - throw1(DB_ERROR("Failed to add removal of tx unlock time to db transaction")); - if (mdb_del(*m_write_txn, m_tx_heights, &val_h, NULL)) - throw1(DB_ERROR("Failed to add removal of tx block height to db transaction")); - - remove_tx_outputs(tx_hash, tx); - - if (mdb_del(*m_write_txn, m_tx_outputs, &val_h, NULL)) - throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction")); - -} - -void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<uint64_t> k(m_num_outputs); - MDB_val_copy<crypto::hash> v(tx_hash); - - if (mdb_put(*m_write_txn, m_output_txs, &k, &v, 0)) - throw0(DB_ERROR("Failed to add output tx hash to db transaction")); - if (mdb_put(*m_write_txn, m_tx_outputs, &v, &k, 0)) - throw0(DB_ERROR("Failed to add tx output index to db transaction")); - - MDB_val_copy<uint64_t> val_local_index(local_index); - if (mdb_put(*m_write_txn, m_output_indices, &k, &val_local_index, 0)) - throw0(DB_ERROR("Failed to add tx output index to db transaction")); - - MDB_val_copy<uint64_t> val_amount(tx_output.amount); - if (auto result = mdb_put(*m_write_txn, m_output_amounts, &val_amount, &k, 0)) - throw0(DB_ERROR(std::string("Failed to add output amount to db transaction").append(mdb_strerror(result)).c_str())); - - if (tx_output.target.type() == typeid(txout_to_key)) - { - MDB_val_copy<crypto::public_key> val_pubkey(boost::get<txout_to_key>(tx_output.target).key); - if (mdb_put(*m_write_txn, m_output_keys, &k, &val_pubkey, 0)) - throw0(DB_ERROR("Failed to add output pubkey to db transaction")); - } - - -/****** Uncomment if ever outputs actually need to be stored in this manner - * - blobdata b = output_to_blob(tx_output); - - v.mv_size = b.size(); - v.mv_data = &b; - if (mdb_put(*m_write_txn, m_outputs, &k, &v, 0)) - throw0(DB_ERROR("Failed to add output to db transaction")); - if (mdb_put(*m_write_txn, m_output_gindices, &v, &k, 0)) - throw0(DB_ERROR("Failed to add output global index to db transaction")); -************************************************************************/ - - m_num_outputs++; -} - -void BlockchainLMDB::remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - - lmdb_cur cur(*m_write_txn, m_tx_outputs); - - MDB_val_copy<crypto::hash> k(tx_hash); - MDB_val v; - - auto result = mdb_cursor_get(cur, &k, &v, MDB_SET); - if (result == MDB_NOTFOUND) - { - LOG_ERROR("Attempting to remove a tx's outputs, but none found. Continuing, but...be wary, because that's weird."); - } - else if (result) - { - throw0(DB_ERROR("DB error attempting to get an output")); - } - else - { - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - - mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP); - - for (uint64_t i = 0; i < num_elems; ++i) - { - const tx_out tx_output = tx.vout[i]; - remove_output(*(const uint64_t*)v.mv_data, tx_output.amount); - if (i < num_elems - 1) - { - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); - } - } - } - - cur.close(); -} - -// TODO: probably remove this function -void BlockchainLMDB::remove_output(const tx_out& tx_output) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " (unused version - does nothing)"); - return; -} - -void BlockchainLMDB::remove_output(const uint64_t& out_index, const uint64_t amount) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<uint64_t> k(out_index); - -/****** Uncomment if ever outputs actually need to be stored in this manner - blobdata b; - t_serializable_object_to_blob(tx_output, b); - k.mv_size = b.size(); - k.mv_data = &b; - - if (mdb_get(*m_write_txn, m_output_gindices, &k, &v)) - throw1(OUTPUT_DNE("Attempting to remove output that does not exist")); - - uint64_t gindex = *(uint64_t*)v.mv_data; - - auto result = mdb_del(*m_write_txn, m_output_gindices, &k, NULL); - if (result != 0 && result != MDB_NOTFOUND) - throw1(DB_ERROR("Error adding removal of output global index to db transaction")); - - result = mdb_del(*m_write_txn, m_outputs, &v, NULL); - if (result != 0 && result != MDB_NOTFOUND) - throw1(DB_ERROR("Error adding removal of output to db transaction")); -*********************************************************************/ - - auto result = mdb_del(*m_write_txn, m_output_indices, &k, NULL); - if (result == MDB_NOTFOUND) - { - LOG_PRINT_L0("Unexpected: global output index not found in m_output_indices"); - } - else if (result) - { - throw1(DB_ERROR("Error adding removal of output tx index to db transaction")); - } - - result = mdb_del(*m_write_txn, m_output_txs, &k, NULL); - // if (result != 0 && result != MDB_NOTFOUND) - // throw1(DB_ERROR("Error adding removal of output tx hash to db transaction")); - if (result == MDB_NOTFOUND) - { - LOG_PRINT_L0("Unexpected: global output index not found in m_output_txs"); - } - else if (result) - { - throw1(DB_ERROR("Error adding removal of output tx hash to db transaction")); - } - - result = mdb_del(*m_write_txn, m_output_keys, &k, NULL); - if (result == MDB_NOTFOUND) - { - LOG_PRINT_L0("Unexpected: global output index not found in m_output_keys"); - } - else if (result) - throw1(DB_ERROR("Error adding removal of output pubkey to db transaction")); - - remove_amount_output_index(amount, out_index); - - m_num_outputs--; -} - -void BlockchainLMDB::remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - lmdb_cur cur(*m_write_txn, m_output_amounts); - - MDB_val_copy<uint64_t> k(amount); - MDB_val v; - - auto result = mdb_cursor_get(cur, &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")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - - mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP); - - uint64_t amount_output_index = 0; - uint64_t goi = 0; - bool found_index = false; - for (uint64_t i = 0; i < num_elems; ++i) - { - mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT); - goi = *(const uint64_t *)v.mv_data; - if (goi == global_output_index) - { - amount_output_index = i; - found_index = true; - break; - } - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); - } - if (found_index) - { - // found the amount output index - // now delete it - result = mdb_cursor_del(cur, 0); - if (result) - throw0(DB_ERROR(std::string("Error deleting amount output index ").append(boost::lexical_cast<std::string>(amount_output_index)).c_str())); - } - else - { - // not found - cur.close(); - throw1(OUTPUT_DNE("Failed to find amount output index")); - } - cur.close(); -} - -void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<crypto::key_image> val_key(k_image); - MDB_val unused; - if (mdb_get(*m_write_txn, m_spent_keys, &val_key, &unused) == 0) - throw1(KEY_IMAGE_EXISTS("Attempting to add spent key image that's already in the db")); - - char anything = '\0'; - unused.mv_size = sizeof(char); - unused.mv_data = &anything; - if (auto result = mdb_put(*m_write_txn, m_spent_keys, &val_key, &unused, 0)) - throw1(DB_ERROR(std::string("Error adding spent key image to db transaction: ").append(mdb_strerror(result)).c_str())); -} - -void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - MDB_val_copy<crypto::key_image> k(k_image); - auto result = mdb_del(*m_write_txn, m_spent_keys, &k, NULL); - if (result != 0 && result != MDB_NOTFOUND) - throw1(DB_ERROR("Error adding removal of key image to db transaction")); -} - -blobdata BlockchainLMDB::output_to_blob(const tx_out& output) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - blobdata b; - if (!t_serializable_object_to_blob(output, b)) - throw1(DB_ERROR("Error serializing output to blob")); - return b; -} - -tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - std::stringstream ss; - ss << blob; - binary_archive<false> ba(ss); - tx_out o; - - if (!(::serialization::serialize(ba, o))) - throw1(DB_ERROR("Error deserializing tx output blob")); - - return o; -} - -uint64_t BlockchainLMDB::get_output_global_index(const uint64_t& amount, const uint64_t& index) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - lmdb_cur cur(txn, m_output_amounts); - - MDB_val_copy<uint64_t> k(amount); - MDB_val v; - - auto result = mdb_cursor_get(cur, &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")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - if (num_elems <= index) - throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); - - mdb_cursor_get(cur, &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(cur, &k, &v, MDB_GET_CURRENT); - - uint64_t glob_index = *(const uint64_t*)v.mv_data; - - cur.close(); - - txn.commit(); - - return glob_index; -} - -void BlockchainLMDB::check_open() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (!m_open) - throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); -} - -BlockchainLMDB::~BlockchainLMDB() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - - // batch transaction shouldn't be active at this point. If it is, consider it aborted. - if (m_batch_active) - batch_abort(); -} - -BlockchainLMDB::BlockchainLMDB(bool batch_transactions) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - // initialize folder to something "safe" just in case - // someone accidentally misuses this class... - m_folder = "thishsouldnotexistbecauseitisgibberish"; - m_open = false; - - m_batch_transactions = batch_transactions; - m_write_txn = nullptr; - m_batch_active = false; - m_height = 0; -} - -void BlockchainLMDB::open(const std::string& filename) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - - if (m_open) - throw0(DB_OPEN_FAILURE("Attempted to open db, but it's already open")); - - boost::filesystem::path direc(filename); - if (boost::filesystem::exists(direc)) - { - if (!boost::filesystem::is_directory(direc)) - throw0(DB_OPEN_FAILURE("LMDB needs a directory path, but a file was passed")); - } - else - { - if (!boost::filesystem::create_directory(direc)) - throw0(DB_OPEN_FAILURE(std::string("Failed to create directory ").append(filename).c_str())); - } - - // check for existing LMDB files in base directory - boost::filesystem::path old_files = direc.parent_path(); - if (boost::filesystem::exists(old_files / "data.mdb") || - boost::filesystem::exists(old_files / "lock.mdb")) - { - LOG_PRINT_L0("Found existing LMDB files in " << old_files.c_str()); - LOG_PRINT_L0("Move data.mdb and/or lock.mdb to " << filename << ", or delete them, and then restart"); - throw DB_ERROR("Database could not be opened"); - } - - m_folder = filename; - - // set up lmdb environment - if (mdb_env_create(&m_env)) - throw0(DB_ERROR("Failed to create lmdb environment")); - if (mdb_env_set_maxdbs(m_env, 20)) - throw0(DB_ERROR("Failed to set max number of dbs")); - - size_t mapsize = 1LL << 34; - if (auto result = mdb_env_set_mapsize(m_env, mapsize)) - throw0(DB_ERROR(std::string("Failed to set max memory map size: ").append(mdb_strerror(result)).c_str())); - if (auto result = mdb_env_open(m_env, filename.c_str(), 0, 0644)) - throw0(DB_ERROR(std::string("Failed to open lmdb environment: ").append(mdb_strerror(result)).c_str())); - - // get a read/write MDB_txn - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, 0, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - // open necessary databases, and set properties as needed - // uses macros to avoid having to change things too many places - lmdb_db_open(txn, LMDB_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_blocks, "Failed to open db handle for m_blocks"); - - lmdb_db_open(txn, LMDB_BLOCK_TIMESTAMPS, MDB_INTEGERKEY | MDB_CREATE, m_block_timestamps, "Failed to open db handle for m_block_timestamps"); - lmdb_db_open(txn, LMDB_BLOCK_HEIGHTS, MDB_CREATE, m_block_heights, "Failed to open db handle for m_block_heights"); - lmdb_db_open(txn, LMDB_BLOCK_HASHES, MDB_INTEGERKEY | MDB_CREATE, m_block_hashes, "Failed to open db handle for m_block_hashes"); - lmdb_db_open(txn, LMDB_BLOCK_SIZES, MDB_INTEGERKEY | MDB_CREATE, m_block_sizes, "Failed to open db handle for m_block_sizes"); - lmdb_db_open(txn, LMDB_BLOCK_DIFFS, MDB_INTEGERKEY | MDB_CREATE, m_block_diffs, "Failed to open db handle for m_block_diffs"); - lmdb_db_open(txn, LMDB_BLOCK_COINS, MDB_INTEGERKEY | MDB_CREATE, m_block_coins, "Failed to open db handle for m_block_coins"); - - 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_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_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"); - -/*************** not used, but kept for posterity - lmdb_db_open(txn, LMDB_OUTPUTS, MDB_INTEGERKEY | MDB_CREATE, m_outputs, "Failed to open db handle for m_outputs"); - lmdb_db_open(txn, LMDB_OUTPUT_GINDICES, MDB_CREATE, m_output_gindices, "Failed to open db handle for m_output_gindices"); -*************************************************/ - - lmdb_db_open(txn, LMDB_SPENT_KEYS, MDB_CREATE, m_spent_keys, "Failed to open db handle for m_spent_keys"); - - mdb_set_dupsort(txn, m_output_amounts, compare_uint64); - mdb_set_dupsort(txn, m_tx_outputs, compare_uint64); - - // get and keep current height - MDB_stat db_stats; - if (mdb_stat(txn, m_blocks, &db_stats)) - throw0(DB_ERROR("Failed to query m_blocks")); - LOG_PRINT_L2("Setting m_height to: " << db_stats.ms_entries); - m_height = db_stats.ms_entries; - - // get and keep current number of outputs - if (mdb_stat(txn, m_output_indices, &db_stats)) - throw0(DB_ERROR("Failed to query m_output_indices")); - m_num_outputs = db_stats.ms_entries; - - // commit the transaction - txn.commit(); - - m_open = true; - // from here, init should be finished -} - -// unused for now, create will happen on open if doesn't exist -void BlockchainLMDB::create(const std::string& filename) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - throw DB_CREATE_FAILURE("create() is not implemented for this BlockchainDB, open() will create files if needed."); -} - -void BlockchainLMDB::close() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (m_batch_active) - { - LOG_PRINT_L3("close() first calling batch_abort() due to active batch transaction"); - batch_abort(); - } - this->sync(); - - // FIXME: not yet thread safe!!! Use with care. - mdb_env_close(m_env); -} - -void BlockchainLMDB::sync() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - // Does nothing unless LMDB environment was opened with MDB_NOSYNC or in part - // MDB_NOMETASYNC. Force flush to be synchronous. - if (auto result = mdb_env_sync(m_env, true)) - { - throw0(DB_ERROR(std::string("Failed to sync database").append(mdb_strerror(result)).c_str())); - } -} - -void BlockchainLMDB::reset() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - // TODO: this -} - -std::vector<std::string> BlockchainLMDB::get_filenames() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - std::vector<std::string> filenames; - - boost::filesystem::path datafile(m_folder); - datafile /= "data.mdb"; - boost::filesystem::path lockfile(m_folder); - lockfile /= "lock.mdb"; - - filenames.push_back(datafile.string()); - filenames.push_back(lockfile.string()); - - return filenames; -} - -// TODO: this? -bool BlockchainLMDB::lock() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - return false; -} - -// TODO: this? -void BlockchainLMDB::unlock() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); -} - -bool BlockchainLMDB::block_exists(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<crypto::hash> key(h); - MDB_val result; - auto get_result = mdb_get(txn, m_block_heights, &key, &result); - if (get_result == MDB_NOTFOUND) - { - txn.commit(); - 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.commit(); - return true; -} - -block BlockchainLMDB::get_block(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - return get_block_from_height(get_block_height(h)); -} - -uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<crypto::hash> key(h); - MDB_val result; - auto get_result = mdb_get(txn, m_block_heights, &key, &result); - 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")); - - txn.commit(); - return *(const uint64_t*)result.mv_data; -} - -block_header BlockchainLMDB::get_block_header(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - // block_header object is automatically cast from block object - return get_block(h); -} - -block BlockchainLMDB::get_block_from_height(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<uint64_t> key(height); - MDB_val result; - auto get_result = mdb_get(txn, m_blocks, &key, &result); - if (get_result == MDB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get block from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block not in db").c_str())); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve a block from the db")); - - txn.commit(); - - blobdata bd; - bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size); - - block b; - if (!parse_and_validate_block_from_blob(bd, b)) - throw0(DB_ERROR("Failed to parse block from blob retrieved from the db")); - - return b; -} - -uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - MDB_val_copy<uint64_t> key(height); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_timestamps, &key, &result); - if (get_result == MDB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- timestamp not in db").c_str())); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve a timestamp from the db")); - - if (! m_batch_active) - txn.commit(); - return *(const uint64_t*)result.mv_data; -} - -uint64_t BlockchainLMDB::get_top_block_timestamp() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - // if no blocks, return 0 - if (m_height == 0) - { - return 0; - } - - return get_block_timestamp(m_height - 1); -} - -size_t BlockchainLMDB::get_block_size(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - - MDB_val_copy<uint64_t> key(height); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_sizes, &key, &result); - if (get_result == MDB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get block size from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str())); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve a block size from the db")); - - if (! m_batch_active) - txn.commit(); - return *(const size_t*)result.mv_data; -} - -difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " height: " << height); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - MDB_val_copy<uint64_t> key(height); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_diffs, &key, &result); - if (get_result == MDB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- difficulty not in db").c_str())); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db")); - - if (! m_batch_active) - txn.commit(); - return *(difficulty_type*)result.mv_data; -} - -difficulty_type BlockchainLMDB::get_block_difficulty(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - difficulty_type diff1 = 0; - difficulty_type diff2 = 0; - - diff1 = get_block_cumulative_difficulty(height); - if (height != 0) - { - diff2 = get_block_cumulative_difficulty(height - 1); - } - - return diff1 - diff2; -} - -uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - - MDB_val_copy<uint64_t> key(height); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_coins, &key, &result); - if (get_result == MDB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str())); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve a total generated coins from the db")); - - if (! m_batch_active) - txn.commit(); - return *(const uint64_t*)result.mv_data; -} - -crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - - MDB_val_copy<uint64_t> key(height); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_block_hashes, &key, &result); - if (get_result == MDB_NOTFOUND) - { - throw0(BLOCK_DNE(std::string("Attempt to get hash from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- hash not in db").c_str())); - } - else if (get_result) - throw0(DB_ERROR(std::string("Error attempting to retrieve a block hash from the db: "). - append(mdb_strerror(get_result)).c_str())); - - if (! m_batch_active) - txn.commit(); - return *(crypto::hash*)result.mv_data; -} - -std::vector<block> BlockchainLMDB::get_blocks_range(const uint64_t& h1, const uint64_t& h2) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - std::vector<block> v; - - for (uint64_t height = h1; height <= h2; ++height) - { - v.push_back(get_block_from_height(height)); - } - - return v; -} - -std::vector<crypto::hash> BlockchainLMDB::get_hashes_range(const uint64_t& h1, const uint64_t& h2) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - std::vector<crypto::hash> v; - - for (uint64_t height = h1; height <= h2; ++height) - { - v.push_back(get_block_hash_from_height(height)); - } - - return v; -} - -crypto::hash BlockchainLMDB::top_block_hash() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - if (m_height != 0) - { - return get_block_hash_from_height(m_height - 1); - } - - return null_hash; -} - -block BlockchainLMDB::get_top_block() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - if (m_height != 0) - { - return get_block_from_height(m_height - 1); - } - - block b; - return b; -} - -uint64_t BlockchainLMDB::height() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - return m_height; -} - -bool BlockchainLMDB::tx_exists(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - - MDB_val_copy<crypto::hash> key(h); - MDB_val result; - - TIME_MEASURE_START(time1); - auto get_result = mdb_get(*txn_ptr, m_txs, &key, &result); - TIME_MEASURE_FINISH(time1); - time_tx_exists += time1; - if (get_result == MDB_NOTFOUND) - { - if (! m_batch_active) - txn.commit(); - LOG_PRINT_L1("transaction 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 transaction from hash")); - - return true; -} - -uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<crypto::hash> key(h); - MDB_val result; - auto get_result = mdb_get(txn, m_tx_unlocks, &key, &result); - 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")); - - return *(const uint64_t*)result.mv_data; -} - -transaction BlockchainLMDB::get_tx(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - - MDB_val_copy<crypto::hash> key(h); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_txs, &key, &result); - 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) - throw0(DB_ERROR("DB error attempting to fetch tx from hash")); - - blobdata bd; - bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size); - - transaction tx; - if (!parse_and_validate_tx_from_blob(bd, tx)) - throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db")); - if (! m_batch_active) - txn.commit(); - - return tx; -} - -uint64_t BlockchainLMDB::get_tx_count() const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_stat db_stats; - if (mdb_stat(txn, m_txs, &db_stats)) - throw0(DB_ERROR("Failed to query m_txs")); - - txn.commit(); - - return db_stats.ms_entries; -} - -std::vector<transaction> BlockchainLMDB::get_tx_list(const std::vector<crypto::hash>& hlist) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - std::vector<transaction> v; - - for (auto& h : hlist) - { - v.push_back(get_tx(h)); - } - - return v; -} - -uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - // If m_batch_active is set, a batch transaction exists beyond this class, - // such as a batch import with verification enabled, or possibly (later) a - // batch network sync. - // - // A regular network sync without batching would be expected to open a new - // read transaction here, as validation is done prior to the write for block - // and tx data. - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - - MDB_val_copy<crypto::hash> key(h); - MDB_val result; - auto get_result = mdb_get(*txn_ptr, m_tx_heights, &key, &result); - 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())); - } - else if (get_result) - throw0(DB_ERROR("DB error attempting to fetch tx height from hash")); - - if (! m_batch_active) - txn.commit(); - - return *(const uint64_t*)result.mv_data; -} - -//FIXME: make sure the random method used here is appropriate -uint64_t BlockchainLMDB::get_random_output(const uint64_t& amount) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - uint64_t num_outputs = get_num_outputs(amount); - if (num_outputs == 0) - throw1(OUTPUT_DNE("Attempting to get a random output for an amount, but none exist")); - - return crypto::rand<uint64_t>() % num_outputs; -} - -uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - lmdb_cur cur(txn, m_output_amounts); - - MDB_val_copy<uint64_t> k(amount); - MDB_val v; - auto result = mdb_cursor_get(cur, &k, &v, MDB_SET); - if (result == MDB_NOTFOUND) - { - return 0; - } - else if (result) - throw0(DB_ERROR("DB error attempting to get number of outputs of an amount")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - - txn.commit(); - - return num_elems; -} - -crypto::public_key BlockchainLMDB::get_output_key(const uint64_t& amount, const uint64_t& index) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - uint64_t glob_index = get_output_global_index(amount, index); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<uint64_t> k(glob_index); - MDB_val v; - auto get_result = mdb_get(txn, m_output_keys, &k, &v); - if (get_result == MDB_NOTFOUND) - throw0(DB_ERROR("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")); - - return *(crypto::public_key*)v.mv_data; -} - -tx_out BlockchainLMDB::get_output(const crypto::hash& h, const uint64_t& index) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - lmdb_cur cur(txn, m_tx_outputs); - - MDB_val_copy<crypto::hash> k(h); - MDB_val v; - auto result = mdb_cursor_get(cur, &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")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - if (num_elems <= index) - throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found")); - - mdb_cursor_get(cur, &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(cur, &k, &v, MDB_GET_CURRENT); - - blobdata b; - b = *(blobdata*)v.mv_data; - - cur.close(); - txn.commit(); - - return output_from_blob(b); -} - -// As this is not used, its return is now a blank output. -// This will save on space in the db. -tx_out BlockchainLMDB::get_output(const uint64_t& index) const -{ - return tx_out(); - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<uint64_t> k(index); - MDB_val v; - auto get_result = mdb_get(txn, m_outputs, &k, &v); - if (get_result == MDB_NOTFOUND) - { - throw OUTPUT_DNE("Attempting to get output by global index, but output does not exist"); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve an output from the db")); - - blobdata b = *(blobdata*)v.mv_data; - - return output_from_blob(b); -} - -tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& index) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - MDB_val_copy<uint64_t> k(index); - MDB_val v; - - auto get_result = mdb_get(*txn_ptr, m_output_txs, &k, &v); - 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 hash")); - - crypto::hash tx_hash = *(crypto::hash*)v.mv_data; - - get_result = mdb_get(*txn_ptr, m_output_indices, &k, &v); - 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")); - if (! m_batch_active) - txn.commit(); - - return tx_out_index(tx_hash, *(const uint64_t *)v.mv_data); -} - -tx_out_index BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - txn_safe* txn_ptr = &txn; - if (m_batch_active) - txn_ptr = m_write_txn; - else - { - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - } - lmdb_cur cur(*txn_ptr, m_output_amounts); - - MDB_val_copy<uint64_t> k(amount); - MDB_val v; - - auto result = mdb_cursor_get(cur, &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")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - if (num_elems <= index) - throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); - - mdb_cursor_get(cur, &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(cur, &k, &v, MDB_GET_CURRENT); - - uint64_t glob_index = *(const uint64_t*)v.mv_data; - - cur.close(); - - if (! m_batch_active) - txn.commit(); - - return get_output_tx_and_index_from_global(glob_index); -} - -std::vector<uint64_t> BlockchainLMDB::get_tx_output_indices(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - std::vector<uint64_t> index_vec; - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - lmdb_cur cur(txn, m_tx_outputs); - - MDB_val_copy<crypto::hash> k(h); - MDB_val v; - auto result = mdb_cursor_get(cur, &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")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - - mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP); - - for (uint64_t i = 0; i < num_elems; ++i) - { - mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT); - index_vec.push_back(*(const uint64_t *)v.mv_data); - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); - } - - cur.close(); - txn.commit(); - - return index_vec; -} - -std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - std::vector<uint64_t> index_vec; - std::vector<uint64_t> index_vec2; - - // get the transaction's global output indices first - index_vec = get_tx_output_indices(h); - // these are next used to obtain the amount output indices - - transaction tx = get_tx(h); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - uint64_t i = 0; - uint64_t global_index; - BOOST_FOREACH(const auto& vout, tx.vout) - { - uint64_t amount = vout.amount; - - global_index = index_vec[i]; - - lmdb_cur cur(txn, m_output_amounts); - - MDB_val_copy<uint64_t> k(amount); - MDB_val v; - - auto result = mdb_cursor_get(cur, &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")); - - size_t num_elems = 0; - mdb_cursor_count(cur, &num_elems); - - mdb_cursor_get(cur, &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); - output_index = *(const uint64_t *)v.mv_data; - if (output_index == global_index) - { - amount_output_index = j; - found_index = true; - break; - } - mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP); - } - if (found_index) - { - index_vec2.push_back(amount_output_index); - } - else - { - // not found - cur.close(); - txn.commit(); - throw1(OUTPUT_DNE("specified output not found in db")); - } - - cur.close(); - ++i; - } - - txn.commit(); - - return index_vec2; -} - - - -bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - MDB_val_copy<crypto::key_image> val_key(img); - MDB_val unused; - if (mdb_get(txn, m_spent_keys, &val_key, &unused) == 0) - { - txn.commit(); - return true; - } - - txn.commit(); - return false; -} - -void BlockchainLMDB::batch_start() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (! m_batch_transactions) - throw0(DB_ERROR("batch transactions not enabled")); - if (m_batch_active) - throw0(DB_ERROR("batch transaction already in progress")); - if (m_write_txn) - throw0(DB_ERROR("batch transaction attempted, but m_write_txn already in use")); - check_open(); - // NOTE: need to make sure it's destroyed properly when done - if (mdb_txn_begin(m_env, NULL, 0, m_write_batch_txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - // indicates this transaction is for batch transactions, but not whether it's - // active - m_write_batch_txn.m_batch_txn = true; - m_write_txn = &m_write_batch_txn; - m_batch_active = true; - LOG_PRINT_L3("batch transaction: begin"); -} - -void BlockchainLMDB::batch_commit() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (! m_batch_transactions) - throw0(DB_ERROR("batch transactions not enabled")); - if (! m_batch_active) - throw0(DB_ERROR("batch transaction not in progress")); - check_open(); - LOG_PRINT_L3("batch transaction: committing..."); - TIME_MEASURE_START(time1); - m_write_txn->commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; - LOG_PRINT_L3("batch transaction: committed"); - - if (mdb_txn_begin(m_env, NULL, 0, m_write_batch_txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - if (! m_write_batch_txn.m_batch_txn) - throw0(DB_ERROR("m_write_batch_txn not marked as a batch transaction")); - m_write_txn = &m_write_batch_txn; -} - -void BlockchainLMDB::batch_stop() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (! m_batch_transactions) - throw0(DB_ERROR("batch transactions not enabled")); - if (! m_batch_active) - throw0(DB_ERROR("batch transaction not in progress")); - 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; - m_batch_active = false; - LOG_PRINT_L3("batch transaction: end"); -} - -void BlockchainLMDB::batch_abort() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - if (! m_batch_transactions) - throw0(DB_ERROR("batch transactions not enabled")); - if (! m_batch_active) - throw0(DB_ERROR("batch transaction not in progress")); - check_open(); - // for destruction of batch transaction - m_write_txn = nullptr; - // explicitly call in case mdb_env_close() (BlockchainLMDB::close()) called before BlockchainLMDB destructor called. - m_write_batch_txn.abort(); - m_batch_active = false; - LOG_PRINT_L3("batch transaction: aborted"); -} - -void BlockchainLMDB::set_batch_transactions(bool batch_transactions) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - m_batch_transactions = batch_transactions; - LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled")); -} - -uint64_t BlockchainLMDB::add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const std::vector<transaction>& txs - ) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (! m_batch_active) - { - if (mdb_txn_begin(m_env, NULL, 0, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - m_write_txn = &txn; - } - - uint64_t num_outputs = m_num_outputs; - try - { - BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); - if (! m_batch_active) - { - m_write_txn = NULL; - - TIME_MEASURE_START(time1); - txn.commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; - } - } - catch (...) - { - m_num_outputs = num_outputs; - if (! m_batch_active) - m_write_txn = NULL; - throw; - } - - return ++m_height; -} - -void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs) -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - txn_safe txn; - if (! m_batch_active) - { - if (mdb_txn_begin(m_env, NULL, 0, txn)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - m_write_txn = &txn; - } - - uint64_t num_outputs = m_num_outputs; - try - { - BlockchainDB::pop_block(blk, txs); - if (! m_batch_active) - { - m_write_txn = NULL; - - txn.commit(); - } - } - catch (...) - { - m_num_outputs = num_outputs; - m_write_txn = NULL; - throw; - } - - --m_height; -} - -} // namespace cryptonote diff --git a/src/cryptonote_core/BlockchainDB_impl/db_lmdb.h b/src/cryptonote_core/BlockchainDB_impl/db_lmdb.h deleted file mode 100644 index 0b4b5ec1a..000000000 --- a/src/cryptonote_core/BlockchainDB_impl/db_lmdb.h +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) 2014, The Monero Project -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "cryptonote_core/blockchain_db.h" -#include "cryptonote_protocol/blobdatatype.h" // for type blobdata - -#include <lmdb.h> - -namespace cryptonote -{ - -struct txn_safe -{ - txn_safe() : m_txn(NULL) { } - ~txn_safe() - { - LOG_PRINT_L3("txn_safe: destructor"); - if (m_txn != NULL) - { - if (m_batch_txn) // this is a batch txn and should have been handled before this point for safety - { - LOG_PRINT_L0("WARNING: txn_safe: m_txn is a batch txn and it's not NULL in destructor - calling mdb_txn_abort()"); - } - else - { - // Example of when this occurs: a lookup fails, so a read-only txn is - // aborted through this destructor. However, successful read-only txns - // ideally should have been committed when done and not end up here. - // - // NOTE: not sure if this is ever reached for a non-batch write - // transaction, but it's probably not ideal if it did. - LOG_PRINT_L3("txn_safe: m_txn not NULL in destructor - calling mdb_txn_abort()"); - } - mdb_txn_abort(m_txn); - } - } - - void commit(std::string message = "") - { - if (message.size() == 0) - { - message = "Failed to commit a transaction to the db"; - } - - if (mdb_txn_commit(m_txn)) - { - m_txn = NULL; - LOG_PRINT_L0(message); - throw DB_ERROR(message.c_str()); - } - m_txn = NULL; - } - - // This should only be needed for batch transaction which must be ensured to - // be aborted before mdb_env_close, not after. So we can't rely on - // BlockchainLMDB destructor to call txn_safe destructor, as that's too late - // to properly abort, since mdb_env_close would have been called earlier. - void abort() - { - LOG_PRINT_L3("txn_safe: abort()"); - if(m_txn != NULL) - { - mdb_txn_abort(m_txn); - m_txn = NULL; - } - else - { - LOG_PRINT_L0("WARNING: txn_safe: abort() called, but m_txn is NULL"); - } - } - - operator MDB_txn*() - { - return m_txn; - } - - operator MDB_txn**() - { - return &m_txn; - } - - MDB_txn* m_txn; - bool m_batch_txn = false; -}; - - -class BlockchainLMDB : public BlockchainDB -{ -public: - BlockchainLMDB(bool batch_transactions=false); - ~BlockchainLMDB(); - - virtual void open(const std::string& filename); - - virtual void create(const std::string& filename); - - virtual void close(); - - virtual void sync(); - - virtual void reset(); - - virtual std::vector<std::string> get_filenames() const; - - virtual bool lock(); - - virtual void unlock(); - - virtual bool block_exists(const crypto::hash& h) const; - - virtual block get_block(const crypto::hash& h) const; - - virtual uint64_t get_block_height(const crypto::hash& h) const; - - virtual block_header get_block_header(const crypto::hash& h) const; - - virtual block get_block_from_height(const uint64_t& height) const; - - virtual uint64_t get_block_timestamp(const uint64_t& height) const; - - virtual uint64_t get_top_block_timestamp() const; - - virtual size_t get_block_size(const uint64_t& height) const; - - virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; - - virtual difficulty_type get_block_difficulty(const uint64_t& height) const; - - virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const; - - virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const; - - virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const; - - virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const; - - virtual crypto::hash top_block_hash() const; - - virtual block get_top_block() const; - - virtual uint64_t height() const; - - virtual bool tx_exists(const crypto::hash& h) const; - - virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const; - - virtual transaction get_tx(const crypto::hash& h) const; - - virtual uint64_t get_tx_count() const; - - virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const; - - virtual uint64_t get_tx_block_height(const crypto::hash& h) const; - - virtual uint64_t get_random_output(const uint64_t& amount) const; - - virtual uint64_t get_num_outputs(const uint64_t& amount) const; - - virtual crypto::public_key get_output_key(const uint64_t& amount, const uint64_t& index) const; - - virtual tx_out get_output(const crypto::hash& h, const uint64_t& index) const; - - /** - * @brief get an output from its global index - * - * @param index global index of the output desired - * - * @return the output associated with the index. - * Will throw OUTPUT_DNE if not output has that global index. - * Will throw DB_ERROR if there is a non-specific LMDB error in fetching - */ - tx_out get_output(const uint64_t& index) const; - - virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const; - - virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const; - - virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const; - virtual std::vector<uint64_t> get_tx_amount_output_indices(const crypto::hash& h) const; - - virtual bool has_key_image(const crypto::key_image& img) const; - - virtual uint64_t add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const std::vector<transaction>& txs - ); - - virtual void set_batch_transactions(bool batch_transactions); - virtual void batch_start(); - virtual void batch_commit(); - virtual void batch_stop(); - virtual void batch_abort(); - - virtual void pop_block(block& blk, std::vector<transaction>& txs); - -private: - virtual void add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const crypto::hash& block_hash - ); - - virtual void remove_block(); - - virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash); - - virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx); - - virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index); - - virtual void remove_output(const tx_out& tx_output); - - void remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx); - - void remove_output(const uint64_t& out_index, const uint64_t amount); - void remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index); - - virtual void add_spent_key(const crypto::key_image& k_image); - - virtual void remove_spent_key(const crypto::key_image& k_image); - - /** - * @brief convert a tx output to a blob for storage - * - * @param output the output to convert - * - * @return the resultant blob - */ - blobdata output_to_blob(const tx_out& output); - - /** - * @brief convert a tx output blob to a tx output - * - * @param blob the blob to convert - * - * @return the resultant tx output - */ - tx_out output_from_blob(const blobdata& blob) const; - - /** - * @brief get the global index of the index-th output of the given amount - * - * @param amount the output amount - * @param index the index into the set of outputs of that amount - * - * @return the global index of the desired output - */ - uint64_t get_output_global_index(const uint64_t& amount, const uint64_t& index) const; - - void check_open() const; - - MDB_env* m_env; - - MDB_dbi m_blocks; - MDB_dbi m_block_heights; - MDB_dbi m_block_hashes; - MDB_dbi m_block_timestamps; - MDB_dbi m_block_sizes; - MDB_dbi m_block_diffs; - MDB_dbi m_block_coins; - - MDB_dbi m_txs; - MDB_dbi m_tx_unlocks; - MDB_dbi m_tx_heights; - MDB_dbi m_tx_outputs; - - MDB_dbi m_output_txs; - MDB_dbi m_output_indices; - MDB_dbi m_output_gindices; - MDB_dbi m_output_amounts; - MDB_dbi m_output_keys; - MDB_dbi m_outputs; - - MDB_dbi m_spent_keys; - - bool m_open; - uint64_t m_height; - uint64_t m_num_outputs; - std::string m_folder; - txn_safe* m_write_txn; // may point to either a short-lived txn or a batch txn - txn_safe m_write_batch_txn; // persist batch txn outside of BlockchainLMDB - - bool m_batch_transactions; // support for batch transactions - bool m_batch_active; // whether batch transaction is in progress -}; - -} // namespace cryptonote diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt index fc9cc629b..26122e367 100644 --- a/src/cryptonote_core/CMakeLists.txt +++ b/src/cryptonote_core/CMakeLists.txt @@ -30,8 +30,6 @@ set(cryptonote_core_sources account.cpp blockchain_storage.cpp blockchain.cpp - blockchain_db.cpp - BlockchainDB_impl/db_lmdb.cpp checkpoints.cpp checkpoints_create.cpp cryptonote_basic_impl.cpp @@ -49,8 +47,6 @@ set(cryptonote_core_private_headers blockchain_storage.h blockchain_storage_boost_serialization.h blockchain.h - blockchain_db.h - BlockchainDB_impl/db_lmdb.h checkpoints.h checkpoints_create.h connection_context.h @@ -76,6 +72,7 @@ target_link_libraries(cryptonote_core LINK_PUBLIC common crypto + blockchain_db ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} @@ -83,5 +80,4 @@ target_link_libraries(cryptonote_core ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} - ${LMDB_LIBRARY} ${EXTRA_LIBRARIES}) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index b2d979432..9f4895736 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -38,8 +38,8 @@ #include "cryptonote_basic_impl.h" #include "tx_pool.h" #include "blockchain.h" -#include "cryptonote_core/blockchain_db.h" -#include "cryptonote_core/BlockchainDB_impl/db_lmdb.h" +#include "blockchain_db/blockchain_db.h" +#include "blockchain_db/lmdb/db_lmdb.h" #include "cryptonote_format_utils.h" #include "cryptonote_boost_serialization.h" #include "cryptonote_config.h" diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 3e8a2de31..477aa4964 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -50,7 +50,7 @@ #include "verification_context.h" #include "crypto/hash.h" #include "checkpoints.h" -#include "blockchain_db.h" +#include "blockchain_db/blockchain_db.h" namespace cryptonote { diff --git a/src/cryptonote_core/blockchain_db.cpp b/src/cryptonote_core/blockchain_db.cpp deleted file mode 100644 index fc8aeef4e..000000000 --- a/src/cryptonote_core/blockchain_db.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2014, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "cryptonote_core/blockchain_db.h" -#include "cryptonote_format_utils.h" -#include "profile_tools.h" - -using epee::string_tools::pod_to_hex; - -namespace cryptonote -{ - -void BlockchainDB::pop_block() -{ - block blk; - std::vector<transaction> txs; - pop_block(blk, txs); -} - -void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr) -{ - crypto::hash tx_hash; - if (!tx_hash_ptr) - { - // should only need to compute hash for miner transactions - tx_hash = get_transaction_hash(tx); - LOG_PRINT_L3("null tx_hash_ptr - needed to compute: " << tx_hash); - } - else - { - tx_hash = *tx_hash_ptr; - } - - add_transaction_data(blk_hash, tx, tx_hash); - - // iterate tx.vout using indices instead of C++11 foreach syntax because - // we need the index - if (tx.vout.size() != 0) // it may be technically possible for a tx to have no outputs - { - for (uint64_t i = 0; i < tx.vout.size(); ++i) - { - add_output(tx_hash, tx.vout[i], i); - } - - for (const txin_v& tx_input : tx.vin) - { - if (tx_input.type() == typeid(txin_to_key)) - { - add_spent_key(boost::get<txin_to_key>(tx_input).k_image); - } - } - } -} - -uint64_t BlockchainDB::add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const std::vector<transaction>& txs - ) -{ - TIME_MEASURE_START(time1); - crypto::hash blk_hash = get_block_hash(blk); - TIME_MEASURE_FINISH(time1); - time_blk_hash += time1; - - // call out to subclass implementation to add the block & metadata - time1 = epee::misc_utils::get_tick_count(); - add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash); - TIME_MEASURE_FINISH(time1); - time_add_block1 += time1; - - // call out to add the transactions - - time1 = epee::misc_utils::get_tick_count(); - add_transaction(blk_hash, blk.miner_tx); - int tx_i = 0; - crypto::hash tx_hash = null_hash; - for (const transaction& tx : txs) - { - tx_hash = blk.tx_hashes[tx_i]; - add_transaction(blk_hash, tx, &tx_hash); - ++tx_i; - } - TIME_MEASURE_FINISH(time1); - time_add_transaction += time1; - - ++num_calls; - - return height(); -} - -void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs) -{ - blk = get_top_block(); - - remove_block(); - - remove_transaction(get_transaction_hash(blk.miner_tx)); - for (const auto& h : blk.tx_hashes) - { - txs.push_back(get_tx(h)); - remove_transaction(h); - } -} - -void BlockchainDB::remove_transaction(const crypto::hash& tx_hash) -{ - transaction tx = get_tx(tx_hash); - - for (const txin_v& tx_input : tx.vin) - { - if (tx_input.type() == typeid(txin_to_key)) - { - remove_spent_key(boost::get<txin_to_key>(tx_input).k_image); - } - } - - // need tx as tx.vout has the tx outputs, and the output amounts are needed - remove_transaction_data(tx_hash, tx); -} - -void BlockchainDB::reset_stats() -{ - num_calls = 0; - time_blk_hash = 0; - time_tx_exists = 0; - time_add_block1 = 0; - time_add_transaction = 0; - time_commit1 = 0; -} - -void BlockchainDB::show_stats() -{ - LOG_PRINT_L1(ENDL - << "*********************************" - << ENDL - << "num_calls: " << num_calls - << ENDL - << "time_blk_hash: " << time_blk_hash << "ms" - << ENDL - << "time_tx_exists: " << time_tx_exists << "ms" - << ENDL - << "time_add_block1: " << time_add_block1 << "ms" - << ENDL - << "time_add_transaction: " << time_add_transaction << "ms" - << ENDL - << "time_commit1: " << time_commit1 << "ms" - << ENDL - << "*********************************" - << ENDL - ); -} - -} // namespace cryptonote diff --git a/src/cryptonote_core/blockchain_db.h b/src/cryptonote_core/blockchain_db.h deleted file mode 100644 index 2a7fa8f82..000000000 --- a/src/cryptonote_core/blockchain_db.h +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright (c) 2014, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef BLOCKCHAIN_DB_H -#define BLOCKCHAIN_DB_H - -#include <list> -#include <string> -#include <exception> -#include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" - -/* DB Driver Interface - * - * The DB interface is a store for the canonical block chain. - * It serves as a persistent storage for the blockchain. - * - * For the sake of efficiency, the reference implementation will also - * store some blockchain data outside of the blocks, such as spent - * transfer key images, unspent transaction outputs, etc. - * - * Transactions are duplicated so that we don't have to fetch a whole block - * in order to fetch a transaction from that block. If this is deemed - * unnecessary later, this can change. - * - * Spent key images are duplicated outside of the blocks so it is quick - * to verify an output hasn't already been spent - * - * Unspent transaction outputs are duplicated to quickly gather random - * outputs to use for mixins - * - * IMPORTANT: - * A concrete implementation of this interface should populate these - * duplicated members! It is possible to have a partial implementation - * of this interface call to private members of the interface to be added - * later that will then populate as needed. - * - * General: - * open() - * close() - * sync() - * reset() - * - * Lock and unlock provided for reorg externally, and for block - * additions internally, this way threaded reads are completely fine - * unless the blockchain is changing. - * bool lock() - * unlock() - * - * vector<str> get_filenames() - * - * Blocks: - * bool block_exists(hash) - * height add_block(block, block_size, cumulative_difficulty, coins_generated, transactions) - * block get_block(hash) - * height get_block_height(hash) - * header get_block_header(hash) - * block get_block_from_height(height) - * size_t get_block_size(height) - * difficulty get_block_cumulative_difficulty(height) - * uint64_t get_block_already_generated_coins(height) - * uint64_t get_block_timestamp(height) - * uint64_t get_top_block_timestamp() - * hash get_block_hash_from_height(height) - * blocks get_blocks_range(height1, height2) - * hashes get_hashes_range(height1, height2) - * hash top_block_hash() - * block get_top_block() - * height height() - * void pop_block(block&, tx_list&) - * - * Transactions: - * bool tx_exists(hash) - * uint64_t get_tx_unlock_time(hash) - * tx get_tx(hash) - * uint64_t get_tx_count() - * tx_list get_tx_list(hash_list) - * height get_tx_block_height(hash) - * - * Outputs: - * index get_random_output(amount) - * uint64_t get_num_outputs(amount) - * pub_key get_output_key(amount, index) - * tx_out get_output(tx_hash, index) - * hash,index get_output_tx_and_index_from_global(index) - * hash,index get_output_tx_and_index(amount, index) - * vec<uint64> get_tx_output_indices(tx_hash) - * - * - * Spent Output Key Images: - * bool has_key_image(key_image) - * - * Exceptions: - * DB_ERROR -- generic - * DB_OPEN_FAILURE - * DB_CREATE_FAILURE - * DB_SYNC_FAILURE - * BLOCK_DNE - * BLOCK_PARENT_DNE - * BLOCK_EXISTS - * BLOCK_INVALID -- considering making this multiple errors - * TX_DNE - * TX_EXISTS - * OUTPUT_DNE - * OUTPUT_EXISTS - * KEY_IMAGE_EXISTS - */ - -namespace cryptonote -{ - -// typedef for convenience -typedef std::pair<crypto::hash, uint64_t> tx_out_index; - -/*********************************** - * Exception Definitions - ***********************************/ -class DB_EXCEPTION : public std::exception -{ - private: - std::string m; - - protected: - DB_EXCEPTION(const char *s) : m(s) { } - - public: - virtual ~DB_EXCEPTION() { } - - const char* what() const throw() - { - return m.c_str(); - } -}; - -class DB_ERROR : public DB_EXCEPTION -{ - public: - DB_ERROR() : DB_EXCEPTION("Generic DB Error") { } - DB_ERROR(const char* s) : DB_EXCEPTION(s) { } -}; - -class DB_OPEN_FAILURE : public DB_EXCEPTION -{ - public: - DB_OPEN_FAILURE() : DB_EXCEPTION("Failed to open the db") { } - DB_OPEN_FAILURE(const char* s) : DB_EXCEPTION(s) { } -}; - -class DB_CREATE_FAILURE : public DB_EXCEPTION -{ - public: - DB_CREATE_FAILURE() : DB_EXCEPTION("Failed to create the db") { } - DB_CREATE_FAILURE(const char* s) : DB_EXCEPTION(s) { } -}; - -class DB_SYNC_FAILURE : public DB_EXCEPTION -{ - public: - DB_SYNC_FAILURE() : DB_EXCEPTION("Failed to sync the db") { } - DB_SYNC_FAILURE(const char* s) : DB_EXCEPTION(s) { } -}; - -class BLOCK_DNE : public DB_EXCEPTION -{ - public: - BLOCK_DNE() : DB_EXCEPTION("The block requested does not exist") { } - BLOCK_DNE(const char* s) : DB_EXCEPTION(s) { } -}; - -class BLOCK_PARENT_DNE : public DB_EXCEPTION -{ - public: - BLOCK_PARENT_DNE() : DB_EXCEPTION("The parent of the block does not exist") { } - BLOCK_PARENT_DNE(const char* s) : DB_EXCEPTION(s) { } -}; - -class BLOCK_EXISTS : public DB_EXCEPTION -{ - public: - BLOCK_EXISTS() : DB_EXCEPTION("The block to be added already exists!") { } - BLOCK_EXISTS(const char* s) : DB_EXCEPTION(s) { } -}; - -class BLOCK_INVALID : public DB_EXCEPTION -{ - public: - BLOCK_INVALID() : DB_EXCEPTION("The block to be added did not pass validation!") { } - BLOCK_INVALID(const char* s) : DB_EXCEPTION(s) { } -}; - -class TX_DNE : public DB_EXCEPTION -{ - public: - TX_DNE() : DB_EXCEPTION("The transaction requested does not exist") { } - TX_DNE(const char* s) : DB_EXCEPTION(s) { } -}; - -class TX_EXISTS : public DB_EXCEPTION -{ - public: - TX_EXISTS() : DB_EXCEPTION("The transaction to be added already exists!") { } - TX_EXISTS(const char* s) : DB_EXCEPTION(s) { } -}; - -class OUTPUT_DNE : public DB_EXCEPTION -{ - public: - OUTPUT_DNE() : DB_EXCEPTION("The output requested does not exist!") { } - OUTPUT_DNE(const char* s) : DB_EXCEPTION(s) { } -}; - -class OUTPUT_EXISTS : public DB_EXCEPTION -{ - public: - OUTPUT_EXISTS() : DB_EXCEPTION("The output to be added already exists!") { } - OUTPUT_EXISTS(const char* s) : DB_EXCEPTION(s) { } -}; - -class KEY_IMAGE_EXISTS : public DB_EXCEPTION -{ - public: - KEY_IMAGE_EXISTS() : DB_EXCEPTION("The spent key image to be added already exists!") { } - KEY_IMAGE_EXISTS(const char* s) : DB_EXCEPTION(s) { } -}; - -/*********************************** - * End of Exception Definitions - ***********************************/ - - -class BlockchainDB -{ -private: - /********************************************************************* - * private virtual members - *********************************************************************/ - - // tells the subclass to add the block and metadata to storage - virtual void add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const crypto::hash& blk_hash - ) = 0; - - // tells the subclass to remove data about the top block - virtual void remove_block() = 0; - - // tells the subclass to store the transaction and its metadata - virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) = 0; - - // tells the subclass to remove data about a transaction - virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) = 0; - - // tells the subclass to store an output - virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index) = 0; - - // tells the subclass to remove an output - virtual void remove_output(const tx_out& tx_output) = 0; - - // tells the subclass to store a spent key - virtual void add_spent_key(const crypto::key_image& k_image) = 0; - - // tells the subclass to remove a spent key - virtual void remove_spent_key(const crypto::key_image& k_image) = 0; - - - /********************************************************************* - * private concrete members - *********************************************************************/ - // private version of pop_block, for undoing if an add_block goes tits up - void pop_block(); - - // helper function for add_transactions, to add each individual tx - void add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr = NULL); - - // helper function to remove transaction from blockchain - void remove_transaction(const crypto::hash& tx_hash); - - uint64_t num_calls = 0; - uint64_t time_blk_hash = 0; - uint64_t time_add_block1 = 0; - uint64_t time_add_transaction = 0; - - -protected: - - mutable uint64_t time_tx_exists = 0; - uint64_t time_commit1 = 0; - - -public: - - // virtual dtor - virtual ~BlockchainDB() { }; - - // reset profiling stats - void reset_stats(); - - // show profiling stats - void show_stats(); - - // open the db at location <filename>, or create it if there isn't one. - virtual void open(const std::string& filename) = 0; - - // make sure implementation has a create function as well - virtual void create(const std::string& filename) = 0; - - // close and sync the db - virtual void close() = 0; - - // sync the db - virtual void sync() = 0; - - // reset the db -- USE WITH CARE - virtual void reset() = 0; - - // get all files used by this db (if any) - virtual std::vector<std::string> get_filenames() const = 0; - - - // FIXME: these are just for functionality mocking, need to implement - // RAII-friendly and multi-read one-write friendly locking mechanism - // - // acquire db lock - virtual bool lock() = 0; - - // release db lock - virtual void unlock() = 0; - - virtual void batch_start() = 0; - virtual void batch_stop() = 0; - virtual void set_batch_transactions(bool) = 0; - - // adds a block with the given metadata to the top of the blockchain, returns the new height - // NOTE: subclass implementations of this (or the functions it calls) need - // to handle undoing any partially-added blocks in the event of a failure. - virtual uint64_t add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const std::vector<transaction>& txs - ); - - // return true if a block with hash <h> exists in the blockchain - virtual bool block_exists(const crypto::hash& h) const = 0; - - // return block with hash <h> - virtual block get_block(const crypto::hash& h) const = 0; - - // return the height of the block with hash <h> on the blockchain, - // throw if it doesn't exist - virtual uint64_t get_block_height(const crypto::hash& h) const = 0; - - // return header for block with hash <h> - virtual block_header get_block_header(const crypto::hash& h) const = 0; - - // return block at height <height> - virtual block get_block_from_height(const uint64_t& height) const = 0; - - // return timestamp of block at height <height> - virtual uint64_t get_block_timestamp(const uint64_t& height) const = 0; - - // return timestamp of most recent block - virtual uint64_t get_top_block_timestamp() const = 0; - - // return block size of block at height <height> - virtual size_t get_block_size(const uint64_t& height) const = 0; - - // return cumulative difficulty up to and including block at height <height> - virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const = 0; - - // return difficulty of block at height <height> - virtual difficulty_type get_block_difficulty(const uint64_t& height) const = 0; - - // return number of coins generated up to and including block at height <height> - virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0; - - // return hash of block at height <height> - virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const = 0; - - // return vector of blocks in range <h1,h2> of height (inclusively) - virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const = 0; - - // return vector of block hashes in range <h1, h2> of height (inclusively) - virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const = 0; - - // return the hash of the top block on the chain - virtual crypto::hash top_block_hash() const = 0; - - // return the block at the top of the blockchain - virtual block get_top_block() const = 0; - - // return the height of the chain - virtual uint64_t height() const = 0; - - // pops the top block off the blockchain. - // Returns by reference the popped block and its associated transactions - // - // IMPORTANT: - // When a block is popped, the transactions associated with it need to be - // removed, as well as outputs and spent key images associated with - // those transactions. - virtual void pop_block(block& blk, std::vector<transaction>& txs); - - - // return true if a transaction with hash <h> exists - virtual bool tx_exists(const crypto::hash& h) const = 0; - - // return unlock time of tx with hash <h> - virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const = 0; - - // return tx with hash <h> - // throw if no such tx exists - virtual transaction get_tx(const crypto::hash& h) const = 0; - - // returns the total number of transactions in all blocks - virtual uint64_t get_tx_count() const = 0; - - // return list of tx with hashes <hlist>. - // TODO: decide if a missing hash means return empty list - // or just skip that hash - virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const = 0; - - // returns height of block that contains transaction with hash <h> - virtual uint64_t get_tx_block_height(const crypto::hash& h) const = 0; - - // return global output index of a random output of amount <amount> - virtual uint64_t get_random_output(const uint64_t& amount) const = 0; - - // returns the total number of outputs of amount <amount> - virtual uint64_t get_num_outputs(const uint64_t& amount) const = 0; - - // return public key for output with global output amount <amount> and index <index> - virtual crypto::public_key get_output_key(const uint64_t& amount, const uint64_t& index) const = 0; - - // returns the output indexed by <index> in the transaction with hash <h> - virtual tx_out get_output(const crypto::hash& h, const uint64_t& index) const = 0; - - // returns the tx hash associated with an output, referenced by global output index - virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const = 0; - - // returns the transaction-local reference for the output with <amount> at <index> - // return type is pair of tx hash and index - virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const = 0; - - // return a vector of indices corresponding to the global output index for - // each output in the transaction with hash <h> - virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const = 0; - // return a vector of indices corresponding to the amount output index for - // each output in the transaction with hash <h> - virtual std::vector<uint64_t> get_tx_amount_output_indices(const crypto::hash& h) const = 0; - - // returns true if key image <img> is present in spent key images storage - virtual bool has_key_image(const crypto::key_image& img) const = 0; - -}; // class BlockchainDB - - -} // namespace cryptonote - -#endif // BLOCKCHAIN_DB_H |