aboutsummaryrefslogtreecommitdiff
path: root/src/blockchain_db
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2018-05-22 14:46:30 +0100
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2018-07-13 11:37:04 +0100
commit45e419bd5c756337635feccec388efedf04f44ac (patch)
tree87d196cadada9f73419e353d02120122e90161d2 /src/blockchain_db
parentMerge pull request #4094 (diff)
downloadmonero-45e419bd5c756337635feccec388efedf04f44ac.tar.xz
db: store cumulative rct output distribution in the db for speed
This gets rid of the temporary precalc cache. Also make the RPC able to send data back in binary or JSON, since there can be a lot of data This bumps the LMDB database format to v3, with migration.
Diffstat (limited to 'src/blockchain_db')
-rw-r--r--src/blockchain_db/blockchain_db.cpp11
-rw-r--r--src/blockchain_db/blockchain_db.h15
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp211
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h6
4 files changed, 236 insertions, 7 deletions
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..a52782b3c 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;
@@ -892,6 +893,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.
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 367bfa49e..3aaade3b4 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -53,7 +53,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 +250,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 +268,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 +678,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 +726,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 +780,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 +1162,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;
}
@@ -1908,6 +1929,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 +3386,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 +3405,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;
@@ -3440,7 +3502,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 +3642,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 +4182,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 +4324,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..b7f6262ae 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -197,6 +197,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 +319,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
);
@@ -387,6 +390,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: