diff options
Diffstat (limited to '')
-rw-r--r-- | src/blockchain_db/blockchain_db.cpp | 11 | ||||
-rw-r--r-- | src/blockchain_db/blockchain_db.h | 15 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.cpp | 211 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.h | 6 |
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: |