aboutsummaryrefslogtreecommitdiff
path: root/src/blockchain_db/lmdb/db_lmdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/blockchain_db/lmdb/db_lmdb.cpp')
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp402
1 files changed, 379 insertions, 23 deletions
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 2b5fa7fd9..8a1303be9 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -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__);