diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp index 1f583917..d104c2c3 100644 --- a/src/blockchain_db/berkeleydb/db_bdb.cpp +++ b/src/blockchain_db/berkeleydb/db_bdb.cpp @@ -1,20 +1,20 @@ // 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 @@ -43,46 +43,58 @@ namespace template inline void throw0(const T &e) { - LOG_PRINT_L0(e.what()); - throw e; + LOG_PRINT_L0(e.what()); + throw e; } template inline void throw1(const T &e) { - LOG_PRINT_L1(e.what()); - throw 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 bdb_cur { - bdb_cur(DbTxn* txn, Db* dbi) - { - if (dbi->cursor(txn, &m_cur, 0)) - throw0(cryptonote::DB_ERROR("Error opening db cursor")); - done = false; - } - - ~bdb_cur() { close(); } - - operator Dbc*() { return m_cur; } - operator Dbc**() { return &m_cur; } - Dbc* operator->() { return m_cur; } - - void close() - { - if (!done) + bdb_cur(DbTxn* txn, Db* dbi) { - m_cur->close(); - done = true; + if (dbi->cursor(txn, &m_cur, 0)) + throw0(cryptonote::DB_ERROR("Error opening db cursor")); + done = false; + } + + ~bdb_cur() + { + close(); + } + + operator Dbc*() + { + return m_cur; + } + operator Dbc**() + { + return &m_cur; + } + Dbc* operator->() + { + return m_cur; + } + + void close() + { + if (!done) + { + m_cur->close(); + done = true; + } } - } private: - Dbc* m_cur; - bool done; + Dbc* m_cur; + bool done; }; const char* const BDB_BLOCKS = "blocks"; @@ -105,67 +117,67 @@ const char* const BDB_OUTPUT_KEYS = "output_keys"; const char* const BDB_SPENT_KEYS = "spent_keys"; -const int BUFFER_LENGTH = 32 * 1024 * 1024; +const int BUFFER_LENGTH = 4 * 1024 * 1024; template struct Dbt_copy: public Dbt { - Dbt_copy(const T &t): t_copy(t) - { - init(); - } + Dbt_copy(const T &t): t_copy(t) + { + init(); + } - Dbt_copy() - { - init(); - } + Dbt_copy() + { + init(); + } - void init() - { - set_data(&t_copy); - set_size(sizeof(T)); - set_ulen(sizeof(T)); - set_flags(DB_DBT_USERMEM); - } + void init() + { + set_data(&t_copy); + set_size(sizeof(T)); + set_ulen(sizeof(T)); + set_flags(DB_DBT_USERMEM); + } - operator T() - { - return t_copy; - } + operator T() + { + return t_copy; + } private: - T t_copy; + T t_copy; }; template<> struct Dbt_copy: public Dbt { - Dbt_copy(const cryptonote::blobdata &bd) : m_data(new char[bd.size()]) - { - memcpy(m_data.get(), bd.data(), bd.size()); - set_data(m_data.get()); - set_size(bd.size()); - set_ulen(bd.size()); - set_flags(DB_DBT_USERMEM); - } + Dbt_copy(const cryptonote::blobdata &bd) : m_data(new char[bd.size()]) + { + memcpy(m_data.get(), bd.data(), bd.size()); + set_data(m_data.get()); + set_size(bd.size()); + set_ulen(bd.size()); + set_flags(DB_DBT_USERMEM); + } private: - std::unique_ptr m_data; + std::unique_ptr m_data; }; struct Dbt_safe : public Dbt { - Dbt_safe() - { - set_data(NULL); - set_flags(DB_DBT_MALLOC); - } - ~Dbt_safe() - { - void* buf = get_data(); - if (buf != NULL) + Dbt_safe() { - free(buf); + set_data(NULL); + set_flags(DB_DBT_MALLOC); + } + ~Dbt_safe() + { + void* buf = get_data(); + if (buf != NULL) + { + free(buf); + } } - } }; } // anonymous namespace @@ -174,1236 +186,1236 @@ namespace cryptonote { void BlockchainBDB::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 - ) + , const size_t& block_size + , const difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , const crypto::hash& blk_hash + ) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy val_h(blk_hash); - if (m_block_heights->exists(*m_write_txn, &val_h, 0) == 0) - throw1(BLOCK_EXISTS("Attempting to add block that's already in the db")); + Dbt_copy val_h(blk_hash); + if (m_block_heights->exists(*m_write_txn, &val_h, 0) == 0) + throw1(BLOCK_EXISTS("Attempting to add block that's already in the db")); - if (m_height > 0) - { - Dbt_copy parent_key(blk.prev_id); - Dbt_copy parent_h; - if (m_block_heights->get(*m_write_txn, &parent_key, &parent_h, 0)) + if (m_height > 0) { - 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")); + Dbt_copy parent_key(blk.prev_id); + Dbt_copy parent_h; + if (m_block_heights->get(*m_write_txn, &parent_key, &parent_h, 0)) + { + 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")); + } + uint32_t parent_height = parent_h; + if (parent_height != m_height) + throw0(BLOCK_PARENT_DNE("Top block is not new block's parent")); } - uint32_t parent_height = parent_h; - if (parent_height != m_height) - throw0(BLOCK_PARENT_DNE("Top block is not new block's parent")); - } - Dbt_copy key(m_height + 1); + Dbt_copy key(m_height + 1); - Dbt_copy blob(block_to_blob(blk)); - auto res = m_blocks->put(*m_write_txn, &key, &blob, 0); - if (res) - throw0(DB_ERROR("Failed to add block blob to db transaction.")); + Dbt_copy blob(block_to_blob(blk)); + auto res = m_blocks->put(*m_write_txn, &key, &blob, 0); + if (res) + throw0(DB_ERROR("Failed to add block blob to db transaction.")); - Dbt_copy sz(block_size); - if (m_block_sizes->put(*m_write_txn, &key, &sz, 0)) - throw0(DB_ERROR("Failed to add block size to db transaction.")); + Dbt_copy sz(block_size); + if (m_block_sizes->put(*m_write_txn, &key, &sz, 0)) + throw0(DB_ERROR("Failed to add block size to db transaction.")); - Dbt_copy ts(blk.timestamp); - if (m_block_timestamps->put(*m_write_txn, &key, &ts, 0)) - throw0(DB_ERROR("Failed to add block timestamp to db transaction.")); + Dbt_copy ts(blk.timestamp); + if (m_block_timestamps->put(*m_write_txn, &key, &ts, 0)) + throw0(DB_ERROR("Failed to add block timestamp to db transaction.")); - Dbt_copy diff(cumulative_difficulty); - if (m_block_diffs->put(*m_write_txn, &key, &diff, 0)) - throw0(DB_ERROR("Failed to add block cumulative difficulty to db transaction.")); + Dbt_copy diff(cumulative_difficulty); + if (m_block_diffs->put(*m_write_txn, &key, &diff, 0)) + throw0(DB_ERROR("Failed to add block cumulative difficulty to db transaction.")); - Dbt_copy coinsgen(coins_generated); - if (m_block_coins->put(*m_write_txn, &key, &coinsgen, 0)) - throw0(DB_ERROR("Failed to add block total generated coins to db transaction.")); + Dbt_copy coinsgen(coins_generated); + if (m_block_coins->put(*m_write_txn, &key, &coinsgen, 0)) + throw0(DB_ERROR("Failed to add block total generated coins to db transaction.")); - if (m_block_heights->put(*m_write_txn, &val_h, &key, 0)) - throw0(DB_ERROR("Failed to add block height by hash to db transaction.")); + if (m_block_heights->put(*m_write_txn, &val_h, &key, 0)) + throw0(DB_ERROR("Failed to add block height by hash to db transaction.")); - if (m_block_hashes->put(*m_write_txn, &key, &val_h, 0)) - throw0(DB_ERROR("Failed to add block hash to db transaction.")); + if (m_block_hashes->put(*m_write_txn, &key, &val_h, 0)) + throw0(DB_ERROR("Failed to add block hash to db transaction.")); } void BlockchainBDB::remove_block() { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - if (m_height == 0) - throw0(BLOCK_DNE ("Attempting to remove block from an empty blockchain")); + if (m_height == 0) + throw0(BLOCK_DNE ("Attempting to remove block from an empty blockchain")); - Dbt_copy k(m_height); - Dbt_copy h; - if (m_block_hashes->get(*m_write_txn, &k, &h, 0)) - throw1(BLOCK_DNE("Attempting to remove block that's not in the db")); + Dbt_copy k(m_height); + Dbt_copy h; + if (m_block_hashes->get(*m_write_txn, &k, &h, 0)) + throw1(BLOCK_DNE("Attempting to remove block that's not in the db")); - if (m_blocks->del(*m_write_txn, &k, 0)) - throw1(DB_ERROR("Failed to add removal of block to db transaction")); + if (m_blocks->del(*m_write_txn, &k, 0)) + throw1(DB_ERROR("Failed to add removal of block to db transaction")); - if (m_block_sizes->del(*m_write_txn, &k, 0)) - throw1(DB_ERROR("Failed to add removal of block size to db transaction")); + if (m_block_sizes->del(*m_write_txn, &k, 0)) + throw1(DB_ERROR("Failed to add removal of block size to db transaction")); - if (m_block_diffs->del(*m_write_txn, &k, 0)) - throw1(DB_ERROR("Failed to add removal of block cumulative difficulty to db transaction")); + if (m_block_diffs->del(*m_write_txn, &k, 0)) + throw1(DB_ERROR("Failed to add removal of block cumulative difficulty to db transaction")); - if (m_block_coins->del(*m_write_txn, &k, 0)) - throw1(DB_ERROR("Failed to add removal of block total generated coins to db transaction")); + if (m_block_coins->del(*m_write_txn, &k, 0)) + throw1(DB_ERROR("Failed to add removal of block total generated coins to db transaction")); - if (m_block_timestamps->del(*m_write_txn, &k, 0)) - throw1(DB_ERROR("Failed to add removal of block timestamp to db transaction")); + if (m_block_timestamps->del(*m_write_txn, &k, 0)) + throw1(DB_ERROR("Failed to add removal of block timestamp to db transaction")); - if (m_block_heights->del(*m_write_txn, &h, 0)) - throw1(DB_ERROR("Failed to add removal of block height by hash to db transaction")); + if (m_block_heights->del(*m_write_txn, &h, 0)) + throw1(DB_ERROR("Failed to add removal of block height by hash to db transaction")); - if (m_block_hashes->del(*m_write_txn, &k, 0)) - throw1(DB_ERROR("Failed to add removal of block hash to db transaction")); + if (m_block_hashes->del(*m_write_txn, &k, 0)) + throw1(DB_ERROR("Failed to add removal of block hash to db transaction")); } void BlockchainBDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy val_h(tx_hash); + Dbt_copy val_h(tx_hash); - if (m_txs->exists(*m_write_txn, &val_h, 0) == 0) - throw1(TX_EXISTS("Attempting to add transaction that's already in the db")); + if (m_txs->exists(*m_write_txn, &val_h, 0) == 0) + throw1(TX_EXISTS("Attempting to add transaction that's already in the db")); - Dbt_copy blob(tx_to_blob(tx)); - if (m_txs->put(*m_write_txn, &val_h, &blob, 0)) - throw0(DB_ERROR("Failed to add tx blob to db transaction")); + Dbt_copy blob(tx_to_blob(tx)); + if (m_txs->put(*m_write_txn, &val_h, &blob, 0)) + throw0(DB_ERROR("Failed to add tx blob to db transaction")); - Dbt_copy height(m_height + 1); - if (m_tx_heights->put(*m_write_txn, &val_h, &height, 0)) - throw0(DB_ERROR("Failed to add tx block height to db transaction")); + Dbt_copy height(m_height + 1); + if (m_tx_heights->put(*m_write_txn, &val_h, &height, 0)) + throw0(DB_ERROR("Failed to add tx block height to db transaction")); - Dbt_copy unlock_time(tx.unlock_time); - if (m_tx_unlocks->put(*m_write_txn, &val_h, &unlock_time, 0)) - throw0(DB_ERROR("Failed to add tx unlock time to db transaction")); + Dbt_copy unlock_time(tx.unlock_time); + if (m_tx_unlocks->put(*m_write_txn, &val_h, &unlock_time, 0)) + throw0(DB_ERROR("Failed to add tx unlock time to db transaction")); } void BlockchainBDB::remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy val_h(tx_hash); - if (m_txs->exists(*m_write_txn, &val_h, 0)) - throw1(TX_DNE("Attempting to remove transaction that isn't in the db")); + Dbt_copy val_h(tx_hash); + if (m_txs->exists(*m_write_txn, &val_h, 0)) + throw1(TX_DNE("Attempting to remove transaction that isn't in the db")); - if (m_txs->del(*m_write_txn, &val_h, 0)) - throw1(DB_ERROR("Failed to add removal of tx to db transaction")); - if (m_tx_unlocks->del(*m_write_txn, &val_h, 0)) - throw1(DB_ERROR("Failed to add removal of tx unlock time to db transaction")); - if (m_tx_heights->del(*m_write_txn, &val_h, 0)) - throw1(DB_ERROR("Failed to add removal of tx block height to db transaction")); + if (m_txs->del(*m_write_txn, &val_h, 0)) + throw1(DB_ERROR("Failed to add removal of tx to db transaction")); + if (m_tx_unlocks->del(*m_write_txn, &val_h, 0)) + throw1(DB_ERROR("Failed to add removal of tx unlock time to db transaction")); + if (m_tx_heights->del(*m_write_txn, &val_h, 0)) + throw1(DB_ERROR("Failed to add removal of tx block height to db transaction")); - remove_tx_outputs(tx_hash, tx); + remove_tx_outputs(tx_hash, tx); - if (m_tx_outputs->del(*m_write_txn, &val_h, 0)) - throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction")); + if (m_tx_outputs->del(*m_write_txn, &val_h, 0)) + throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction")); } void BlockchainBDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy k(m_num_outputs + 1); - Dbt_copy v(tx_hash); + Dbt_copy k(m_num_outputs + 1); + Dbt_copy v(tx_hash); - if (m_output_txs->put(*m_write_txn, &k, &v, 0)) - throw0(DB_ERROR("Failed to add output tx hash to db transaction")); - if (m_tx_outputs->put(*m_write_txn, &v, &k, 0)) - throw0(DB_ERROR("Failed to add tx output index to db transaction")); + if (m_output_txs->put(*m_write_txn, &k, &v, 0)) + throw0(DB_ERROR("Failed to add output tx hash to db transaction")); + if (m_tx_outputs->put(*m_write_txn, &v, &k, 0)) + throw0(DB_ERROR("Failed to add tx output index to db transaction")); - Dbt_copy val_local_index(local_index); - if (m_output_indices->put(*m_write_txn, &k, &val_local_index, 0)) - throw0(DB_ERROR("Failed to add tx output index to db transaction")); + Dbt_copy val_local_index(local_index); + if (m_output_indices->put(*m_write_txn, &k, &val_local_index, 0)) + throw0(DB_ERROR("Failed to add tx output index to db transaction")); - Dbt_copy val_amount(tx_output.amount); - if (m_output_amounts->put(*m_write_txn, &val_amount, &k, 0)) - throw0(DB_ERROR("Failed to add output amount to db transaction.")); + Dbt_copy val_amount(tx_output.amount); + if (m_output_amounts->put(*m_write_txn, &val_amount, &k, 0)) + throw0(DB_ERROR("Failed to add output amount to db transaction.")); - if (tx_output.target.type() == typeid(txout_to_key)) - { - Dbt_copy val_pubkey(boost::get(tx_output.target).key); - if (m_output_keys->put(*m_write_txn, &k, &val_pubkey, 0)) - throw0(DB_ERROR("Failed to add output pubkey to db transaction")); - } + if (tx_output.target.type() == typeid(txout_to_key)) + { + Dbt_copy val_pubkey(boost::get(tx_output.target).key); + if (m_output_keys->put(*m_write_txn, &k, &val_pubkey, 0)) + throw0(DB_ERROR("Failed to add output pubkey to db transaction")); + } - m_num_outputs++; + m_num_outputs++; } void BlockchainBDB::remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); - bdb_cur cur(*m_write_txn, m_tx_outputs); + bdb_cur cur(*m_write_txn, m_tx_outputs); - Dbt_copy k(tx_hash); - Dbt_copy v; + Dbt_copy k(tx_hash); + Dbt_copy v; - auto result = cur->get(&k, &v, DB_SET); - if (result == DB_NOTFOUND) - { - throw0(DB_ERROR("Attempting to remove a tx's outputs, but none found.")); - } - else if (result) - { - throw0(DB_ERROR("DB error attempting to get an output")); - } - else - { - db_recno_t num_elems = 0; - cur->count(&num_elems, 0); - - for (uint64_t i = 0; i < num_elems; ++i) + auto result = cur->get(&k, &v, DB_SET); + if (result == DB_NOTFOUND) { - const tx_out tx_output = tx.vout[i]; - remove_output(v, tx_output.amount); - if (i < num_elems - 1) - { - cur->get(&k, &v, DB_NEXT_DUP); - } + throw0(DB_ERROR("Attempting to remove a tx's outputs, but none found.")); } - } + else if (result) + { + throw0(DB_ERROR("DB error attempting to get an output")); + } + else + { + db_recno_t num_elems = 0; + cur->count(&num_elems, 0); - cur.close(); + for (uint64_t i = 0; i < num_elems; ++i) + { + const tx_out tx_output = tx.vout[i]; + remove_output(v, tx_output.amount); + if (i < num_elems - 1) + { + cur->get(&k, &v, DB_NEXT_DUP); + } + } + } + + cur.close(); } // TODO: probably remove this function void BlockchainBDB::remove_output(const tx_out& tx_output) { - LOG_PRINT_L3("BlockchainBDB::" << __func__ << " (unused version - does nothing)"); - return; + LOG_PRINT_L3("BlockchainBDB::" << __func__ << " (unused version - does nothing)"); + return; } void BlockchainBDB::remove_output(const uint64_t& out_index, const uint64_t amount) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy k(out_index); + Dbt_copy k(out_index); - auto result = m_output_indices->del(*m_write_txn, &k, 0); - if (result == DB_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")); - } + auto result = m_output_indices->del(*m_write_txn, &k, 0); + if (result == DB_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 = m_output_txs->del(*m_write_txn, &k, 0); - // if (result != 0 && result != DB_NOTFOUND) - // throw1(DB_ERROR("Error adding removal of output tx hash to db transaction")); - if (result == DB_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 = m_output_txs->del(*m_write_txn, &k, 0); + // if (result != 0 && result != DB_NOTFOUND) + // throw1(DB_ERROR("Error adding removal of output tx hash to db transaction")); + if (result == DB_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 = m_output_keys->del(*m_write_txn, &k, 0); - if (result == DB_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")); + result = m_output_keys->del(*m_write_txn, &k, 0); + if (result == DB_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); + remove_amount_output_index(amount, out_index); - m_num_outputs--; + m_num_outputs--; } void BlockchainBDB::remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_cur cur(*m_write_txn, m_output_amounts); + bdb_cur cur(*m_write_txn, m_output_amounts); - Dbt_copy k(amount); - Dbt_copy v; + Dbt_copy k(amount); + Dbt_copy v; - auto result = cur->get(&k, &v, DB_SET); - if (result == DB_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")); + auto result = cur->get(&k, &v, DB_SET); + if (result == DB_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")); - db_recno_t num_elems = 0; - cur->count(&num_elems, 0); + db_recno_t num_elems = 0; + cur->count(&num_elems, 0); - uint64_t amount_output_index = 0; - uint64_t goi = 0; - bool found_index = false; - for (uint64_t i = 0; i < num_elems; ++i) - { - goi = v; - if (goi == global_output_index) + uint64_t amount_output_index = 0; + uint64_t goi = 0; + bool found_index = false; + for (uint64_t i = 0; i < num_elems; ++i) { - amount_output_index = i; - found_index = true; - break; + goi = v; + if (goi == global_output_index) + { + amount_output_index = i; + found_index = true; + break; + } + cur->get(&k, &v, DB_NEXT_DUP); } - cur->get(&k, &v, DB_NEXT_DUP); - } - if (found_index) - { - // found the amount output index - // now delete it - result = cur->del(0); - if (result) - throw0(DB_ERROR(std::string("Error deleting amount output index ").append(boost::lexical_cast(amount_output_index)).c_str())); - } - else - { - // not found - throw1(OUTPUT_DNE("Failed to find amount output index")); - } - cur.close(); + if (found_index) + { + // found the amount output index + // now delete it + result = cur->del(0); + if (result) + throw0(DB_ERROR(std::string("Error deleting amount output index ").append(boost::lexical_cast(amount_output_index)).c_str())); + } + else + { + // not found + throw1(OUTPUT_DNE("Failed to find amount output index")); + } + cur.close(); } void BlockchainBDB::add_spent_key(const crypto::key_image& k_image) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy val_key(k_image); - if (m_spent_keys->exists(*m_write_txn, &val_key, 0) == 0) - throw1(KEY_IMAGE_EXISTS("Attempting to add spent key image that's already in the db")); + Dbt_copy val_key(k_image); + if (m_spent_keys->exists(*m_write_txn, &val_key, 0) == 0) + throw1(KEY_IMAGE_EXISTS("Attempting to add spent key image that's already in the db")); - Dbt_copy val('\0'); - if (m_spent_keys->put(*m_write_txn, &val_key, &val, 0)) - throw1(DB_ERROR("Error adding spent key image to db transaction.")); + Dbt_copy val('\0'); + if (m_spent_keys->put(*m_write_txn, &val_key, &val, 0)) + throw1(DB_ERROR("Error adding spent key image to db transaction.")); } void BlockchainBDB::remove_spent_key(const crypto::key_image& k_image) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - Dbt_copy k(k_image); - auto result = m_spent_keys->del(*m_write_txn, &k, 0); - if (result != 0 && result != DB_NOTFOUND) - throw1(DB_ERROR("Error adding removal of key image to db transaction")); + Dbt_copy k(k_image); + auto result = m_spent_keys->del(*m_write_txn, &k, 0); + if (result != 0 && result != DB_NOTFOUND) + throw1(DB_ERROR("Error adding removal of key image to db transaction")); } blobdata BlockchainBDB::output_to_blob(const tx_out& output) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - blobdata b; - if (!t_serializable_object_to_blob(output, b)) - throw1(DB_ERROR("Error serializing output to blob")); - return b; + LOG_PRINT_L3("BlockchainBDB::" << __func__); + blobdata b; + if (!t_serializable_object_to_blob(output, b)) + throw1(DB_ERROR("Error serializing output to blob")); + return b; } tx_out BlockchainBDB::output_from_blob(const blobdata& blob) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - std::stringstream ss; - ss << blob; - binary_archive ba(ss); - tx_out o; + LOG_PRINT_L3("BlockchainBDB::" << __func__); + std::stringstream ss; + ss << blob; + binary_archive ba(ss); + tx_out o; - if (!(::serialization::serialize(ba, o))) - throw1(DB_ERROR("Error deserializing tx output blob")); + if (!(::serialization::serialize(ba, o))) + throw1(DB_ERROR("Error deserializing tx output blob")); - return o; + return o; } uint64_t BlockchainBDB::get_output_global_index(const uint64_t& amount, const uint64_t& index) const { - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); - bdb_cur cur(txn, m_output_amounts); + bdb_cur cur(txn, m_output_amounts); - Dbt_copy k(amount); - Dbt_copy v; + Dbt_copy k(amount); + Dbt_copy v; - auto result = cur->get(&k, &v, DB_SET); - if (result == DB_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")); + auto result = cur->get(&k, &v, DB_SET); + if (result == DB_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")); - db_recno_t num_elems; - cur->count(&num_elems, 0); + db_recno_t num_elems; + cur->count(&num_elems, 0); - if (num_elems <= index) - throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); + if (num_elems <= index) + throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); - for (uint64_t i = 0; i < index; ++i) - { - cur->get(&k, &v, DB_NEXT_DUP); - } + for (uint64_t i = 0; i < index; ++i) + { + cur->get(&k, &v, DB_NEXT_DUP); + } - uint64_t glob_index = v; + uint64_t glob_index = v; - cur.close(); + cur.close(); - txn.commit(); + txn.commit(); - return glob_index; + return glob_index; } void BlockchainBDB::check_open() const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - if (!m_open) - throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + if (!m_open) + throw0(DB_ERROR("DB operation attempted on a not-open DB instance")); } BlockchainBDB::~BlockchainBDB() { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); if(m_buffer != NULL) free(m_buffer); m_buffer = NULL; - if (m_open) - { - close(); - } + if (m_open) + { + close(); + } } BlockchainBDB::BlockchainBDB(bool batch_transactions) { m_buffer = malloc(BUFFER_LENGTH); - LOG_PRINT_L3("BlockchainBDB::" << __func__); - // initialize folder to something "safe" just in case - // someone accidentally misuses this class... - m_folder = "thishsouldnotexistbecauseitisgibberish"; - m_open = false; + LOG_PRINT_L3("BlockchainBDB::" << __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_height = 0; + m_batch_transactions = batch_transactions; + m_write_txn = nullptr; + m_height = 0; } void BlockchainBDB::open(const std::string& filename, const int db_flags) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); - if (m_open) - throw0(DB_OPEN_FAILURE("Attempted to open db, but it's already open")); + 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("DB 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())); - } + boost::filesystem::path direc(filename); + if (boost::filesystem::exists(direc)) + { + if (!boost::filesystem::is_directory(direc)) + throw0(DB_OPEN_FAILURE("DB 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())); + } - m_folder = filename; + m_folder = filename; - try - { + try + { - //Create BerkeleyDB environment - m_env = new DbEnv(0); // no flags needed for DbEnv + //Create BerkeleyDB environment + m_env = new DbEnv(0); // no flags needed for DbEnv - uint32_t db_env_open_flags = DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK - | DB_INIT_LOG | DB_INIT_TXN | DB_RECOVER - | DB_THREAD; + uint32_t db_env_open_flags = DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK + | DB_INIT_LOG | DB_INIT_TXN | DB_RECOVER + | DB_THREAD; - // last parameter left 0, files will be created with default rw access - m_env->open(filename.c_str(), db_env_open_flags, 0); + // last parameter left 0, files will be created with default rw access + m_env->open(filename.c_str(), db_env_open_flags, 0); + + // begin transaction to init dbs + bdb_txn_safe txn; + m_env->txn_begin(NULL, txn, 0); + + // create Dbs in the environment + m_blocks = new Db(m_env, 0); + m_block_heights = new Db(m_env, 0); + m_block_hashes = new Db(m_env, 0); + m_block_timestamps = new Db(m_env, 0); + m_block_sizes = new Db(m_env, 0); + m_block_diffs = new Db(m_env, 0); + m_block_coins = new Db(m_env, 0); + + m_txs = new Db(m_env, 0); + m_tx_unlocks = new Db(m_env, 0); + m_tx_heights = new Db(m_env, 0); + m_tx_outputs = new Db(m_env, 0); + + m_output_txs = new Db(m_env, 0); + m_output_indices = new Db(m_env, 0); + m_output_amounts = new Db(m_env, 0); + m_output_keys = new Db(m_env, 0); + + m_spent_keys = new Db(m_env, 0); + + // Tell DB about Dbs that need duplicate support + // Note: no need to tell about sorting, + // as the default is insertion order, which we want + m_tx_outputs->set_flags(DB_DUP); + m_output_amounts->set_flags(DB_DUP); + + // Tell DB about fixed-size values. + m_block_hashes->set_re_len(sizeof(crypto::hash)); + m_block_timestamps->set_re_len(sizeof(uint64_t)); + m_block_sizes->set_re_len(sizeof(size_t)); // should really store block size as uint64_t... + m_block_diffs->set_re_len(sizeof(difficulty_type)); + m_block_coins->set_re_len(sizeof(uint64_t)); + + m_output_txs->set_re_len(sizeof(crypto::hash)); + m_output_indices->set_re_len(sizeof(uint64_t)); + m_output_keys->set_re_len(sizeof(crypto::public_key)); + + //TODO: Find out if we need to do Db::set_flags(DB_RENUMBER) + // for the RECNO databases. We shouldn't as we're only + // inserting/removing from the end, but we'll see. + + // open Dbs in the environment + // m_tx_outputs and m_output_amounts must be DB_HASH or DB_BTREE + // because they need duplicate entry support. The rest are DB_RECNO, + // as it seems that will be the most performant choice. + m_blocks->open(txn, BDB_BLOCKS, NULL, DB_RECNO, DB_CREATE, 0); + + m_block_timestamps->open(txn, BDB_BLOCK_TIMESTAMPS, NULL, DB_RECNO, DB_CREATE, 0); + m_block_heights->open(txn, BDB_BLOCK_HEIGHTS, NULL, DB_HASH, DB_CREATE, 0); + m_block_hashes->open(txn, BDB_BLOCK_HASHES, NULL, DB_RECNO, DB_CREATE, 0); + m_block_sizes->open(txn, BDB_BLOCK_SIZES, NULL, DB_RECNO, DB_CREATE, 0); + m_block_diffs->open(txn, BDB_BLOCK_DIFFS, NULL, DB_RECNO, DB_CREATE, 0); + m_block_coins->open(txn, BDB_BLOCK_COINS, NULL, DB_RECNO, DB_CREATE, 0); + + m_txs->open(txn, BDB_TXS, NULL, DB_HASH, DB_CREATE, 0); + m_tx_unlocks->open(txn, BDB_TX_UNLOCKS, NULL, DB_HASH, DB_CREATE, 0); + m_tx_heights->open(txn, BDB_TX_HEIGHTS, NULL, DB_HASH, DB_CREATE, 0); + m_tx_outputs->open(txn, BDB_TX_OUTPUTS, NULL, DB_HASH, DB_CREATE, 0); + + m_output_txs->open(txn, BDB_OUTPUT_TXS, NULL, DB_RECNO, DB_CREATE, 0); + m_output_indices->open(txn, BDB_OUTPUT_INDICES, NULL, DB_RECNO, DB_CREATE, 0); + m_output_amounts->open(txn, BDB_OUTPUT_AMOUNTS, NULL, DB_HASH, DB_CREATE, 0); + m_output_keys->open(txn, BDB_OUTPUT_KEYS, NULL, DB_RECNO, DB_CREATE, 0); + + m_spent_keys->open(txn, BDB_SPENT_KEYS, NULL, DB_HASH, DB_CREATE, 0); + + DB_BTREE_STAT* stats; + + // DB_FAST_STAT can apparently cause an incorrect number of records + // to be returned. The flag should be set to 0 instead if this proves + // to be the case. + m_blocks->stat(txn, &stats, DB_FAST_STAT); + m_height = stats->bt_nkeys; + delete stats; + + // see above comment about DB_FAST_STAT + m_output_indices->stat(txn, &stats, DB_FAST_STAT); + m_num_outputs = stats->bt_nkeys; + delete stats; + + txn.commit(); + } + catch (const std::exception& e) + { + throw0(DB_OPEN_FAILURE(e.what())); + } + + m_open = true; +} + +void BlockchainBDB::close() +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + this->sync(); + + // FIXME: not yet thread safe!!! Use with care. + m_open = false; + m_env->close(DB_FORCESYNC); +} + +void BlockchainBDB::sync() +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + try + { + m_blocks->sync(0); + m_block_heights->sync(0); + m_block_hashes->sync(0); + m_block_timestamps->sync(0); + m_block_sizes->sync(0); + m_block_diffs->sync(0); + m_block_coins->sync(0); + + m_txs->sync(0); + m_tx_unlocks->sync(0); + m_tx_heights->sync(0); + m_tx_outputs->sync(0); + + m_output_txs->sync(0); + m_output_indices->sync(0); + m_output_amounts->sync(0); + m_output_keys->sync(0); + + m_spent_keys->sync(0); + } + catch (const std::exception& e) + { + throw0(DB_ERROR(std::string("Failed to sync database: ").append(e.what()).c_str())); + } +} + +void BlockchainBDB::reset() +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + // TODO: this +} + +std::vector BlockchainBDB::get_filenames() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + std::vector filenames; + + char *fname, *dbname; + const char **pfname, **pdbname; + + pfname = (const char **)&fname; + pdbname = (const char **)&dbname; + + m_blocks->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_block_heights->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_block_hashes->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_block_timestamps->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_block_sizes->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_block_diffs->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_block_coins->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_txs->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_tx_unlocks->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_tx_heights->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_tx_outputs->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_output_txs->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_output_indices->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_output_amounts->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_output_keys->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + m_spent_keys->get_dbname(pfname, pdbname); + filenames.push_back(fname); + + std::vector full_paths; + +for (auto& filename : filenames) + { + boost::filesystem::path p(m_folder); + p /= filename; + full_paths.push_back(p.string()); + } + + return full_paths; +} + +std::string BlockchainBDB::get_db_name() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + + return std::string("BerkeleyDB"); +} + +// TODO: this? +bool BlockchainBDB::lock() +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + return false; +} + +// TODO: this? +void BlockchainBDB::unlock() +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); +} + +bool BlockchainBDB::block_exists(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - // begin transaction to init dbs bdb_txn_safe txn; - m_env->txn_begin(NULL, txn, 0); + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); - // create Dbs in the environment - m_blocks = new Db(m_env, 0); - m_block_heights = new Db(m_env, 0); - m_block_hashes = new Db(m_env, 0); - m_block_timestamps = new Db(m_env, 0); - m_block_sizes = new Db(m_env, 0); - m_block_diffs = new Db(m_env, 0); - m_block_coins = new Db(m_env, 0); + Dbt_copy key(h); - m_txs = new Db(m_env, 0); - m_tx_unlocks = new Db(m_env, 0); - m_tx_heights = new Db(m_env, 0); - m_tx_outputs = new Db(m_env, 0); + auto get_result = m_block_heights->exists(txn, &key, 0); + if (get_result == DB_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")); - m_output_txs = new Db(m_env, 0); - m_output_indices = new Db(m_env, 0); - m_output_amounts = new Db(m_env, 0); - m_output_keys = new Db(m_env, 0); + txn.commit(); + return true; +} - m_spent_keys = new Db(m_env, 0); +block BlockchainBDB::get_block(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - // Tell DB about Dbs that need duplicate support - // Note: no need to tell about sorting, - // as the default is insertion order, which we want - m_tx_outputs->set_flags(DB_DUP); - m_output_amounts->set_flags(DB_DUP); + return get_block_from_height(get_block_height(h)); +} - // Tell DB about fixed-size values. - m_block_hashes->set_re_len(sizeof(crypto::hash)); - m_block_timestamps->set_re_len(sizeof(uint64_t)); - m_block_sizes->set_re_len(sizeof(size_t)); // should really store block size as uint64_t... - m_block_diffs->set_re_len(sizeof(difficulty_type)); - m_block_coins->set_re_len(sizeof(uint64_t)); +uint64_t BlockchainBDB::get_block_height(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - m_output_txs->set_re_len(sizeof(crypto::hash)); - m_output_indices->set_re_len(sizeof(uint64_t)); - m_output_keys->set_re_len(sizeof(crypto::public_key)); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); - //TODO: Find out if we need to do Db::set_flags(DB_RENUMBER) - // for the RECNO databases. We shouldn't as we're only - // inserting/removing from the end, but we'll see. + Dbt_copy key(h); + Dbt_copy result; - // open Dbs in the environment - // m_tx_outputs and m_output_amounts must be DB_HASH or DB_BTREE - // because they need duplicate entry support. The rest are DB_RECNO, - // as it seems that will be the most performant choice. - m_blocks->open(txn, BDB_BLOCKS, NULL, DB_RECNO, DB_CREATE, 0); + auto get_result = m_block_heights->get(txn, &key, &result, 0); + if (get_result == DB_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")); - m_block_timestamps->open(txn, BDB_BLOCK_TIMESTAMPS, NULL, DB_RECNO, DB_CREATE, 0); - m_block_heights->open(txn, BDB_BLOCK_HEIGHTS, NULL, DB_HASH, DB_CREATE, 0); - m_block_hashes->open(txn, BDB_BLOCK_HASHES, NULL, DB_RECNO, DB_CREATE, 0); - m_block_sizes->open(txn, BDB_BLOCK_SIZES, NULL, DB_RECNO, DB_CREATE, 0); - m_block_diffs->open(txn, BDB_BLOCK_DIFFS, NULL, DB_RECNO, DB_CREATE, 0); - m_block_coins->open(txn, BDB_BLOCK_COINS, NULL, DB_RECNO, DB_CREATE, 0); + txn.commit(); - m_txs->open(txn, BDB_TXS, NULL, DB_HASH, DB_CREATE, 0); - m_tx_unlocks->open(txn, BDB_TX_UNLOCKS, NULL, DB_HASH, DB_CREATE, 0); - m_tx_heights->open(txn, BDB_TX_HEIGHTS, NULL, DB_HASH, DB_CREATE, 0); - m_tx_outputs->open(txn, BDB_TX_OUTPUTS, NULL, DB_HASH, DB_CREATE, 0); + return (uint64_t)result - 1; +} - m_output_txs->open(txn, BDB_OUTPUT_TXS, NULL, DB_RECNO, DB_CREATE, 0); - m_output_indices->open(txn, BDB_OUTPUT_INDICES, NULL, DB_RECNO, DB_CREATE, 0); - m_output_amounts->open(txn, BDB_OUTPUT_AMOUNTS, NULL, DB_HASH, DB_CREATE, 0); - m_output_keys->open(txn, BDB_OUTPUT_KEYS, NULL, DB_RECNO, DB_CREATE, 0); +block_header BlockchainBDB::get_block_header(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - m_spent_keys->open(txn, BDB_SPENT_KEYS, NULL, DB_HASH, DB_CREATE, 0); + // block_header object is automatically cast from block object + return get_block(h); +} + +block BlockchainBDB::get_block_from_height(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(height + 1); + Dbt_safe result; + auto get_result = m_blocks->get(txn, &key, &result, 0); + if (get_result == DB_NOTFOUND) + { + throw0(DB_ERROR(std::string("Attempt to get block from height ").append(boost::lexical_cast(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(result.get_data()), result.get_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 BlockchainBDB::get_block_timestamp(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(height + 1); + Dbt_copy result; + auto get_result = m_block_timestamps->get(txn, &key, &result, 0); + if (get_result == DB_NOTFOUND) + { + throw0(DB_ERROR(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast(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")); + + txn.commit(); + return result; +} + +uint64_t BlockchainBDB::get_top_block_timestamp() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + // if no blocks, return 0 + if (m_height == 0) + { + return 0; + } + + return get_block_timestamp(m_height - 1); +} + +size_t BlockchainBDB::get_block_size(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(height + 1); + Dbt_copy result; + auto get_result = m_block_sizes->get(txn, &key, &result, 0); + if (get_result == DB_NOTFOUND) + { + throw0(DB_ERROR(std::string("Attempt to get block size from height ").append(boost::lexical_cast(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")); + + txn.commit(); + return result; +} + +difficulty_type BlockchainBDB::get_block_cumulative_difficulty(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__ << " height: " << height); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(height + 1); + Dbt_copy result; + auto get_result = m_block_diffs->get(txn, &key, &result, 0); + if (get_result == DB_NOTFOUND) + { + throw0(DB_ERROR(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast(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")); + + txn.commit(); + return result; +} + +difficulty_type BlockchainBDB::get_block_difficulty(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __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 BlockchainBDB::get_block_already_generated_coins(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(height + 1); + Dbt_copy result; + auto get_result = m_block_coins->get(txn, &key, &result, 0); + if (get_result == DB_NOTFOUND) + { + throw0(DB_ERROR(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast(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")); + + txn.commit(); + return result; +} + +crypto::hash BlockchainBDB::get_block_hash_from_height(const uint64_t& height) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(height + 1); + Dbt_copy result; + auto get_result = m_block_hashes->get(txn, &key, &result, 0); + if (get_result == DB_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())); + } + else if (get_result) + throw0(DB_ERROR("Error attempting to retrieve a block hash from the db.")); + + txn.commit(); + return result; +} + +std::vector BlockchainBDB::get_blocks_range(const uint64_t& h1, const uint64_t& h2) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + std::vector v; + + for (uint64_t height = h1; height <= h2; ++height) + { + v.push_back(get_block_from_height(height)); + } + + return v; +} + +std::vector BlockchainBDB::get_hashes_range(const uint64_t& h1, const uint64_t& h2) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + std::vector v; + + for (uint64_t height = h1; height <= h2; ++height) + { + v.push_back(get_block_hash_from_height(height)); + } + + return v; +} + +crypto::hash BlockchainBDB::top_block_hash() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + if (m_height > 0) + { + return get_block_hash_from_height(m_height - 1); + } + + return null_hash; +} + +block BlockchainBDB::get_top_block() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + if (m_height > 0) + { + return get_block_from_height(m_height - 1); + } + + block b; + return b; +} + +uint64_t BlockchainBDB::height() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + return m_height; +} + +bool BlockchainBDB::tx_exists(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(h); + + TIME_MEASURE_START(time1); + auto get_result = m_txs->exists(txn, &key, 0); + TIME_MEASURE_FINISH(time1); + time_tx_exists += time1; + if (get_result == DB_NOTFOUND) + { + 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 BlockchainBDB::get_tx_unlock_time(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(h); + Dbt_copy result; + auto get_result = m_tx_unlocks->get(txn, &key, &result, 0); + if (get_result == DB_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 result; +} + +transaction BlockchainBDB::get_tx(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy key(h); + Dbt_safe result; + auto get_result = m_txs->get(txn, &key, &result, 0); + if (get_result == DB_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(result.get_data()), result.get_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")); + + txn.commit(); + + return tx; +} + +uint64_t BlockchainBDB::get_tx_count() const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); DB_BTREE_STAT* stats; // DB_FAST_STAT can apparently cause an incorrect number of records // to be returned. The flag should be set to 0 instead if this proves // to be the case. - m_blocks->stat(txn, &stats, DB_FAST_STAT); - m_height = stats->bt_nkeys; - delete stats; - - // see above comment about DB_FAST_STAT - m_output_indices->stat(txn, &stats, DB_FAST_STAT); - m_num_outputs = stats->bt_nkeys; + m_txs->stat(txn, &stats, DB_FAST_STAT); + auto num_txs = stats->bt_nkeys; delete stats; txn.commit(); - } - catch (const std::exception& e) - { - throw0(DB_OPEN_FAILURE(e.what())); - } - m_open = true; -} - -void BlockchainBDB::close() -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - this->sync(); - - // FIXME: not yet thread safe!!! Use with care. - m_open = false; - m_env->close(DB_FORCESYNC); -} - -void BlockchainBDB::sync() -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - try - { - m_blocks->sync(0); - m_block_heights->sync(0); - m_block_hashes->sync(0); - m_block_timestamps->sync(0); - m_block_sizes->sync(0); - m_block_diffs->sync(0); - m_block_coins->sync(0); - - m_txs->sync(0); - m_tx_unlocks->sync(0); - m_tx_heights->sync(0); - m_tx_outputs->sync(0); - - m_output_txs->sync(0); - m_output_indices->sync(0); - m_output_amounts->sync(0); - m_output_keys->sync(0); - - m_spent_keys->sync(0); - } - catch (const std::exception& e) - { - throw0(DB_ERROR(std::string("Failed to sync database: ").append(e.what()).c_str())); - } -} - -void BlockchainBDB::reset() -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - // TODO: this -} - -std::vector BlockchainBDB::get_filenames() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - std::vector filenames; - - char *fname, *dbname; - const char **pfname, **pdbname; - - pfname = (const char **)&fname; - pdbname = (const char **)&dbname; - - m_blocks->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_block_heights->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_block_hashes->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_block_timestamps->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_block_sizes->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_block_diffs->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_block_coins->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_txs->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_tx_unlocks->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_tx_heights->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_tx_outputs->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_output_txs->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_output_indices->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_output_amounts->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_output_keys->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - m_spent_keys->get_dbname(pfname, pdbname); - filenames.push_back(fname); - - std::vector full_paths; - - for (auto& filename : filenames) - { - boost::filesystem::path p(m_folder); - p /= filename; - full_paths.push_back(p.string()); - } - - return full_paths; -} - -std::string BlockchainBDB::get_db_name() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - - return std::string("BerkeleyDB"); -} - -// TODO: this? -bool BlockchainBDB::lock() -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - return false; -} - -// TODO: this? -void BlockchainBDB::unlock() -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); -} - -bool BlockchainBDB::block_exists(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(h); - - auto get_result = m_block_heights->exists(txn, &key, 0); - if (get_result == DB_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 BlockchainBDB::get_block(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - return get_block_from_height(get_block_height(h)); -} - -uint64_t BlockchainBDB::get_block_height(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(h); - Dbt_copy result; - - auto get_result = m_block_heights->get(txn, &key, &result, 0); - if (get_result == DB_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 (uint64_t)result - 1; -} - -block_header BlockchainBDB::get_block_header(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - // block_header object is automatically cast from block object - return get_block(h); -} - -block BlockchainBDB::get_block_from_height(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(height + 1); - Dbt_safe result; - auto get_result = m_blocks->get(txn, &key, &result, 0); - if (get_result == DB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get block from height ").append(boost::lexical_cast(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(result.get_data()), result.get_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 BlockchainBDB::get_block_timestamp(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(height + 1); - Dbt_copy result; - auto get_result = m_block_timestamps->get(txn, &key, &result, 0); - if (get_result == DB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast(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")); - - txn.commit(); - return result; -} - -uint64_t BlockchainBDB::get_top_block_timestamp() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - // if no blocks, return 0 - if (m_height == 0) - { - return 0; - } - - return get_block_timestamp(m_height - 1); -} - -size_t BlockchainBDB::get_block_size(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(height + 1); - Dbt_copy result; - auto get_result = m_block_sizes->get(txn, &key, &result, 0); - if (get_result == DB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get block size from height ").append(boost::lexical_cast(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")); - - txn.commit(); - return result; -} - -difficulty_type BlockchainBDB::get_block_cumulative_difficulty(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__ << " height: " << height); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(height + 1); - Dbt_copy result; - auto get_result = m_block_diffs->get(txn, &key, &result, 0); - if (get_result == DB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast(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")); - - txn.commit(); - return result; -} - -difficulty_type BlockchainBDB::get_block_difficulty(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __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 BlockchainBDB::get_block_already_generated_coins(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(height + 1); - Dbt_copy result; - auto get_result = m_block_coins->get(txn, &key, &result, 0); - if (get_result == DB_NOTFOUND) - { - throw0(DB_ERROR(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast(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")); - - txn.commit(); - return result; -} - -crypto::hash BlockchainBDB::get_block_hash_from_height(const uint64_t& height) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(height + 1); - Dbt_copy result; - auto get_result = m_block_hashes->get(txn, &key, &result, 0); - if (get_result == DB_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())); - } - else if (get_result) - throw0(DB_ERROR("Error attempting to retrieve a block hash from the db.")); - - txn.commit(); - return result; -} - -std::vector BlockchainBDB::get_blocks_range(const uint64_t& h1, const uint64_t& h2) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - std::vector v; - - for (uint64_t height = h1; height <= h2; ++height) - { - v.push_back(get_block_from_height(height)); - } - - return v; -} - -std::vector BlockchainBDB::get_hashes_range(const uint64_t& h1, const uint64_t& h2) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - std::vector v; - - for (uint64_t height = h1; height <= h2; ++height) - { - v.push_back(get_block_hash_from_height(height)); - } - - return v; -} - -crypto::hash BlockchainBDB::top_block_hash() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - if (m_height > 0) - { - return get_block_hash_from_height(m_height - 1); - } - - return null_hash; -} - -block BlockchainBDB::get_top_block() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - if (m_height > 0) - { - return get_block_from_height(m_height - 1); - } - - block b; - return b; -} - -uint64_t BlockchainBDB::height() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - return m_height; -} - -bool BlockchainBDB::tx_exists(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(h); - - TIME_MEASURE_START(time1); - auto get_result = m_txs->exists(txn, &key, 0); - TIME_MEASURE_FINISH(time1); - time_tx_exists += time1; - if (get_result == DB_NOTFOUND) - { - 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 BlockchainBDB::get_tx_unlock_time(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainLMDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(h); - Dbt_copy result; - auto get_result = m_tx_unlocks->get(txn, &key, &result, 0); - if (get_result == DB_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 result; -} - -transaction BlockchainBDB::get_tx(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - Dbt_copy key(h); - Dbt_safe result; - auto get_result = m_txs->get(txn, &key, &result, 0); - if (get_result == DB_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(result.get_data()), result.get_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")); - - txn.commit(); - - return tx; -} - -uint64_t BlockchainBDB::get_tx_count() const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - DB_BTREE_STAT* stats; - - // DB_FAST_STAT can apparently cause an incorrect number of records - // to be returned. The flag should be set to 0 instead if this proves - // to be the case. - m_txs->stat(txn, &stats, DB_FAST_STAT); - auto num_txs = stats->bt_nkeys; - delete stats; - - txn.commit(); - - return num_txs; + return num_txs; } std::vector BlockchainBDB::get_tx_list(const std::vector& hlist) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - std::vector v; + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + std::vector v; - for (auto& h : hlist) - { - v.push_back(get_tx(h)); - } +for (auto& h : hlist) + { + v.push_back(get_tx(h)); + } - return v; + return v; } uint64_t BlockchainBDB::get_tx_block_height(const crypto::hash& h) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); - Dbt_copy key(h); - Dbt_copy result; - auto get_result = m_tx_heights->get(txn, &key, &result, 0); - if (get_result == DB_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")); + Dbt_copy key(h); + Dbt_copy result; + auto get_result = m_tx_heights->get(txn, &key, &result, 0); + if (get_result == DB_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")); - txn.commit(); + txn.commit(); - return (uint64_t)result - 1; + return (uint64_t)result - 1; } //FIXME: make sure the random method used here is appropriate uint64_t BlockchainBDB::get_random_output(const uint64_t& amount) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __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")); + 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() % num_outputs; + return crypto::rand() % num_outputs; } uint64_t BlockchainBDB::get_num_outputs(const uint64_t& amount) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); - bdb_cur cur(txn, m_output_amounts); + bdb_cur cur(txn, m_output_amounts); - Dbt_copy k(amount); - Dbt_copy v; - auto result = cur->get(&k, &v, DB_SET); - if (result == DB_NOTFOUND) - { - return 0; - } - else if (result) - throw0(DB_ERROR("DB error attempting to get number of outputs of an amount")); + Dbt_copy k(amount); + Dbt_copy v; + auto result = cur->get(&k, &v, DB_SET); + if (result == DB_NOTFOUND) + { + return 0; + } + else if (result) + throw0(DB_ERROR("DB error attempting to get number of outputs of an amount")); - db_recno_t num_elems = 0; - cur->count(&num_elems, 0); + db_recno_t num_elems = 0; + cur->count(&num_elems, 0); - txn.commit(); + txn.commit(); - return num_elems; + return num_elems; } crypto::public_key BlockchainBDB::get_output_key(const uint64_t& amount, const uint64_t& index) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - uint64_t glob_index = get_output_global_index(amount, index); + uint64_t glob_index = get_output_global_index(amount, index); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); - Dbt_copy k(glob_index); - Dbt_copy v; - auto get_result = m_output_keys->get(txn, &k, &v, 0); - if (get_result == DB_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")); + Dbt_copy k(glob_index); + Dbt_copy v; + auto get_result = m_output_keys->get(txn, &k, &v, 0); + if (get_result == DB_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 v; + return v; } // As this is not used, its return is now a blank output. // This will save on space in the db. tx_out BlockchainBDB::get_output(const crypto::hash& h, const uint64_t& index) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - return tx_out(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + return tx_out(); } // As this is not used, its return is now a blank output. // This will save on space in the db. tx_out BlockchainBDB::get_output(const uint64_t& index) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - return tx_out(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + return tx_out(); } void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, std::vector &offsets, std::vector &indices) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); bdb_cur cur(txn, m_output_amounts); uint64_t max = 0; @@ -1413,31 +1425,31 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, max = index; } - // ??? might be a bug, don't always treat as uint64_t - #define DBT_VALUE(dbt) v.get_size() == sizeof(uint64_t) ? \ - *((uint64_t *)v.get_data()) : *((uint32_t *)v.get_data()) \ + // ??? might be a bug, don't always treat as uint64_t + #define DBT_VALUE(dbt) v.get_size() == sizeof(uint64_t) ? \ + *((uint64_t *)v.get_data()) : *((uint32_t *)v.get_data()) \ - // get returned keypairs count - #define DB_COUNT_RECORDS(dbt, cnt) \ - do { \ - uint32_t *_p = (uint32_t *) ((uint8_t *)(dbt)->data + \ - (dbt)->ulen - sizeof(uint32_t)); \ - cnt = 0; \ - while(*_p != (uint32_t) -1) { \ - _p -= 2; \ - ++cnt; \ - } \ - } while(0); \ + // get returned keypairs count + #define DB_COUNT_RECORDS(dbt, cnt) \ + do { \ + uint32_t *_p = (uint32_t *) ((uint8_t *)(dbt)->data + \ + (dbt)->ulen - sizeof(uint32_t)); \ + cnt = 0; \ + while(*_p != (uint32_t) -1) { \ + _p -= 2; \ + ++cnt; \ + } \ + } while(0); \ - Dbt_copy k(amount); + Dbt_copy k(amount); Dbt_copy v; uint64_t buflen = 0; - uint64_t t_dbmul = 0; - uint64_t t_dbscan = 0; + uint64_t t_dbmul = 0; + uint64_t t_dbscan = 0; TIME_MEASURE_START(db2); if(max <= 1) { - for (const uint64_t& index : offsets) + for (const uint64_t& index : offsets) { TIME_MEASURE_START(t_seek); @@ -1469,28 +1481,28 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, } else { - // setup a 256KB minimum buffer size - uint32_t pagesize = 256 * 1024; + // setup a 256KB minimum buffer size + uint32_t pagesize = 256 * 1024; - // Retrieve only a suitable portion of the kvp data, up to somewhere near - // the maximum offset value being retrieved - buflen = (max + 1) * 4 * sizeof(uint64_t); - buflen = ((buflen / pagesize) + ((buflen % pagesize) > 0 ? 1 : 0)) * pagesize; - bool singlebuff = buflen <= BUFFER_LENGTH; - buflen = buflen < BUFFER_LENGTH ? buflen : BUFFER_LENGTH; + // Retrieve only a suitable portion of the kvp data, up to somewhere near + // the maximum offset value being retrieved + buflen = (max + 1) * 4 * sizeof(uint64_t); + buflen = ((buflen / pagesize) + ((buflen % pagesize) > 0 ? 1 : 0)) * pagesize; + bool singlebuff = buflen <= BUFFER_LENGTH; + buflen = buflen < BUFFER_LENGTH ? buflen : BUFFER_LENGTH; - Dbt data; - data.set_data(m_buffer); - data.set_ulen(buflen); - data.set_size(buflen); - data.set_flags(DB_DBT_USERMEM); + Dbt data; + data.set_data(m_buffer); + data.set_ulen(buflen); + data.set_size(buflen); + data.set_flags(DB_DBT_USERMEM); - uint32_t curcount = 0; - uint32_t blockstart = 0; + uint32_t curcount = 0; + uint32_t blockstart = 0; for (const uint64_t& index : offsets) { - // fixme! for whatever reason, the first call to DB_MULTIPLE | DB_SET does not - // retrieve the first value. + // fixme! for whatever reason, the first call to DB_MULTIPLE | DB_SET does not + // retrieve the first value. if(index <= 1) { auto result = cur->get(&k, &v, DB_SET); @@ -1509,40 +1521,40 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, } else { - while(index >= curcount) - { - TIME_MEASURE_START(t_db1); - try - { - cur->get(&k, &data, DB_MULTIPLE | (curcount == 0 ? DB_SET : DB_NEXT_DUP)); - blockstart = curcount; - // skip counting if using single buffer, it actually adds some overhead on some systems. - if(!singlebuff) - { - int count = 0; - DB_COUNT_RECORDS((DBT *) &data, count); - curcount += count; - } - } - catch (const std::exception &e) - { - LOG_PRINT_L0("DB_EXCEPTION: " << e.what()); - } + while(index >= curcount) + { + TIME_MEASURE_START(t_db1); + try + { + cur->get(&k, &data, DB_MULTIPLE | (curcount == 0 ? DB_SET : DB_NEXT_DUP)); + blockstart = curcount; + // skip counting if using single buffer, it actually adds some overhead on some systems. + if(!singlebuff) + { + int count = 0; + DB_COUNT_RECORDS((DBT *) &data, count); + curcount += count; + } + } + catch (const std::exception &e) + { + LOG_PRINT_L0("DB_EXCEPTION: " << e.what()); + } - TIME_MEASURE_FINISH(t_db1); - t_dbmul += t_db1; - if(singlebuff) - break; - } + TIME_MEASURE_FINISH(t_db1); + t_dbmul += t_db1; + if(singlebuff) + break; + } - LOG_PRINT_L1("Records returned: " << curcount << " Index: " << index); - TIME_MEASURE_START(t_db2); - DBT *pdata = (DBT *) &data; + LOG_PRINT_L1("Records returned: " << curcount << " Index: " << index); + TIME_MEASURE_START(t_db2); + DBT *pdata = (DBT *) &data; uint8_t *value; uint64_t dlen = 0; - void *pbase = ((uint8_t *)(pdata->data)) + pdata->ulen - sizeof(uint32_t); + void *pbase = ((uint8_t *)(pdata->data)) + pdata->ulen - sizeof(uint32_t); uint32_t *p = (uint32_t *) pbase; if (*p == (uint32_t) -1) { @@ -1560,11 +1572,11 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, if (value != NULL) { v = dlen == sizeof(uint64_t) ? *((uint64_t *) value) - : *((uint32_t *) value); + : *((uint32_t *) value); } - TIME_MEASURE_FINISH(t_db2); - t_dbscan += t_db2; - } + TIME_MEASURE_FINISH(t_db2); + t_dbscan += t_db2; + } uint64_t glob_index = DBT_VALUE(v); @@ -1579,108 +1591,17 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, LOG_PRINT_L1("blen: " << buflen << " db1: " << t_dbmul << " db2: " << t_dbscan); cur.close(); - txn.commit(); + txn.commit(); } tx_out_index BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - bdb_cur cur(txn, m_output_amounts); - - Dbt_copy k(amount); - Dbt_copy v; - - auto result = cur->get(&k, &v, DB_SET); - if (result == DB_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")); - - db_recno_t num_elems = 0; - cur->count(&num_elems, 0); - - if (num_elems <= index) - throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); - - for (uint64_t i = 0; i < index; ++i) - { - cur->get(&k, &v, DB_NEXT_DUP); - } - - uint64_t glob_index = v; - - cur.close(); - - txn.commit(); - - return get_output_tx_and_index_from_global(glob_index); -} - -std::vector BlockchainBDB::get_tx_output_indices(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - std::vector index_vec; - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - bdb_cur cur(txn, m_tx_outputs); - - Dbt_copy k(h); - Dbt_copy v; - auto result = cur->get(&k, &v, DB_SET); - if (result == DB_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")); - - db_recno_t num_elems = 0; - cur->count(&num_elems, 0); - - for (uint64_t i = 0; i < num_elems; ++i) - { - index_vec.push_back(v); - cur->get(&k, &v, DB_NEXT_DUP); - } - - cur.close(); - txn.commit(); - - return index_vec; -} - -std::vector BlockchainBDB::get_tx_amount_output_indices(const crypto::hash& h) const -{ - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); - std::vector index_vec; - std::vector 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); - - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - - uint64_t i = 0; - uint64_t global_index; - for (const auto& vout : tx.vout) - { - uint64_t amount = vout.amount; - - global_index = index_vec[i]; + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); bdb_cur cur(txn, m_output_amounts); @@ -1689,46 +1610,137 @@ std::vector BlockchainBDB::get_tx_amount_output_indices(const crypto:: auto result = cur->get(&k, &v, DB_SET); if (result == DB_NOTFOUND) - throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found")); + 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")); + throw0(DB_ERROR("DB error attempting to get an output")); db_recno_t num_elems = 0; cur->count(&num_elems, 0); - uint64_t amount_output_index = 0; - uint64_t output_index = 0; - bool found_index = false; - for (uint64_t j = 0; j < num_elems; ++j) + if (num_elems <= index) + throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found")); + + for (uint64_t i = 0; i < index; ++i) { - output_index = v; - if (output_index == global_index) - { - amount_output_index = j; - found_index = true; - break; - } - cur->get(&k, &v, DB_NEXT_DUP); + cur->get(&k, &v, DB_NEXT_DUP); } - if (found_index) + + uint64_t glob_index = v; + + cur.close(); + + txn.commit(); + + return get_output_tx_and_index_from_global(glob_index); +} + +std::vector BlockchainBDB::get_tx_output_indices(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + std::vector index_vec; + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + bdb_cur cur(txn, m_tx_outputs); + + Dbt_copy k(h); + Dbt_copy v; + auto result = cur->get(&k, &v, DB_SET); + if (result == DB_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")); + + db_recno_t num_elems = 0; + cur->count(&num_elems, 0); + + for (uint64_t i = 0; i < num_elems; ++i) { - index_vec2.push_back(amount_output_index); - } - else - { - // not found - cur.close(); - txn.commit(); - throw1(OUTPUT_DNE("specified output not found in db")); + index_vec.push_back(v); + cur->get(&k, &v, DB_NEXT_DUP); } cur.close(); - ++i; - } + txn.commit(); - txn.commit(); + return index_vec; +} - return index_vec2; +std::vector BlockchainBDB::get_tx_amount_output_indices(const crypto::hash& h) const +{ + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); + std::vector index_vec; + std::vector 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); + + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + uint64_t i = 0; + uint64_t global_index; + for (const auto& vout : tx.vout) + { + uint64_t amount = vout.amount; + + global_index = index_vec[i]; + + bdb_cur cur(txn, m_output_amounts); + + Dbt_copy k(amount); + Dbt_copy v; + + auto result = cur->get(&k, &v, DB_SET); + if (result == DB_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")); + + db_recno_t num_elems = 0; + cur->count(&num_elems, 0); + + uint64_t amount_output_index = 0; + uint64_t output_index = 0; + bool found_index = false; + for (uint64_t j = 0; j < num_elems; ++j) + { + output_index = v; + if (output_index == global_index) + { + amount_output_index = j; + found_index = true; + break; + } + cur->get(&k, &v, DB_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; } @@ -1766,22 +1778,22 @@ tx_out_index BlockchainBDB::get_output_tx_and_index_from_global(const uint64_t& bool BlockchainBDB::has_key_image(const crypto::key_image& img) const { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + throw0(DB_ERROR("Failed to create a transaction for the db")); + + Dbt_copy val_key(img); + if (m_spent_keys->exists(txn, &val_key, 0) == 0) + { + txn.commit(); + return true; + } - Dbt_copy val_key(img); - if (m_spent_keys->exists(txn, &val_key, 0) == 0) - { txn.commit(); - return true; - } - - txn.commit(); - return false; + return false; } // Ostensibly BerkeleyDB has batch transaction support built-in, @@ -1789,93 +1801,93 @@ bool BlockchainBDB::has_key_image(const crypto::key_image& img) const void BlockchainBDB::batch_start(uint64_t batch_num_blocks) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); } void BlockchainBDB::batch_commit() { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); } void BlockchainBDB::batch_stop() { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); } void BlockchainBDB::batch_abort() { - LOG_PRINT_L3("BlockchainBDB::" << __func__); + LOG_PRINT_L3("BlockchainBDB::" << __func__); } void BlockchainBDB::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")); + LOG_PRINT_L3("BlockchainLMDB::" << __func__); + m_batch_transactions = batch_transactions; + LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled")); } uint64_t BlockchainBDB::add_block( const block& blk - , const size_t& block_size - , const difficulty_type& cumulative_difficulty - , const uint64_t& coins_generated - , const std::vector& txs - ) + , const size_t& block_size + , const difficulty_type& cumulative_difficulty + , const uint64_t& coins_generated + , const std::vector& txs + ) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - m_write_txn = &txn; + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + 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); - m_write_txn = NULL; + uint64_t num_outputs = m_num_outputs; + try + { + BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); + m_write_txn = NULL; - TIME_MEASURE_START(time1); - txn.commit(); - TIME_MEASURE_FINISH(time1); - time_commit1 += time1; - } - catch (const std::exception& e) - { - m_num_outputs = num_outputs; - m_write_txn = NULL; - throw; - } + TIME_MEASURE_START(time1); + txn.commit(); + TIME_MEASURE_FINISH(time1); + time_commit1 += time1; + } + catch (const std::exception& e) + { + m_num_outputs = num_outputs; + m_write_txn = NULL; + throw; + } - return ++m_height; + return ++m_height; } void BlockchainBDB::pop_block(block& blk, std::vector& txs) { - LOG_PRINT_L3("BlockchainBDB::" << __func__); - check_open(); + LOG_PRINT_L3("BlockchainBDB::" << __func__); + check_open(); - bdb_txn_safe txn; - if (m_env->txn_begin(NULL, txn, 0)) - throw0(DB_ERROR("Failed to create a transaction for the db")); - m_write_txn = &txn; + bdb_txn_safe txn; + if (m_env->txn_begin(NULL, txn, 0)) + 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); + uint64_t num_outputs = m_num_outputs; + try + { + BlockchainDB::pop_block(blk, txs); - m_write_txn = NULL; - txn.commit(); - } - catch (...) - { - m_num_outputs = num_outputs; - m_write_txn = NULL; - throw; - } + m_write_txn = NULL; + txn.commit(); + } + catch (...) + { + m_num_outputs = num_outputs; + m_write_txn = NULL; + throw; + } - --m_height; + --m_height; } } // namespace cryptonote