|
|
|
@@ -35,6 +35,7 @@
|
|
|
|
|
#include "string_tools.h"
|
|
|
|
|
#include "file_io_utils.h"
|
|
|
|
|
#include "common/util.h"
|
|
|
|
|
#include "common/pruning.h"
|
|
|
|
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
|
|
|
|
#include "crypto/crypto.h"
|
|
|
|
|
#include "profile_tools.h"
|
|
|
|
@@ -130,14 +131,20 @@ private:
|
|
|
|
|
std::unique_ptr<char[]> data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int compare_uint64(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace cryptonote
|
|
|
|
|
{
|
|
|
|
|
const uint64_t va = *(const uint64_t *)a->mv_data;
|
|
|
|
|
const uint64_t vb = *(const uint64_t *)b->mv_data;
|
|
|
|
|
|
|
|
|
|
int BlockchainLMDB::compare_uint64(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
{
|
|
|
|
|
uint64_t va, vb;
|
|
|
|
|
memcpy(&va, a->mv_data, sizeof(va));
|
|
|
|
|
memcpy(&vb, b->mv_data, sizeof(vb));
|
|
|
|
|
return (va < vb) ? -1 : va > vb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int compare_hash32(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
int BlockchainLMDB::compare_hash32(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *va = (uint32_t*) a->mv_data;
|
|
|
|
|
uint32_t *vb = (uint32_t*) b->mv_data;
|
|
|
|
@@ -151,13 +158,18 @@ int compare_hash32(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int compare_string(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
int BlockchainLMDB::compare_string(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
{
|
|
|
|
|
const char *va = (const char*) a->mv_data;
|
|
|
|
|
const char *vb = (const char*) b->mv_data;
|
|
|
|
|
return strcmp(va, vb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* DB schema:
|
|
|
|
|
*
|
|
|
|
|
* Table Key Data
|
|
|
|
@@ -169,6 +181,7 @@ int compare_string(const MDB_val *a, const MDB_val *b)
|
|
|
|
|
* txs_pruned txn ID pruned txn blob
|
|
|
|
|
* txs_prunable txn ID prunable txn blob
|
|
|
|
|
* txs_prunable_hash txn ID prunable txn hash
|
|
|
|
|
* txs_prunable_tip txn ID height
|
|
|
|
|
* tx_indices txn hash {txn ID, metadata}
|
|
|
|
|
* tx_outputs txn ID [txn amount output indices]
|
|
|
|
|
*
|
|
|
|
@@ -196,6 +209,7 @@ const char* const LMDB_TXS = "txs";
|
|
|
|
|
const char* const LMDB_TXS_PRUNED = "txs_pruned";
|
|
|
|
|
const char* const LMDB_TXS_PRUNABLE = "txs_prunable";
|
|
|
|
|
const char* const LMDB_TXS_PRUNABLE_HASH = "txs_prunable_hash";
|
|
|
|
|
const char* const LMDB_TXS_PRUNABLE_TIP = "txs_prunable_tip";
|
|
|
|
|
const char* const LMDB_TX_INDICES = "tx_indices";
|
|
|
|
|
const char* const LMDB_TX_OUTPUTS = "tx_outputs";
|
|
|
|
|
|
|
|
|
@@ -279,11 +293,6 @@ typedef struct blk_height {
|
|
|
|
|
uint64_t bh_height;
|
|
|
|
|
} blk_height;
|
|
|
|
|
|
|
|
|
|
typedef struct txindex {
|
|
|
|
|
crypto::hash key;
|
|
|
|
|
tx_data_t data;
|
|
|
|
|
} txindex;
|
|
|
|
|
|
|
|
|
|
typedef struct pre_rct_outkey {
|
|
|
|
|
uint64_t amount_index;
|
|
|
|
|
uint64_t output_id;
|
|
|
|
@@ -549,18 +558,18 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const
|
|
|
|
|
// additional size needed.
|
|
|
|
|
uint64_t size_used = mst.ms_psize * mei.me_last_pgno;
|
|
|
|
|
|
|
|
|
|
LOG_PRINT_L1("DB map size: " << mei.me_mapsize);
|
|
|
|
|
LOG_PRINT_L1("Space used: " << size_used);
|
|
|
|
|
LOG_PRINT_L1("Space remaining: " << mei.me_mapsize - size_used);
|
|
|
|
|
LOG_PRINT_L1("Size threshold: " << threshold_size);
|
|
|
|
|
MDEBUG("DB map size: " << mei.me_mapsize);
|
|
|
|
|
MDEBUG("Space used: " << size_used);
|
|
|
|
|
MDEBUG("Space remaining: " << mei.me_mapsize - size_used);
|
|
|
|
|
MDEBUG("Size threshold: " << threshold_size);
|
|
|
|
|
float resize_percent = RESIZE_PERCENT;
|
|
|
|
|
LOG_PRINT_L1(boost::format("Percent used: %.04f Percent threshold: %.04f") % ((double)size_used/mei.me_mapsize) % resize_percent);
|
|
|
|
|
MDEBUG(boost::format("Percent used: %.04f Percent threshold: %.04f") % ((double)size_used/mei.me_mapsize) % resize_percent);
|
|
|
|
|
|
|
|
|
|
if (threshold_size > 0)
|
|
|
|
|
{
|
|
|
|
|
if (mei.me_mapsize - size_used < threshold_size)
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L1("Threshold met (size-based)");
|
|
|
|
|
MINFO("Threshold met (size-based)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@@ -569,7 +578,7 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const
|
|
|
|
|
|
|
|
|
|
if ((double)size_used / mei.me_mapsize > resize_percent)
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L1("Threshold met (percent-based)");
|
|
|
|
|
MINFO("Threshold met (percent-based)");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
@@ -581,7 +590,7 @@ bool BlockchainLMDB::need_resize(uint64_t threshold_size) const
|
|
|
|
|
void BlockchainLMDB::check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes)
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
|
|
|
|
LOG_PRINT_L1("[" << __func__ << "] " << "checking DB size");
|
|
|
|
|
MTRACE("[" << __func__ << "] " << "checking DB size");
|
|
|
|
|
const uint64_t min_increase_size = 512 * (1 << 20);
|
|
|
|
|
uint64_t threshold_size = 0;
|
|
|
|
|
uint64_t increase_size = 0;
|
|
|
|
@@ -811,6 +820,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
|
|
|
|
CURSOR(txs_pruned)
|
|
|
|
|
CURSOR(txs_prunable)
|
|
|
|
|
CURSOR(txs_prunable_hash)
|
|
|
|
|
CURSOR(txs_prunable_tip)
|
|
|
|
|
CURSOR(tx_indices)
|
|
|
|
|
|
|
|
|
|
MDB_val_set(val_tx_id, tx_id);
|
|
|
|
@@ -858,6 +868,14 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to add prunable tx blob to db transaction: ", result).c_str()));
|
|
|
|
|
|
|
|
|
|
if (get_blockchain_pruning_seed())
|
|
|
|
|
{
|
|
|
|
|
MDB_val_set(val_height, m_height);
|
|
|
|
|
result = mdb_cursor_put(m_cur_txs_prunable_tip, &val_tx_id, &val_height, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to add prunable tx id to db transaction: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tx.version > 1)
|
|
|
|
|
{
|
|
|
|
|
MDB_val_set(val_prunable_hash, tx_prunable_hash);
|
|
|
|
@@ -883,6 +901,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
|
|
|
|
CURSOR(txs_pruned)
|
|
|
|
|
CURSOR(txs_prunable)
|
|
|
|
|
CURSOR(txs_prunable_hash)
|
|
|
|
|
CURSOR(txs_prunable_tip)
|
|
|
|
|
CURSOR(tx_outputs)
|
|
|
|
|
|
|
|
|
|
MDB_val_set(val_h, tx_hash);
|
|
|
|
@@ -898,11 +917,25 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
|
|
|
|
if (result)
|
|
|
|
|
throw1(DB_ERROR(lmdb_error("Failed to add removal of pruned tx to db transaction: ", result).c_str()));
|
|
|
|
|
|
|
|
|
|
if ((result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, NULL, MDB_SET)))
|
|
|
|
|
result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, NULL, MDB_SET);
|
|
|
|
|
if (result == 0)
|
|
|
|
|
{
|
|
|
|
|
result = mdb_cursor_del(m_cur_txs_prunable, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw1(DB_ERROR(lmdb_error("Failed to add removal of prunable tx to db transaction: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
else if (result != MDB_NOTFOUND)
|
|
|
|
|
throw1(DB_ERROR(lmdb_error("Failed to locate prunable tx for removal: ", result).c_str()));
|
|
|
|
|
result = mdb_cursor_del(m_cur_txs_prunable, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw1(DB_ERROR(lmdb_error("Failed to add removal of prunable tx to db transaction: ", result).c_str()));
|
|
|
|
|
|
|
|
|
|
result = mdb_cursor_get(m_cur_txs_prunable_tip, &val_tx_id, NULL, MDB_SET);
|
|
|
|
|
if (result && result != MDB_NOTFOUND)
|
|
|
|
|
throw1(DB_ERROR(lmdb_error("Failed to locate tx id for removal: ", result).c_str()));
|
|
|
|
|
if (result == 0)
|
|
|
|
|
{
|
|
|
|
|
result = mdb_cursor_del(m_cur_txs_prunable_tip, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw1(DB_ERROR(lmdb_error("Error adding removal of tx id to db transaction", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tx.version > 1)
|
|
|
|
|
{
|
|
|
|
@@ -1308,6 +1341,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
|
|
|
|
|
|
|
|
|
// open necessary databases, and set properties as needed
|
|
|
|
|
// uses macros to avoid having to change things too many places
|
|
|
|
|
// also change blockchain_prune.cpp to match
|
|
|
|
|
lmdb_db_open(txn, LMDB_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_blocks, "Failed to open db handle for m_blocks");
|
|
|
|
|
|
|
|
|
|
lmdb_db_open(txn, LMDB_BLOCK_INFO, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for m_block_info");
|
|
|
|
@@ -1316,7 +1350,9 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
|
|
|
|
lmdb_db_open(txn, LMDB_TXS, MDB_INTEGERKEY | MDB_CREATE, m_txs, "Failed to open db handle for m_txs");
|
|
|
|
|
lmdb_db_open(txn, LMDB_TXS_PRUNED, MDB_INTEGERKEY | MDB_CREATE, m_txs_pruned, "Failed to open db handle for m_txs_pruned");
|
|
|
|
|
lmdb_db_open(txn, LMDB_TXS_PRUNABLE, MDB_INTEGERKEY | MDB_CREATE, m_txs_prunable, "Failed to open db handle for m_txs_prunable");
|
|
|
|
|
lmdb_db_open(txn, LMDB_TXS_PRUNABLE_HASH, MDB_INTEGERKEY | MDB_CREATE, m_txs_prunable_hash, "Failed to open db handle for m_txs_prunable_hash");
|
|
|
|
|
lmdb_db_open(txn, LMDB_TXS_PRUNABLE_HASH, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_txs_prunable_hash, "Failed to open db handle for m_txs_prunable_hash");
|
|
|
|
|
if (!(mdb_flags & MDB_RDONLY))
|
|
|
|
|
lmdb_db_open(txn, LMDB_TXS_PRUNABLE_TIP, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_txs_prunable_tip, "Failed to open db handle for m_txs_prunable_tip");
|
|
|
|
|
lmdb_db_open(txn, LMDB_TX_INDICES, MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_tx_indices, "Failed to open db handle for m_tx_indices");
|
|
|
|
|
lmdb_db_open(txn, LMDB_TX_OUTPUTS, MDB_INTEGERKEY | MDB_CREATE, m_tx_outputs, "Failed to open db handle for m_tx_outputs");
|
|
|
|
|
|
|
|
|
@@ -1344,6 +1380,10 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
|
|
|
|
mdb_set_dupsort(txn, m_output_amounts, compare_uint64);
|
|
|
|
|
mdb_set_dupsort(txn, m_output_txs, compare_uint64);
|
|
|
|
|
mdb_set_dupsort(txn, m_block_info, compare_uint64);
|
|
|
|
|
if (!(mdb_flags & MDB_RDONLY))
|
|
|
|
|
mdb_set_dupsort(txn, m_txs_prunable_tip, compare_uint64);
|
|
|
|
|
mdb_set_compare(txn, m_txs_prunable, compare_uint64);
|
|
|
|
|
mdb_set_dupsort(txn, m_txs_prunable_hash, compare_uint64);
|
|
|
|
|
|
|
|
|
|
mdb_set_compare(txn, m_txpool_meta, compare_hash32);
|
|
|
|
|
mdb_set_compare(txn, m_txpool_blob, compare_hash32);
|
|
|
|
@@ -1502,6 +1542,8 @@ void BlockchainLMDB::reset()
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to drop m_txs_prunable: ", result).c_str()));
|
|
|
|
|
if (auto result = mdb_drop(txn, m_txs_prunable_hash, 0))
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to drop m_txs_prunable_hash: ", result).c_str()));
|
|
|
|
|
if (auto result = mdb_drop(txn, m_txs_prunable_tip, 0))
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to drop m_txs_prunable_tip: ", result).c_str()));
|
|
|
|
|
if (auto result = mdb_drop(txn, m_tx_indices, 0))
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to drop m_tx_indices: ", result).c_str()));
|
|
|
|
|
if (auto result = mdb_drop(txn, m_tx_outputs, 0))
|
|
|
|
@@ -1827,6 +1869,290 @@ cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid
|
|
|
|
|
return bd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t BlockchainLMDB::get_blockchain_pruning_seed() const
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
|
|
|
|
check_open();
|
|
|
|
|
|
|
|
|
|
TXN_PREFIX_RDONLY();
|
|
|
|
|
RCURSOR(properties)
|
|
|
|
|
MDB_val_str(k, "pruning_seed");
|
|
|
|
|
MDB_val v;
|
|
|
|
|
int result = mdb_cursor_get(m_cur_properties, &k, &v, MDB_SET);
|
|
|
|
|
if (result == MDB_NOTFOUND)
|
|
|
|
|
return 0;
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to retrieve pruning seed: ", result).c_str()));
|
|
|
|
|
if (v.mv_size != sizeof(uint32_t))
|
|
|
|
|
throw0(DB_ERROR("Failed to retrieve or create pruning seed: unexpected value size"));
|
|
|
|
|
uint32_t pruning_seed;
|
|
|
|
|
memcpy(&pruning_seed, v.mv_data, sizeof(pruning_seed));
|
|
|
|
|
TXN_POSTFIX_RDONLY();
|
|
|
|
|
return pruning_seed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool is_v1_tx(MDB_cursor *c_txs_pruned, MDB_val *tx_id)
|
|
|
|
|
{
|
|
|
|
|
MDB_val v;
|
|
|
|
|
int ret = mdb_cursor_get(c_txs_pruned, tx_id, &v, MDB_SET);
|
|
|
|
|
if (ret)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to find transaction pruned data: ", ret).c_str()));
|
|
|
|
|
if (v.mv_size == 0)
|
|
|
|
|
throw0(DB_ERROR("Invalid transaction pruned data"));
|
|
|
|
|
return cryptonote::is_v1_tx(cryptonote::blobdata_ref{(const char*)v.mv_data, v.mv_size});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum { prune_mode_prune, prune_mode_update, prune_mode_check };
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
|
|
|
|
const uint32_t log_stripes = tools::get_pruning_log_stripes(pruning_seed);
|
|
|
|
|
if (log_stripes && log_stripes != CRYPTONOTE_PRUNING_LOG_STRIPES)
|
|
|
|
|
throw0(DB_ERROR("Pruning seed not in range"));
|
|
|
|
|
pruning_seed = tools::get_pruning_stripe(pruning_seed);;
|
|
|
|
|
if (pruning_seed > (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES))
|
|
|
|
|
throw0(DB_ERROR("Pruning seed not in range"));
|
|
|
|
|
check_open();
|
|
|
|
|
|
|
|
|
|
TIME_MEASURE_START(t);
|
|
|
|
|
|
|
|
|
|
size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0;
|
|
|
|
|
uint64_t n_bytes = 0;
|
|
|
|
|
|
|
|
|
|
mdb_txn_safe txn;
|
|
|
|
|
auto 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_txs_prunable, &db_stats)))
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to query m_txs_prunable: ", result).c_str()));
|
|
|
|
|
const size_t pages0 = db_stats.ms_branch_pages + db_stats.ms_leaf_pages + db_stats.ms_overflow_pages;
|
|
|
|
|
|
|
|
|
|
MDB_val_str(k, "pruning_seed");
|
|
|
|
|
MDB_val v;
|
|
|
|
|
result = mdb_get(txn, m_properties, &k, &v);
|
|
|
|
|
bool prune_tip_table = false;
|
|
|
|
|
if (result == MDB_NOTFOUND)
|
|
|
|
|
{
|
|
|
|
|
// not pruned yet
|
|
|
|
|
if (mode != prune_mode_prune)
|
|
|
|
|
{
|
|
|
|
|
txn.abort();
|
|
|
|
|
TIME_MEASURE_FINISH(t);
|
|
|
|
|
MDEBUG("Pruning not enabled, nothing to do");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (pruning_seed == 0)
|
|
|
|
|
pruning_seed = tools::get_random_stripe();
|
|
|
|
|
pruning_seed = tools::make_pruning_seed(pruning_seed, CRYPTONOTE_PRUNING_LOG_STRIPES);
|
|
|
|
|
v.mv_data = &pruning_seed;
|
|
|
|
|
v.mv_size = sizeof(pruning_seed);
|
|
|
|
|
result = mdb_put(txn, m_properties, &k, &v, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR("Failed to save pruning seed"));
|
|
|
|
|
prune_tip_table = false;
|
|
|
|
|
}
|
|
|
|
|
else if (result == 0)
|
|
|
|
|
{
|
|
|
|
|
// pruned already
|
|
|
|
|
if (v.mv_size != sizeof(uint32_t))
|
|
|
|
|
throw0(DB_ERROR("Failed to retrieve or create pruning seed: unexpected value size"));
|
|
|
|
|
const uint32_t data = *(const uint32_t*)v.mv_data;
|
|
|
|
|
if (pruning_seed == 0)
|
|
|
|
|
pruning_seed = tools::get_pruning_stripe(data);
|
|
|
|
|
if (tools::get_pruning_stripe(data) != pruning_seed)
|
|
|
|
|
throw0(DB_ERROR("Blockchain already pruned with different seed"));
|
|
|
|
|
if (tools::get_pruning_log_stripes(data) != CRYPTONOTE_PRUNING_LOG_STRIPES)
|
|
|
|
|
throw0(DB_ERROR("Blockchain already pruned with different base"));
|
|
|
|
|
pruning_seed = tools::make_pruning_seed(pruning_seed, CRYPTONOTE_PRUNING_LOG_STRIPES);
|
|
|
|
|
prune_tip_table = (mode == prune_mode_update);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to retrieve or create pruning seed: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode == prune_mode_check)
|
|
|
|
|
MINFO("Checking blockchain pruning...");
|
|
|
|
|
else
|
|
|
|
|
MINFO("Pruning blockchain...");
|
|
|
|
|
|
|
|
|
|
MDB_cursor *c_txs_pruned, *c_txs_prunable, *c_txs_prunable_tip;
|
|
|
|
|
result = mdb_cursor_open(txn, m_txs_pruned, &c_txs_pruned);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_pruned: ", result).c_str()));
|
|
|
|
|
result = mdb_cursor_open(txn, m_txs_prunable, &c_txs_prunable);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable: ", result).c_str()));
|
|
|
|
|
result = mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable_tip: ", result).c_str()));
|
|
|
|
|
const uint64_t blockchain_height = height();
|
|
|
|
|
|
|
|
|
|
if (prune_tip_table)
|
|
|
|
|
{
|
|
|
|
|
MDB_cursor_op op = MDB_FIRST;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
int ret = mdb_cursor_get(c_txs_prunable_tip, &k, &v, op);
|
|
|
|
|
op = MDB_NEXT;
|
|
|
|
|
if (ret == MDB_NOTFOUND)
|
|
|
|
|
break;
|
|
|
|
|
if (ret)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to enumerate transactions: ", ret).c_str()));
|
|
|
|
|
|
|
|
|
|
uint64_t block_height;
|
|
|
|
|
memcpy(&block_height, v.mv_data, sizeof(block_height));
|
|
|
|
|
if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS < blockchain_height)
|
|
|
|
|
{
|
|
|
|
|
++n_total_records;
|
|
|
|
|
if (!tools::has_unpruned_block(block_height, blockchain_height, pruning_seed) && !is_v1_tx(c_txs_pruned, &k))
|
|
|
|
|
{
|
|
|
|
|
++n_prunable_records;
|
|
|
|
|
result = mdb_cursor_get(c_txs_prunable, &k, &v, MDB_SET);
|
|
|
|
|
if (result == MDB_NOTFOUND)
|
|
|
|
|
MWARNING("Already pruned at height " << block_height << "/" << blockchain_height);
|
|
|
|
|
else if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to find transaction prunable data: ", result).c_str()));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MDEBUG("Pruning at height " << block_height << "/" << blockchain_height);
|
|
|
|
|
++n_pruned_records;
|
|
|
|
|
n_bytes += k.mv_size + v.mv_size;
|
|
|
|
|
result = mdb_cursor_del(c_txs_prunable, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to delete transaction prunable data: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result = mdb_cursor_del(c_txs_prunable_tip, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to delete transaction tip data: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MDB_cursor *c_tx_indices;
|
|
|
|
|
result = mdb_cursor_open(txn, m_tx_indices, &c_tx_indices);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for tx_indices: ", result).c_str()));
|
|
|
|
|
MDB_cursor_op op = MDB_FIRST;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
int ret = mdb_cursor_get(c_tx_indices, &k, &v, op);
|
|
|
|
|
op = MDB_NEXT;
|
|
|
|
|
if (ret == MDB_NOTFOUND)
|
|
|
|
|
break;
|
|
|
|
|
if (ret)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to enumerate transactions: ", ret).c_str()));
|
|
|
|
|
|
|
|
|
|
++n_total_records;
|
|
|
|
|
//const txindex *ti = (const txindex *)v.mv_data;
|
|
|
|
|
txindex ti;
|
|
|
|
|
memcpy(&ti, v.mv_data, sizeof(ti));
|
|
|
|
|
const uint64_t block_height = ti.data.block_id;
|
|
|
|
|
if (block_height + CRYPTONOTE_PRUNING_TIP_BLOCKS >= blockchain_height)
|
|
|
|
|
{
|
|
|
|
|
MDB_val_set(kp, ti.data.tx_id);
|
|
|
|
|
MDB_val_set(vp, block_height);
|
|
|
|
|
if (mode == prune_mode_check)
|
|
|
|
|
{
|
|
|
|
|
result = mdb_cursor_get(c_txs_prunable_tip, &kp, &vp, MDB_SET);
|
|
|
|
|
if (result && result != MDB_NOTFOUND)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Error looking for transaction prunable data: ", result).c_str()));
|
|
|
|
|
if (result == MDB_NOTFOUND)
|
|
|
|
|
MERROR("Transaction not found in prunable tip table for height " << block_height << "/" << blockchain_height <<
|
|
|
|
|
", seed " << epee::string_tools::to_string_hex(pruning_seed));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
result = mdb_cursor_put(c_txs_prunable_tip, &kp, &vp, 0);
|
|
|
|
|
if (result && result != MDB_NOTFOUND)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Error looking for transaction prunable data: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MDB_val_set(kp, ti.data.tx_id);
|
|
|
|
|
if (!tools::has_unpruned_block(block_height, blockchain_height, pruning_seed) && !is_v1_tx(c_txs_pruned, &kp))
|
|
|
|
|
{
|
|
|
|
|
result = mdb_cursor_get(c_txs_prunable, &kp, &v, MDB_SET);
|
|
|
|
|
if (result && result != MDB_NOTFOUND)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Error looking for transaction prunable data: ", result).c_str()));
|
|
|
|
|
if (mode == prune_mode_check)
|
|
|
|
|
{
|
|
|
|
|
if (result != MDB_NOTFOUND)
|
|
|
|
|
MERROR("Prunable data found for pruned height " << block_height << "/" << blockchain_height <<
|
|
|
|
|
", seed " << epee::string_tools::to_string_hex(pruning_seed));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++n_prunable_records;
|
|
|
|
|
if (result == MDB_NOTFOUND)
|
|
|
|
|
MWARNING("Already pruned at height " << block_height << "/" << blockchain_height);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MDEBUG("Pruning at height " << block_height << "/" << blockchain_height);
|
|
|
|
|
++n_pruned_records;
|
|
|
|
|
n_bytes += kp.mv_size + v.mv_size;
|
|
|
|
|
result = mdb_cursor_del(c_txs_prunable, 0);
|
|
|
|
|
if (result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to delete transaction prunable data: ", result).c_str()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (mode == prune_mode_check)
|
|
|
|
|
{
|
|
|
|
|
MDB_val_set(kp, ti.data.tx_id);
|
|
|
|
|
result = mdb_cursor_get(c_txs_prunable, &kp, &v, MDB_SET);
|
|
|
|
|
if (result && result != MDB_NOTFOUND)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Error looking for transaction prunable data: ", result).c_str()));
|
|
|
|
|
if (result == MDB_NOTFOUND)
|
|
|
|
|
MERROR("Prunable data not found for unpruned height " << block_height << "/" << blockchain_height <<
|
|
|
|
|
", seed " << epee::string_tools::to_string_hex(pruning_seed));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mdb_cursor_close(c_tx_indices);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((result = mdb_stat(txn, m_txs_prunable, &db_stats)))
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("Failed to query m_txs_prunable: ", result).c_str()));
|
|
|
|
|
const size_t pages1 = db_stats.ms_branch_pages + db_stats.ms_leaf_pages + db_stats.ms_overflow_pages;
|
|
|
|
|
const size_t db_bytes = (pages0 - pages1) * db_stats.ms_psize;
|
|
|
|
|
|
|
|
|
|
mdb_cursor_close(c_txs_prunable_tip);
|
|
|
|
|
mdb_cursor_close(c_txs_prunable);
|
|
|
|
|
mdb_cursor_close(c_txs_pruned);
|
|
|
|
|
|
|
|
|
|
txn.commit();
|
|
|
|
|
|
|
|
|
|
TIME_MEASURE_FINISH(t);
|
|
|
|
|
|
|
|
|
|
MINFO((mode == prune_mode_check ? "Checked" : "Pruned") << " blockchain in " <<
|
|
|
|
|
t << " ms: " << (n_bytes/1024.0f/1024.0f) << " MB (" << db_bytes/1024.0f/1024.0f << " MB) pruned in " <<
|
|
|
|
|
n_pruned_records << " records (" << pages0 - pages1 << "/" << pages0 << " " << db_stats.ms_psize << " byte pages), " <<
|
|
|
|
|
n_prunable_records << "/" << n_total_records << " pruned records");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::prune_blockchain(uint32_t pruning_seed)
|
|
|
|
|
{
|
|
|
|
|
return prune_worker(prune_mode_prune, pruning_seed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::update_pruning()
|
|
|
|
|
{
|
|
|
|
|
return prune_worker(prune_mode_update, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::check_pruning()
|
|
|
|
|
{
|
|
|
|
|
return prune_worker(prune_mode_check, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
|
|
|
@@ -2428,6 +2754,36 @@ bool BlockchainLMDB::get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobd
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
|
|
|
|
check_open();
|
|
|
|
|
|
|
|
|
|
TXN_PREFIX_RDONLY();
|
|
|
|
|
RCURSOR(tx_indices);
|
|
|
|
|
RCURSOR(txs_prunable);
|
|
|
|
|
|
|
|
|
|
MDB_val_set(v, h);
|
|
|
|
|
MDB_val result;
|
|
|
|
|
auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
|
|
|
|
|
if (get_result == 0)
|
|
|
|
|
{
|
|
|
|
|
const txindex *tip = (const txindex *)v.mv_data;
|
|
|
|
|
MDB_val_set(val_tx_id, tip->data.tx_id);
|
|
|
|
|
get_result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &result, MDB_SET);
|
|
|
|
|
}
|
|
|
|
|
if (get_result == MDB_NOTFOUND)
|
|
|
|
|
return false;
|
|
|
|
|
else if (get_result)
|
|
|
|
|
throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str()));
|
|
|
|
|
|
|
|
|
|
bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
|
|
|
|
|
|
|
|
|
|
TXN_POSTFIX_RDONLY();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BlockchainLMDB::get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const
|
|
|
|
|
{
|
|
|
|
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
|
|
|
|