mirror of
https://github.com/monero-project/monero.git
synced 2025-08-03 12:46:06 -04:00
Merge pull request #3981
45e419b
db: store cumulative rct output distribution in the db for speed (moneromooo-monero)
This commit is contained in:
commit
21afa71ba6
9 changed files with 314 additions and 176 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -905,6 +906,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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1923,6 +1944,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__);
|
||||
|
@ -3343,6 +3401,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);
|
||||
|
@ -3361,6 +3420,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;
|
||||
|
@ -3455,7 +3517,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"))
|
||||
|
||||
|
@ -3595,7 +3657,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");
|
||||
|
@ -4135,6 +4197,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) {
|
||||
|
@ -4142,6 +4339,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:
|
||||
;
|
||||
}
|
||||
|
|
|
@ -199,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;
|
||||
|
@ -319,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
|
||||
);
|
||||
|
||||
|
@ -389,6 +392,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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue