aboutsummaryrefslogtreecommitdiff
path: root/src/blockchain_db
diff options
context:
space:
mode:
Diffstat (limited to 'src/blockchain_db')
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp5
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h2
-rw-r--r--src/blockchain_db/blockchain_db.cpp11
-rw-r--r--src/blockchain_db/blockchain_db.h36
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp237
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h12
6 files changed, 295 insertions, 8 deletions
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index e1b76ec1e..f827ab7c3 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -1213,6 +1213,11 @@ std::vector<std::string> BlockchainBDB::get_filenames() const
return full_paths;
}
+bool BlockchainBDB::remove_data_file(const std::string& folder)
+{
+ return true;
+}
+
std::string BlockchainBDB::get_db_name() const
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index cecbba28f..c90d030a2 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -244,6 +244,8 @@ public:
virtual std::vector<std::string> get_filenames() const;
+ virtual bool remove_data_file(const std::string& folder);
+
virtual std::string get_db_name() const;
virtual bool lock();
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 88ac34255..8544cc3f0 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -218,13 +218,22 @@ uint64_t BlockchainDB::add_block( const block& blk
// call out to add the transactions
time1 = epee::misc_utils::get_tick_count();
+
+ uint64_t num_rct_outs = 0;
add_transaction(blk_hash, blk.miner_tx);
+ if (blk.miner_tx.version == 2)
+ num_rct_outs += blk.miner_tx.vout.size();
int tx_i = 0;
crypto::hash tx_hash = crypto::null_hash;
for (const transaction& tx : txs)
{
tx_hash = blk.tx_hashes[tx_i];
add_transaction(blk_hash, tx, &tx_hash);
+ for (const auto &vout: tx.vout)
+ {
+ if (vout.amount == 0)
+ ++num_rct_outs;
+ }
++tx_i;
}
TIME_MEASURE_FINISH(time1);
@@ -232,7 +241,7 @@ uint64_t BlockchainDB::add_block( const block& blk
// 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);
+ add_block(blk, block_size, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash);
TIME_MEASURE_FINISH(time1);
time_add_block1 += time1;
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 564016fc9..6851e2404 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -367,6 +367,7 @@ private:
, const size_t& block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
+ , uint64_t num_rct_outs
, const crypto::hash& blk_hash
) = 0;
@@ -655,6 +656,20 @@ public:
*/
virtual std::vector<std::string> get_filenames() const = 0;
+ /**
+ * @brief remove file(s) storing the database
+ *
+ * This function is for resetting the database (for core tests, functional tests, etc).
+ * The function reset() is not usable because it needs to open the database file first
+ * which can fail if the existing database file is in an incompatible format.
+ * As such, this function needs to be called before calling open().
+ *
+ * @param folder The path of the folder containing the database file(s) which must not end with slash '/'.
+ *
+ * @return true if the operation is succesfull
+ */
+ virtual bool remove_data_file(const std::string& folder) const = 0;
+
// return the name of the folder the db's file(s) should reside in
/**
* @brief gets the name of the folder the BlockchainDB's file(s) should be in
@@ -892,6 +907,20 @@ public:
virtual uint64_t get_block_timestamp(const uint64_t& height) const = 0;
/**
+ * @brief fetch a block's cumulative number of rct outputs
+ *
+ * The subclass should return the numer of rct outputs in the blockchain
+ * up to the block with the given height (inclusive).
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the cumulative number of rct outputs
+ */
+ virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const = 0;
+
+ /**
* @brief fetch the top block's timestamp
*
* The subclass should return the timestamp of the most recent block.
@@ -1536,6 +1565,13 @@ public:
*/
virtual bool is_read_only() const = 0;
+ /**
+ * @brief get disk space requirements
+ *
+ * @return the size required
+ */
+ virtual uint64_t get_database_size() const = 0;
+
// TODO: this should perhaps be (or call) a series of functions which
// progressively update through version updates
/**
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 367bfa49e..5d1679c0c 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -35,6 +35,7 @@
#include <random>
#include "string_tools.h"
+#include "file_io_utils.h"
#include "common/util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "crypto/crypto.h"
@@ -53,7 +54,7 @@ using epee::string_tools::pod_to_hex;
using namespace crypto;
// Increase when the DB structure changes
-#define VERSION 2
+#define VERSION 3
namespace
{
@@ -250,6 +251,16 @@ inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi
namespace cryptonote
{
+typedef struct mdb_block_info_old
+{
+ uint64_t bi_height;
+ uint64_t bi_timestamp;
+ uint64_t bi_coins;
+ uint64_t bi_size; // a size_t really but we need 32-bit compat
+ difficulty_type bi_diff;
+ crypto::hash bi_hash;
+} mdb_block_info_old;
+
typedef struct mdb_block_info
{
uint64_t bi_height;
@@ -258,6 +269,7 @@ typedef struct mdb_block_info
uint64_t bi_size; // a size_t really but we need 32-bit compat
difficulty_type bi_diff;
crypto::hash bi_hash;
+ uint64_t bi_cum_rct;
} mdb_block_info;
typedef struct blk_height {
@@ -667,7 +679,7 @@ estim:
}
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)
+ uint64_t num_rct_outs, const crypto::hash& blk_hash)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -715,6 +727,16 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
bi.bi_size = block_size;
bi.bi_diff = cumulative_difficulty;
bi.bi_hash = blk_hash;
+ bi.bi_cum_rct = num_rct_outs;
+ if (blk.major_version >= 4)
+ {
+ uint64_t last_height = m_height-1;
+ MDB_val_set(h, last_height);
+ if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH)))
+ throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str()));
+ const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data;
+ bi.bi_cum_rct += bi_prev->bi_cum_rct;
+ }
MDB_val_set(val, bi);
result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP);
@@ -759,8 +781,6 @@ void BlockchainLMDB::remove_block()
if ((result = mdb_cursor_del(m_cur_block_heights, 0)))
throw1(DB_ERROR(lmdb_error("Failed to add removal of block height by hash to db transaction: ", result).c_str()));
- if ((result = mdb_cursor_get(m_cur_blocks, &k, NULL, MDB_SET)))
- throw1(DB_ERROR(lmdb_error("Failed to locate block for removal: ", result).c_str()));
if ((result = mdb_cursor_del(m_cur_blocks, 0)))
throw1(DB_ERROR(lmdb_error("Failed to add removal of block to db transaction: ", result).c_str()));
@@ -1143,6 +1163,8 @@ BlockchainLMDB::BlockchainLMDB(bool batch_transactions): BlockchainDB()
m_cum_size = 0;
m_cum_count = 0;
+ // reset may also need changing when initialize things here
+
m_hardfork = nullptr;
}
@@ -1469,6 +1491,21 @@ std::vector<std::string> BlockchainLMDB::get_filenames() const
return filenames;
}
+bool BlockchainLMDB::remove_data_file(const std::string& folder) const
+{
+ const std::string filename = folder + "/data.mdb";
+ try
+ {
+ boost::filesystem::remove(filename);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to remove " << filename << ": " << e.what());
+ return false;
+ }
+ return true;
+}
+
std::string BlockchainLMDB::get_db_name() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1908,6 +1945,43 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
return ret;
}
+std::vector<uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ std::vector<uint64_t> res;
+ int result;
+
+ if (heights.empty())
+ return {};
+ res.reserve(heights.size());
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(block_info);
+
+ MDB_stat db_stats;
+ if ((result = mdb_stat(m_txn, m_blocks, &db_stats)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
+ for (size_t i = 0; i < heights.size(); ++i)
+ if (heights[i] >= db_stats.ms_entries)
+ throw0(BLOCK_DNE(std::string("Attempt to get rct distribution from height " + std::to_string(heights[i]) + " failed -- block size not in db").c_str()));
+
+ MDB_val v;
+
+ for (uint64_t height: heights)
+ {
+ MDB_val_set(v, height);
+ result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
+ const mdb_block_info *bi = (const mdb_block_info *)v.mv_data;
+ res.push_back(bi->bi_cum_rct);
+ }
+
+ TXN_POSTFIX_RDONLY();
+ return res;
+}
+
uint64_t BlockchainLMDB::get_top_block_timestamp() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -3328,6 +3402,7 @@ bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_heig
MDB_val_set(k, amount);
MDB_val v;
MDB_cursor_op op = MDB_SET;
+ base = 0;
while (1)
{
int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op);
@@ -3346,6 +3421,9 @@ bool BlockchainLMDB::get_output_distribution(uint64_t amount, uint64_t from_heig
break;
}
+ for (size_t n = 1; n < distribution.size(); ++n)
+ distribution[n] += distribution[n - 1];
+
TXN_POSTFIX_RDONLY();
return true;
@@ -3423,6 +3501,16 @@ bool BlockchainLMDB::is_read_only() const
return false;
}
+uint64_t BlockchainLMDB::get_database_size() const
+{
+ uint64_t size = 0;
+ boost::filesystem::path datafile(m_folder);
+ datafile /= CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
+ if (!epee::file_io_utils::get_file_size(datafile.string(), size))
+ size = 0;
+ return size;
+}
+
void BlockchainLMDB::fixup()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -3440,7 +3528,7 @@ void BlockchainLMDB::fixup()
if (result) \
throw0(DB_ERROR(lmdb_error("Failed to get DB record for " name ": ", result).c_str())); \
ptr = (char *)k.mv_data; \
- ptr[sizeof(name)-2] = 's'
+ ptr[sizeof(name)-2]++
#define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global"))
@@ -3580,7 +3668,7 @@ void BlockchainLMDB::migrate_0_1()
break;
}
MDB_dbi diffs, hashes, sizes, timestamps;
- mdb_block_info bi;
+ mdb_block_info_old bi;
MDB_val_set(nv, bi);
lmdb_db_open(txn, "block_diffs", 0, diffs, "Failed to open db handle for block_diffs");
@@ -4120,6 +4208,141 @@ void BlockchainLMDB::migrate_1_2()
txn.commit();
}
+void BlockchainLMDB::migrate_2_3()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ uint64_t i;
+ int result;
+ mdb_txn_safe txn(false);
+ MDB_val k, v;
+ char *ptr;
+
+ MGINFO_YELLOW("Migrating blockchain from DB version 2 to 3 - this may take a while:");
+
+ do {
+ LOG_PRINT_L1("migrating block info:");
+
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+
+ MDB_stat db_stats;
+ if ((result = mdb_stat(txn, m_blocks, &db_stats)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
+ const uint64_t blockchain_height = db_stats.ms_entries;
+
+ MDEBUG("enumerating rct outputs...");
+ std::vector<uint64_t> distribution(blockchain_height, 0);
+ bool r = for_all_outputs(0, [&](uint64_t height) {
+ if (height >= blockchain_height)
+ {
+ MERROR("Output found claiming height >= blockchain height");
+ return false;
+ }
+ distribution[height]++;
+ return true;
+ });
+ if (!r)
+ throw0(DB_ERROR("Failed to build rct output distribution"));
+ for (size_t i = 1; i < distribution.size(); ++i)
+ distribution[i] += distribution[i - 1];
+
+ /* the block_info table name is the same but the old version and new version
+ * have incompatible data. Create a new table. We want the name to be similar
+ * to the old name so that it will occupy the same location in the DB.
+ */
+ MDB_dbi o_block_info = m_block_info;
+ lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
+ mdb_set_dupsort(txn, m_block_info, compare_uint64);
+
+ MDB_cursor *c_old, *c_cur;
+ i = 0;
+ while(1) {
+ if (!(i % 1000)) {
+ if (i) {
+ LOGIF(el::Level::Info) {
+ std::cout << i << " / " << blockchain_height << " \r" << std::flush;
+ }
+ txn.commit();
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ }
+ result = mdb_cursor_open(txn, m_block_info, &c_cur);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str()));
+ result = mdb_cursor_open(txn, o_block_info, &c_old);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str()));
+ if (!i) {
+ MDB_stat db_stat;
+ result = mdb_stat(txn, m_block_info, &db_stats);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str()));
+ i = db_stats.ms_entries;
+ }
+ }
+ result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT);
+ if (result == MDB_NOTFOUND) {
+ txn.commit();
+ break;
+ }
+ else if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str()));
+ const mdb_block_info_old *bi_old = (const mdb_block_info_old*)v.mv_data;
+ mdb_block_info bi;
+ bi.bi_height = bi_old->bi_height;
+ bi.bi_timestamp = bi_old->bi_timestamp;
+ bi.bi_coins = bi_old->bi_coins;
+ bi.bi_size = bi_old->bi_size;
+ bi.bi_diff = bi_old->bi_diff;
+ bi.bi_hash = bi_old->bi_hash;
+ if (bi_old->bi_height >= distribution.size())
+ throw0(DB_ERROR("Bad height in block_info record"));
+ bi.bi_cum_rct = distribution[bi_old->bi_height];
+ MDB_val_set(nv, bi);
+ result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str()));
+ /* we delete the old records immediately, so the overall DB and mapsize should not grow.
+ * This is a little slower than just letting mdb_drop() delete it all at the end, but
+ * it saves a significant amount of disk space.
+ */
+ result = mdb_cursor_del(c_old, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str()));
+ i++;
+ }
+
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ /* Delete the old table */
+ result = mdb_drop(txn, o_block_info, 1);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str()));
+
+ RENAME_DB("block_infn");
+
+ lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
+ mdb_set_dupsort(txn, m_block_info, compare_uint64);
+
+ txn.commit();
+ } while(0);
+
+ uint32_t version = 3;
+ v.mv_data = (void *)&version;
+ v.mv_size = sizeof(version);
+ MDB_val_copy<const char *> vk("version");
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ result = mdb_put(txn, m_properties, &vk, &v, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str()));
+ txn.commit();
+}
+
void BlockchainLMDB::migrate(const uint32_t oldversion)
{
switch(oldversion) {
@@ -4127,6 +4350,8 @@ void BlockchainLMDB::migrate(const uint32_t oldversion)
migrate_0_1(); /* FALLTHRU */
case 1:
migrate_1_2(); /* FALLTHRU */
+ case 2:
+ migrate_2_3(); /* FALLTHRU */
default:
;
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index cc1b06ca0..8b214d2df 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -166,7 +166,7 @@ struct mdb_txn_safe
class BlockchainLMDB : public BlockchainDB
{
public:
- BlockchainLMDB(bool batch_transactions=false);
+ BlockchainLMDB(bool batch_transactions=true);
~BlockchainLMDB();
virtual void open(const std::string& filename, const int mdb_flags=0);
@@ -181,6 +181,8 @@ public:
virtual std::vector<std::string> get_filenames() const;
+ virtual bool remove_data_file(const std::string& folder) const;
+
virtual std::string get_db_name() const;
virtual bool lock();
@@ -197,6 +199,8 @@ public:
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const;
+ virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const;
+
virtual uint64_t get_block_timestamp(const uint64_t& height) const;
virtual uint64_t get_top_block_timestamp() const;
@@ -317,6 +321,7 @@ private:
, const size_t& block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
+ , uint64_t num_rct_outs
, const crypto::hash& block_hash
);
@@ -375,6 +380,8 @@ private:
virtual bool is_read_only() const;
+ virtual uint64_t get_database_size() const;
+
// fix up anything that may be wrong due to past bugs
virtual void fixup();
@@ -387,6 +394,9 @@ private:
// migrate from DB version 1 to 2
void migrate_1_2();
+ // migrate from DB version 2 to 3
+ void migrate_2_3();
+
void cleanup_batch();
private: