31 #include <boost/filesystem.hpp>
32 #include <boost/format.hpp>
33 #include <boost/circular_buffer.hpp>
34 #include <boost/archive/text_oarchive.hpp>
35 #include <boost/archive/text_iarchive.hpp>
48 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
49 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "blockchain.db.lmdb"
52 #if defined(__i386) || defined(__x86_64)
53 #define MISALIGNED_OK 1
67 struct pre_rct_output_data_t
76 inline void throw0(
const T &e)
83 inline void throw1(
const T &e)
89 #define MDB_val_set(var, val) MDB_val var = {sizeof(val), (void *)&val}
91 #define MDB_val_sized(var, val) MDB_val var = {val.size(), (void *)val.data()}
93 #define MDB_val_str(var, val) MDB_val var = {strlen(val) + 1, (void *)val}
96 struct MDB_val_copy:
public MDB_val
98 MDB_val_copy(
const T &t) :
101 mv_size =
sizeof (
T);
112 data(new char[bd.size()])
114 memcpy(data.get(), bd.data(), bd.size());
116 mv_data = data.get();
119 std::unique_ptr<char[]> data;
123 struct MDB_val_copy<const char*>:
public MDB_val
125 MDB_val_copy(
const char *s):
130 mv_data = data.get();
135 std::unique_ptr<char[]> data;
146 memcpy(&va,
a->mv_data,
sizeof(va));
148 return (va < vb) ? -1 : va > vb;
155 for (
int n = 7; n >= 0; n--)
159 return va[n] < vb[n] ? -1 : 1;
167 const char *va = (
const char*)
a->mv_data;
168 const char *vb = (
const char*) b->
mv_data;
169 return strcmp(va, vb);
174 size_t size = std::max(
a->mv_size, b->
mv_size);
178 for (
size_t n = 0; n < size; ++n)
182 return va[n] < vb[n] ? -1 : 1;
192 for (
int n = 0; n < 32; ++n)
196 return va[n] < vb[n] ? -1 : 1;
238 const char*
const LMDB_BLOCKS =
"blocks";
239 const char*
const LMDB_BLOCK_HEIGHTS =
"block_heights";
240 const char*
const LMDB_BLOCK_INFO =
"block_info";
242 const char*
const LMDB_TXS =
"txs";
243 const char*
const LMDB_TXS_PRUNED =
"txs_pruned";
244 const char*
const LMDB_TXS_PRUNABLE =
"txs_prunable";
245 const char*
const LMDB_TXS_PRUNABLE_HASH =
"txs_prunable_hash";
246 const char*
const LMDB_TXS_PRUNABLE_TIP =
"txs_prunable_tip";
247 const char*
const LMDB_TX_INDICES =
"tx_indices";
248 const char*
const LMDB_TX_OUTPUTS =
"tx_outputs";
250 const char*
const LMDB_OUTPUT_TXS =
"output_txs";
251 const char*
const LMDB_OUTPUT_AMOUNTS =
"output_amounts";
252 const char*
const LMDB_SPENT_KEYS =
"spent_keys";
254 const char*
const LMDB_TXPOOL_META =
"txpool_meta";
255 const char*
const LMDB_TXPOOL_BLOB =
"txpool_blob";
257 const char*
const LMDB_HF_STARTING_HEIGHTS =
"hf_starting_heights";
258 const char*
const LMDB_HF_VERSIONS =
"hf_versions";
259 const char*
const LMDB_VALIDATORS =
"validators";
260 const char*
const LMDB_PROPERTIES =
"properties";
261 const char*
const LMDB_UTXOS =
"unspent_txos";
262 const char*
const LMDB_ADDR_OUTPUTS =
"unspent_addr_outputs";
263 const char*
const LMDB_TX_INPUTS =
"tx_inputs";
265 const char zerokey[8] = {0};
266 const MDB_val zerokval = {
sizeof(zerokey), (
void *)zerokey };
283 #define CURSOR(name) \
284 if (!m_cur_ ## name) { \
285 int result = mdb_cursor_open(*m_write_txn, m_ ## name, &m_cur_ ## name); \
287 throw0(DB_ERROR(lmdb_error("Failed to open cursor: ", result).c_str())); \
290 #define RCURSOR(name) \
291 if (!m_cur_ ## name) { \
292 int result = mdb_cursor_open(m_txn, m_ ## name, (MDB_cursor **)&m_cur_ ## name); \
294 throw0(DB_ERROR(lmdb_error("Failed to open cursor: ", result).c_str())); \
295 if (m_cursors != &m_wcursors) \
296 m_tinfo->m_ti_rflags.m_rf_ ## name = true; \
297 } else if (m_cursors != &m_wcursors && !m_tinfo->m_ti_rflags.m_rf_ ## name) { \
298 int result = mdb_cursor_renew(m_txn, m_cur_ ## name); \
300 throw0(DB_ERROR(lmdb_error("Failed to renew cursor: ", result).c_str())); \
301 m_tinfo->m_ti_rflags.m_rf_ ## name = true; \
386 std::atomic<uint64_t> mdb_txn_safe::num_active_txns{0};
387 std::atomic_flag mdb_txn_safe::creation_gate = ATOMIC_FLAG_INIT;
389 mdb_threadinfo::~mdb_threadinfo()
391 MDB_cursor **cur = &m_ti_rcursors.m_txc_blocks;
400 mdb_txn_safe::mdb_txn_safe(
const bool check) : m_txn(NULL), m_tinfo(NULL), m_check(check)
419 }
else if (
m_txn !=
nullptr)
423 LOG_PRINT_L0(
"WARNING: mdb_txn_safe: m_txn is a batch txn and it's not NULL in destructor - calling mdb_txn_abort()");
433 LOG_PRINT_L3(
"mdb_txn_safe: m_txn not NULL in destructor - calling mdb_txn_abort()");
450 message =
"Failed to commit a transaction to the db";
471 LOG_PRINT_L0(
"WARNING: mdb_txn_safe: abort() called, but m_txn is NULL");
499 MGINFO(
"LMDB map resize detected.");
510 throw0(
DB_ERROR(lmdb_error(
"Failed to set new mapsize: ", result).c_str()));
515 MGINFO(
"LMDB Mapsize increased." <<
" Old: " << old / (1024 * 1024) <<
"MiB" <<
", New: " << new_mapsize / (1024 * 1024) <<
"MiB");
540 inline void BlockchainLMDB::check_open()
const
543 throw0(DB_ERROR(
"DB operation attempted on a not-open DB instance"));
546 void BlockchainLMDB::do_resize(
uint64_t increase_size)
550 const uint64_t add_size = 1LL << 30;
555 boost::filesystem::path path(m_folder);
556 boost::filesystem::space_info si = boost::filesystem::space(path);
557 if(si.available < add_size)
559 MERROR(
"!! WARNING: Insufficient free space to extend database !!: " <<
560 (si.available >> 20L) <<
" MB available, " << (add_size >> 20L) <<
" MB needed");
567 MWARNING(
"Unable to query free disk space.");
584 if (increase_size > 0)
587 new_mapsize += (new_mapsize % mst.
ms_psize);
591 if (m_write_txn !=
nullptr)
595 throw0(DB_ERROR(
"lmdb resizing not yet supported when batch transactions enabled!"));
599 throw0(DB_ERROR(
"attempting resize with write transaction in progress, this should not happen!"));
607 throw0(DB_ERROR(lmdb_error(
"Failed to set new mapsize: ", result).c_str()));
609 MGINFO(
"LMDB Mapsize increased." <<
" Old: " << mei.
me_mapsize / (1024 * 1024) <<
"MiB" <<
", New: " << new_mapsize / (1024 * 1024) <<
"MiB");
615 bool BlockchainLMDB::need_resize(
uint64_t threshold_size)
const
618 #if defined(ENABLE_AUTO_RESIZE)
634 MDEBUG(
"Space used: " << size_used);
636 MDEBUG(
"Size threshold: " << threshold_size);
637 float resize_percent = RESIZE_PERCENT;
638 MDEBUG(boost::format(
"Percent used: %.04f Percent threshold: %.04f") % ((
double)size_used/mei.
me_mapsize) % resize_percent);
640 if (threshold_size > 0)
642 if (mei.
me_mapsize - size_used < threshold_size)
644 MINFO(
"Threshold met (size-based)");
651 if ((
double)size_used / mei.
me_mapsize > resize_percent)
653 MINFO(
"Threshold met (percent-based)");
662 void BlockchainLMDB::check_and_resize_for_batch(
uint64_t batch_num_blocks,
uint64_t batch_bytes)
665 MTRACE(
"[" << __func__ <<
"] " <<
"checking DB size");
666 const uint64_t min_increase_size = 512 * (1 << 20);
669 if (batch_num_blocks > 0)
671 threshold_size = get_estimated_batch_size(batch_num_blocks, batch_bytes);
672 MDEBUG(
"calculated batch size: " << threshold_size);
680 increase_size = (threshold_size > min_increase_size) ? threshold_size : min_increase_size;
681 MDEBUG(
"increase size: " << increase_size);
687 if (need_resize(threshold_size))
689 MGINFO(
"[batch] DB resize needed");
690 do_resize(increase_size);
701 float batch_safety_factor = 1.7f;
702 float batch_fudge_factor = batch_safety_factor * batch_num_blocks;
705 float db_expand_factor = 4.5f;
713 block_stop = m_height - 1;
715 if (block_stop >= num_prev_blocks)
716 block_start = block_stop - num_prev_blocks + 1;
719 MDEBUG(
"[" << __func__ <<
"] " <<
"m_height: " << m_height <<
" block_start: " << block_start <<
" block_stop: " << block_stop);
720 size_t avg_block_size = 0;
723 avg_block_size = batch_bytes / batch_num_blocks;
728 MDEBUG(
"No existing blocks to check for average block size");
730 else if (m_cum_count >= num_prev_blocks)
732 avg_block_size = m_cum_size / m_cum_count;
733 MDEBUG(
"average block size across recent " << m_cum_count <<
" blocks: " << avg_block_size);
742 for (
uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
748 total_block_size += block_weight;
754 avg_block_size = total_block_size / num_blocks_used;
755 MDEBUG(
"average block size across recent " << num_blocks_used <<
" blocks: " << avg_block_size);
758 if (avg_block_size < min_block_size)
759 avg_block_size = min_block_size;
760 MDEBUG(
"estimated average block size for batch: " << avg_block_size);
763 if (batch_fudge_factor < 5000.0)
764 batch_fudge_factor = 5000.0;
765 threshold_size = avg_block_size * db_expand_factor * batch_fudge_factor;
766 return threshold_size;
781 throw1(BLOCK_EXISTS(
"Attempting to add block that's already in the db"));
791 throw0(DB_ERROR(lmdb_error(
"Failed to get top block hash to check for new block's parent: ", result).c_str()));
794 if (prev->bh_height != m_height - 1)
795 throw0(BLOCK_PARENT_DNE(
"Top block is not new block's parent"));
810 throw0(DB_ERROR(lmdb_error(
"Failed to add block blob to db transaction: ", result).c_str()));
814 bi.bi_timestamp = blk.timestamp;
815 bi.bi_coins = coins_generated;
816 bi.bi_weight = block_weight;
817 bi.bi_diff_hi = ((cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<
uint64_t>();
818 bi.bi_diff_lo = (cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
819 bi.bi_hash = blk_hash;
820 bi.bi_cum_rct = num_rct_outs;
821 if (blk.major_version >= 4)
826 throw1(BLOCK_DNE(lmdb_error(
"Failed to get block info: ", result).c_str()));
830 bi.bi_long_term_block_weight = long_term_block_weight;
835 throw0(DB_ERROR(lmdb_error(
"Failed to add block info to db transaction: ", result).c_str()));
839 throw0(DB_ERROR(lmdb_error(
"Failed to add block height by hash to db transaction: ", result).c_str()));
843 m_cum_size += block_weight;
847 void BlockchainLMDB::remove_block()
856 throw0(BLOCK_DNE (
"Attempting to remove block from an empty blockchain"));
862 MDB_val_copy<uint64_t> k(m_height - 1);
865 throw1(BLOCK_DNE(lmdb_error(
"Attempting to remove block that's not in the db: ", result).c_str()));
873 throw1(DB_ERROR(lmdb_error(
"Failed to locate block height by hash for removal: ", result).c_str()));
875 throw1(DB_ERROR(lmdb_error(
"Failed to add removal of block height by hash to db transaction: ", result).c_str()));
878 throw1(DB_ERROR(lmdb_error(
"Failed to add removal of block to db transaction: ", result).c_str()));
881 throw1(DB_ERROR(lmdb_error(
"Failed to add removal of block info to db transaction: ", result).c_str()));
905 throw1(TX_EXISTS(
std::string(
"Attempting to add transaction that's already in the db (tx id ").append(boost::lexical_cast<std::string>(tip->data.tx_id)).append(
")").c_str()));
913 ti.data.tx_id = tx_id;
915 ti.data.block_id = m_height;
917 val_h.mv_size =
sizeof(ti);
918 val_h.mv_data = (
void *)&ti;
922 throw0(DB_ERROR(lmdb_error(
"Failed to add tx data to db transaction: ", result).c_str()));
928 if (unprunable_size == 0)
930 std::stringstream ss;
934 throw0(DB_ERROR(
"Failed to serialize pruned tx"));
935 unprunable_size = ss.str().size();
938 if (unprunable_size > blob.size())
939 throw0(DB_ERROR(
"pruned tx size is larger than tx size"));
941 MDB_val pruned_blob = {unprunable_size, (
void*)blob.data()};
944 throw0(DB_ERROR(lmdb_error(
"Failed to add pruned tx blob to db transaction: ", result).c_str()));
946 MDB_val prunable_blob = {blob.size() - unprunable_size, (
void*)(blob.data() + unprunable_size)};
949 throw0(DB_ERROR(lmdb_error(
"Failed to add prunable tx blob to db transaction: ", result).c_str()));
951 if (get_blockchain_pruning_seed())
956 throw0(DB_ERROR(lmdb_error(
"Failed to add prunable tx id to db transaction: ", result).c_str()));
964 void BlockchainLMDB::remove_transaction_data(
const crypto::hash& tx_hash,
const transaction& tx)
982 throw1(TX_DNE(
"Attempting to remove transaction that isn't in the db"));
987 throw1(DB_ERROR(lmdb_error(
"Failed to locate pruned tx for removal: ", result).c_str()));
990 throw1(DB_ERROR(lmdb_error(
"Failed to add removal of pruned tx to db transaction: ", result).c_str()));
997 throw1(DB_ERROR(lmdb_error(
"Failed to add removal of prunable tx to db transaction: ", result).c_str()));
1000 throw1(DB_ERROR(lmdb_error(
"Failed to locate prunable tx for removal: ", result).c_str()));
1004 throw1(DB_ERROR(lmdb_error(
"Failed to locate tx id for removal: ", result).c_str()));
1009 throw1(DB_ERROR(lmdb_error(
"Error adding removal of tx id to db transaction", result).c_str()));
1012 if (tx.version == 1) {
1013 remove_tx_outputs(tip->data.tx_id, tx);
1016 LOG_PRINT_L1(
"tx has no outputs to remove: " << tx_hash);
1018 throw1(DB_ERROR(lmdb_error(
"Failed to locate tx outputs for removal: ", result).c_str()));
1022 throw1(DB_ERROR(lmdb_error(
"Failed to add removal of tx outputs to db transaction: ", result).c_str()));
1028 throw1(DB_ERROR(
"Failed to add removal of tx index to db transaction"));
1032 const tx_out& tx_output,
1041 uint64_t m_num_outputs = num_outputs();
1048 if (tx_output.target.type() !=
typeid(txout_to_key))
1049 throw0(DB_ERROR(
"Wrong output type: expected txout_to_key"));
1050 if (tx_output.amount == 0 && !commitment)
1051 throw0(DB_ERROR(
"RCT output without commitment"));
1053 outtx ot = {m_num_outputs, tx_hash, local_index};
1058 throw0(DB_ERROR(lmdb_error(
"Failed to add output tx hash to db transaction: ", result).c_str()));
1062 MDB_val_copy<uint64_t> val_amount(tx_output.amount);
1069 throw0(DB_ERROR(
std::string(
"Failed to get number of outputs for amount: ").append(
mdb_strerror(result)).c_str()));
1070 ok.amount_index = num_elems;
1073 throw0(DB_ERROR(lmdb_error(
"Failed to get output amount in db transaction: ", result).c_str()));
1075 ok.amount_index = 0;
1076 ok.output_id = m_num_outputs;
1077 ok.data.pubkey = boost::get < txout_to_key > (tx_output.target).key;
1078 ok.data.unlock_time = unlock_time;
1079 ok.data.height = m_height;
1080 if (tx_output.amount == 0)
1082 ok.data.commitment = *commitment;
1092 throw0(DB_ERROR(lmdb_error(
"Failed to add output pubkey to db transaction: ", result).c_str()));
1094 return ok.amount_index;
1097 void BlockchainLMDB::add_tx_amount_output_indices(
const uint64_t tx_id,
1098 const std::vector<uint64_t>& amount_output_indices)
1107 size_t num_outputs = amount_output_indices.size();
1111 v.
mv_data = num_outputs ? (
void *)amount_output_indices.data() : (
void*)
"";
1117 throw0(DB_ERROR(
std::string(
"Failed to add <tx hash, amount output index array> to db transaction: ").append(
mdb_strerror(result)).c_str()));
1120 void BlockchainLMDB::remove_tx_outputs(
const uint64_t tx_id,
const transaction& tx)
1124 std::vector<std::vector<uint64_t>> amount_output_indices_set = get_tx_amount_output_indices(tx_id, 1);
1125 const std::vector<uint64_t> &amount_output_indices = amount_output_indices_set.front();
1127 if (amount_output_indices.empty())
1129 if (tx.vout.empty())
1130 LOG_PRINT_L2(
"tx has no outputs, so no output indices");
1132 throw0(DB_ERROR(
"tx has outputs, but no output indices found"));
1135 for (
size_t i = tx.vout.size(); i-- > 0;)
1137 remove_output(tx.vout[i].amount, amount_output_indices[i]);
1141 void BlockchainLMDB::remove_output(
const uint64_t amount,
const uint64_t& out_index)
1154 throw1(OUTPUT_DNE(
"Attempting to get an output index by amount and amount index, but amount not found"));
1156 throw0(DB_ERROR(lmdb_error(
"DB error attempting to get an output", result).c_str()));
1163 throw0(DB_ERROR(
"Unexpected: global output index not found in m_output_txs"));
1167 throw1(DB_ERROR(lmdb_error(
"Error adding removal of output tx to db transaction", result).c_str()));
1171 throw0(DB_ERROR(lmdb_error(
std::string(
"Error deleting output index ").append(boost::lexical_cast<std::string>(out_index).append(
": ")).c_str(), result).c_str()));
1176 throw0(DB_ERROR(lmdb_error(
std::string(
"Error deleting amount for output index ").append(boost::lexical_cast<std::string>(out_index).append(
": ")).c_str(), result).c_str()));
1179 void BlockchainLMDB::prune_outputs(
uint64_t amount)
1187 MINFO(
"Pruning outputs for amount " << amount);
1195 throw0(DB_ERROR(lmdb_error(
"Error looking up outputs: ", result).c_str()));
1200 MINFO(num_elems <<
" outputs found");
1201 std::vector<uint64_t> output_ids;
1202 output_ids.reserve(num_elems);
1206 output_ids.push_back(okp->output_id);
1207 MDEBUG(
"output id " << okp->output_id);
1212 throw0(DB_ERROR(lmdb_error(
"Error counting outputs: ", result).c_str()));
1214 if (output_ids.size() != num_elems)
1215 throw0(DB_ERROR(
"Unexpected number of outputs"));
1219 throw0(DB_ERROR(lmdb_error(
"Error deleting outputs: ", result).c_str()));
1221 for (
uint64_t output_id: output_ids)
1226 throw0(DB_ERROR(lmdb_error(
"Error looking up output: ", result).c_str()));
1229 throw0(DB_ERROR(lmdb_error(
"Error deleting output: ", result).c_str()));
1241 MDB_val k = {
sizeof(k_image), (
void *)&k_image};
1244 throw1(KEY_IMAGE_EXISTS(
"Attempting to add spent key image that's already in the db"));
1246 throw1(DB_ERROR(lmdb_error(
"Error adding spent key image to db transaction: ", result).c_str()));
1258 MDB_val k = {
sizeof(k_image), (
void *)&k_image};
1261 throw1(DB_ERROR(lmdb_error(
"Error finding spent key to remove", result).c_str()));
1266 throw1(DB_ERROR(lmdb_error(
"Error adding removal of key image to db transaction", result).c_str()));
1270 blobdata BlockchainLMDB::output_to_blob(
const tx_out& output)
const
1275 throw1(DB_ERROR(
"Error serializing output to blob"));
1279 tx_out BlockchainLMDB::output_from_blob(
const blobdata& blob)
const
1282 std::stringstream ss;
1288 throw1(DB_ERROR(
"Error deserializing tx output blob"));
1293 blobdata BlockchainLMDB::validator_to_blob(
const validator_db& v)
const
1299 throw1(DB_ERROR(
"Error serializing validator to blob"));
1303 validator_db BlockchainLMDB::validator_from_blob(
const blobdata blob)
const
1306 std::stringstream ss;
1312 throw1(DB_ERROR(
"Error deserializing validator blob"));
1317 BlockchainLMDB::~BlockchainLMDB()
1324 try { batch_abort(); }
1336 m_folder =
"thishsouldnotexistbecauseitisgibberish";
1338 m_batch_transactions = batch_transactions;
1339 m_write_txn =
nullptr;
1340 m_write_batch_txn =
nullptr;
1341 m_batch_active =
false;
1358 throw0(
DB_OPEN_FAILURE(
"Attempted to open db, but it's already open"));
1360 boost::filesystem::path direc(filename);
1361 if (boost::filesystem::exists(direc))
1363 if (!boost::filesystem::is_directory(direc))
1364 throw0(
DB_OPEN_FAILURE(
"LMDB needs a directory path, but a file was passed"));
1368 if (!boost::filesystem::create_directories(direc))
1373 boost::filesystem::path old_files = direc.parent_path();
1377 LOG_PRINT_L0(
"Found existing LMDB files in " << old_files.string());
1379 throw DB_ERROR(
"Database could not be opened");
1382 boost::optional<bool> is_hdd_result =
tools::is_hdd(filename.c_str());
1385 if (is_hdd_result.value())
1389 m_folder = filename;
1399 throw0(
DB_ERROR(lmdb_error(
"Failed to create lmdb environment: ", result).c_str()));
1401 throw0(
DB_ERROR(lmdb_error(
"Failed to set max number of dbs: ", result).c_str()));
1404 if (threads > 110 &&
1406 throw0(
DB_ERROR(lmdb_error(
"Failed to set max number of readers: ", result).c_str()));
1408 size_t mapsize = DEFAULT_MAPSIZE;
1419 if (
auto result =
mdb_env_open(m_env, filename.c_str(), mdb_flags, 0644))
1420 throw0(
DB_ERROR(lmdb_error(
"Failed to open lmdb environment: ", result).c_str()));
1426 if (cur_mapsize < mapsize)
1429 throw0(
DB_ERROR(lmdb_error(
"Failed to set max memory map size: ", result).c_str()));
1437 LOG_PRINT_L0(
"LMDB memory map needs to be resized, doing that now.");
1447 if (
auto mdb_res =
mdb_txn_begin(m_env, NULL, txn_flags, txn))
1448 throw0(
DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", mdb_res).c_str()));
1459 lmdb_db_open(txn, LMDB_TXS_PRUNED,
MDB_INTEGERKEY |
MDB_CREATE, m_txs_pruned,
"Failed to open db handle for m_txs_pruned");
1460 lmdb_db_open(txn, LMDB_TXS_PRUNABLE,
MDB_INTEGERKEY |
MDB_CREATE, m_txs_prunable,
"Failed to open db handle for m_txs_prunable");
1465 lmdb_db_open(txn, LMDB_TX_OUTPUTS,
MDB_INTEGERKEY |
MDB_CREATE, m_tx_outputs,
"Failed to open db handle for m_tx_outputs");
1472 lmdb_db_open(txn, LMDB_TXPOOL_META,
MDB_CREATE, m_txpool_meta,
"Failed to open db handle for m_txpool_meta");
1473 lmdb_db_open(txn, LMDB_TXPOOL_BLOB,
MDB_CREATE, m_txpool_blob,
"Failed to open db handle for m_txpool_blob");
1479 lmdb_db_open(txn, LMDB_HF_STARTING_HEIGHTS,
MDB_CREATE, m_hf_starting_heights,
"Failed to open db handle for m_hf_starting_heights");
1481 lmdb_db_open(txn, LMDB_HF_VERSIONS,
MDB_INTEGERKEY |
MDB_CREATE, m_hf_versions,
"Failed to open db handle for m_hf_versions");
1483 lmdb_db_open(txn, LMDB_VALIDATORS,
MDB_INTEGERKEY |
MDB_CREATE, m_validators,
"Failed to open db handle for m_validators");
1484 lmdb_db_open(txn, LMDB_UTXOS,
MDB_CREATE, m_utxos,
"Failed to open db handle for m_utxos");
1486 lmdb_db_open(txn, LMDB_TX_INPUTS,
MDB_CREATE, m_tx_inputs,
"Failed to open db handle for m_tx_inputs");
1487 lmdb_db_open(txn, LMDB_PROPERTIES,
MDB_CREATE, m_properties,
"Failed to open db handle for m_properties");
1513 result =
mdb_drop(txn, m_hf_starting_heights, 1);
1515 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_hf_starting_heights: ", result).c_str()));
1520 if ((result =
mdb_stat(txn, m_blocks, &db_stats)))
1521 throw0(
DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
1525 bool compatible =
true;
1529 auto get_result =
mdb_get(txn, m_properties, &k, &v);
1535 MWARNING(
"Existing lmdb database was made by a later version (" << db_version <<
"). We don't know how it will change yet.");
1539 else if (db_version <
VERSION)
1546 MFATAL(
"Existing lmdb database needs to be converted, which cannot be done on a read-only database.");
1547 MFATAL(
"Please run electroneumd once to convert the database.");
1555 migrate(db_version);
1565 if (
VERSION > 0 && m_height > 0)
1574 MFATAL(
"Existing lmdb database is incompatible with this version.");
1575 MFATAL(
"Please delete the existing database and resync.");
1585 MDB_val_copy<uint32_t> v(
VERSION);
1586 auto put_result =
mdb_put(txn, m_properties, &k, &v, 0);
1592 MERROR(
"Failed to write version to database.");
1610 LOG_PRINT_L3(
"close() first calling batch_abort() due to active batch transaction");
1633 throw0(
DB_ERROR(lmdb_error(
"Failed to sync database: ", result).c_str()));
1639 MINFO(
"switching safe mode " << (onoff ?
"on" :
"off"));
1650 throw0(
DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
1652 if (
auto result =
mdb_drop(txn, m_blocks, 0))
1653 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_blocks: ", result).c_str()));
1654 if (
auto result =
mdb_drop(txn, m_block_info, 0))
1655 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_block_info: ", result).c_str()));
1656 if (
auto result =
mdb_drop(txn, m_block_heights, 0))
1657 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_block_heights: ", result).c_str()));
1658 if (
auto result =
mdb_drop(txn, m_txs_pruned, 0))
1659 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_txs_pruned: ", result).c_str()));
1660 if (
auto result =
mdb_drop(txn, m_txs_prunable, 0))
1661 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_txs_prunable: ", result).c_str()));
1662 if (
auto result =
mdb_drop(txn, m_txs_prunable_hash, 0))
1663 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_txs_prunable_hash: ", result).c_str()));
1664 if (
auto result =
mdb_drop(txn, m_txs_prunable_tip, 0))
1665 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_txs_prunable_tip: ", result).c_str()));
1666 if (
auto result =
mdb_drop(txn, m_tx_indices, 0))
1667 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_tx_indices: ", result).c_str()));
1668 if (
auto result =
mdb_drop(txn, m_tx_outputs, 0))
1669 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_tx_outputs: ", result).c_str()));
1670 if (
auto result =
mdb_drop(txn, m_output_txs, 0))
1671 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_output_txs: ", result).c_str()));
1672 if (
auto result =
mdb_drop(txn, m_output_amounts, 0))
1673 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_output_amounts: ", result).c_str()));
1674 if (
auto result =
mdb_drop(txn, m_spent_keys, 0))
1675 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_spent_keys: ", result).c_str()));
1676 (void)
mdb_drop(txn, m_hf_starting_heights, 0);
1677 if (
auto result =
mdb_drop(txn, m_hf_versions, 0))
1678 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_hf_versions: ", result).c_str()));
1679 if (
auto result =
mdb_drop(txn, m_validators, 0))
1680 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_validators: ", result).c_str()));
1681 if (
auto result =
mdb_drop(txn, m_utxos, 0))
1682 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_utxos: ", result).c_str()));
1683 if (
auto result =
mdb_drop(txn, m_addr_outputs, 0))
1684 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_addr_outputs: ", result).c_str()));
1685 if (
auto result =
mdb_drop(txn, m_tx_inputs, 0))
1686 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_tx_inputs: ", result).c_str()));
1687 if (
auto result =
mdb_drop(txn, m_properties, 0))
1688 throw0(
DB_ERROR(lmdb_error(
"Failed to drop m_properties: ", result).c_str()));
1692 MDB_val_copy<uint32_t> v(
VERSION);
1693 if (
auto result =
mdb_put(txn, m_properties, &k, &v, 0))
1694 throw0(
DB_ERROR(lmdb_error(
"Failed to write version to database: ", result).c_str()));
1704 std::vector<std::string> filenames;
1706 boost::filesystem::path datafile(m_folder);
1708 boost::filesystem::path lockfile(m_folder);
1711 filenames.push_back(datafile.string());
1712 filenames.push_back(lockfile.string());
1719 const std::string filename = folder +
"/data.mdb";
1722 boost::filesystem::remove(filename);
1724 catch (
const std::exception &e)
1726 MERROR(
"Failed to remove " << filename <<
": " << e.what());
1754 #define TXN_PREFIX(flags); \
1755 mdb_txn_safe auto_txn; \
1756 mdb_txn_safe* txn_ptr = &auto_txn; \
1757 if (m_batch_active) \
1758 txn_ptr = m_write_txn; \
1761 if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \
1762 throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \
1765 #define TXN_PREFIX_RDONLY() \
1767 mdb_txn_cursors *m_cursors; \
1768 mdb_txn_safe auto_txn; \
1769 bool my_rtxn = block_rtxn_start(&m_txn, &m_cursors); \
1770 if (my_rtxn) auto_txn.m_tinfo = m_tinfo.get(); \
1771 else auto_txn.uncheck()
1772 #define TXN_POSTFIX_RDONLY()
1774 #define TXN_POSTFIX_SUCCESS() \
1776 if (! m_batch_active) \
1777 auto_txn.commit(); \
1789 #define TXN_BLOCK_PREFIX(flags); \
1790 mdb_txn_safe auto_txn; \
1791 mdb_txn_safe* txn_ptr = &auto_txn; \
1792 if (m_batch_active || m_write_txn) \
1793 txn_ptr = m_write_txn; \
1796 if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \
1797 throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \
1800 #define TXN_BLOCK_POSTFIX_SUCCESS() \
1802 if (! m_batch_active && ! m_write_txn) \
1803 auto_txn.commit(); \
1807 void BlockchainLMDB::add_chainstate_utxo(
const crypto::hash tx_hash,
const uint32_t relative_out_index,
1818 chainstate_key_t index;
1819 index.tx_hash = tx_hash;
1820 index.relative_out_index = relative_out_index;
1822 chainstate_value_t data;
1823 data.amount = amount;
1824 data.combined_key = combined_key;
1826 data.unlock_time = unlock_time;
1833 throw1(UTXO_EXISTS(
"Attempting to add utxo that's already in the db"));
1835 throw1(DB_ERROR(lmdb_error(
"Error adding utxo to db transaction: ", result).c_str()));
1839 bool BlockchainLMDB::check_chainstate_utxo(
const crypto::hash tx_hash,
const uint32_t relative_out_index)
1847 chainstate_key_t index;
1848 index.tx_hash = tx_hash;
1849 index.relative_out_index = relative_out_index;
1851 MDB_val k = {
sizeof(index), (
void *)&index};
1857 throw1(DB_ERROR(lmdb_error(
"Error finding utxo: ", result).c_str()));
1871 chainstate_key_t index;
1872 index.tx_hash = tx_hash;
1873 index.relative_out_index = relative_out_index;
1875 MDB_val k = {
sizeof(index), (
void *)&index};
1882 throw1(DB_ERROR(lmdb_error(
"Error finding utxo: ", result).c_str()));
1884 auto res = *(
const chainstate_value_t *) v.
mv_data;
1886 return res.unlock_time;
1890 void BlockchainLMDB::remove_chainstate_utxo(
const crypto::hash tx_hash,
const uint32_t relative_out_index)
1898 chainstate_key_t index;
1899 index.tx_hash = tx_hash;
1900 index.relative_out_index = relative_out_index;
1902 MDB_val k = {
sizeof(index), (
void *)&index};
1906 throw1(DB_ERROR(lmdb_error(
"Error finding utxo to remove", result).c_str()));
1911 throw1(DB_ERROR(lmdb_error(
"Error adding removal of utxo to db transaction", result).c_str()));
1925 chainstate_key_t
key;
1926 key.tx_hash = tx_hash;
1927 key.relative_out_index = relative_out_index;
1930 data.tx_hash = parent_tx_hash;
1931 data.in_index = in_index;
1938 throw1(UTXO_EXISTS(
"Attempting to add tx input that's already in the db"));
1940 throw1(DB_ERROR(lmdb_error(
"Error adding tx input to db transaction: ", result).c_str()));
1953 key.tx_hash = tx_hash;
1954 key.relative_out_index = relative_out_index;
1962 throw1(
DB_ERROR(lmdb_error(
"Error finding tx input: ", result).c_str()));
1968 void BlockchainLMDB::remove_tx_input(
const crypto::hash tx_hash,
const uint32_t relative_out_index)
1977 key.tx_hash = tx_hash;
1978 key.relative_out_index = relative_out_index;
1984 throw1(DB_ERROR(lmdb_error(
"Error finding tx input to remove", result).c_str()));
1989 throw1(DB_ERROR(lmdb_error(
"Error adding removal of tx input to db transaction", result).c_str()));
1993 void BlockchainLMDB::add_addr_output(
const crypto::hash tx_hash,
const uint32_t relative_out_index,
2004 MDB_val k = {
sizeof(combined_key), (
void *)&combined_key};
2008 throw1(DB_ERROR(lmdb_error(
"Error finding addr output to add: ", result).c_str()));
2016 throw0(DB_ERROR(
std::string(
"Failed to get number outputs for address: ").append(
mdb_strerror(result)).c_str()));
2019 num_elems =
res.db_index + 1;
2024 acc.tx_hash = tx_hash;
2025 acc.relative_out_index = relative_out_index;
2026 acc.amount = amount;
2027 acc.unlock_time = unlock_time;
2029 k = {
sizeof(combined_key), (
void *)&combined_key};
2030 MDB_val acc_v = {
sizeof(acc), (
void *)&acc};
2034 throw1(UTXO_EXISTS(
"Attempting to add addr output that's already in the db."));
2035 else if(result != 0)
2036 throw1(DB_ERROR(lmdb_error(
"Error adding addr output to db transaction: ", result).c_str()));
2051 MDB_val k = {
sizeof(combined_key), (
void *)&combined_key};
2061 throw0(
DB_ERROR(
"Failed to enumerate address outputs"));
2067 addr_out.tx_hash =
res.tx_hash;
2068 addr_out.relative_out_index =
res.relative_out_index;
2069 addr_out.amount =
res.amount;
2070 addr_out.spent = !check_chainstate_utxo(
res.tx_hash,
res.relative_out_index);
2091 MDB_val k = {
sizeof(combined_key), (
void *)&combined_key};
2102 throw1(
DB_ERROR(lmdb_error(
"Failed to enumerate address outputs", result).c_str()));
2105 std::set<std::string> tx_hashes;
2106 for(
size_t i = 0; i < batch_size + 1; ++i) {
2108 v =
MDB_val{
sizeof(start_db_index), (
void*)&start_db_index};
2115 throw0(
DB_ERROR(
"Failed to enumerate address outputs"));
2120 if(tx_hashes.find(tx_hash_hex) != tx_hashes.end())
2131 addr_out.
spent = !check_chainstate_utxo(
res.tx_hash,
res.relative_out_index);
2134 tx_hashes.emplace(tx_hash_hex);
2151 MDB_val k = {
sizeof(combined_key), (
void *)&combined_key};
2161 throw0(
DB_ERROR(
"Failed to enumerate address outputs"));
2165 if(check_chainstate_utxo(
res.tx_hash,
res.relative_out_index))
2166 balance +=
res.amount;
2174 void BlockchainLMDB::remove_addr_output(
const crypto::hash tx_hash,
const uint32_t relative_out_index,
2186 MDB_val k = {
sizeof(combined_key), (
void *)&combined_key};
2191 throw1(DB_ERROR(lmdb_error(
"Failed to enumerate address outputs", result).c_str()));
2195 k = {
sizeof(combined_key), (
void *)&combined_key};
2201 throw0(DB_ERROR(
"Failed to enumerate outputs"));
2205 if(
res.tx_hash == tx_hash &&
res.relative_out_index == relative_out_index &&
res.amount == amount &&
res.unlock_time == unlock_time ) {
2208 throw1(DB_ERROR(lmdb_error(
"Error removing of addr output from db: ", result).c_str()));
2224 MDB_val k = {
sizeof(txid), (
void *)&txid};
2225 MDB_val v = {
sizeof(meta), (
void *)&meta};
2228 throw1(
DB_ERROR(
"Attempting to add txpool tx metadata that's already in the db"));
2230 throw1(
DB_ERROR(lmdb_error(
"Error adding txpool tx metadata to db transaction: ", result).c_str()));
2235 throw1(
DB_ERROR(
"Attempting to add txpool tx blob that's already in the db"));
2237 throw1(
DB_ERROR(lmdb_error(
"Error adding txpool tx blob to db transaction: ", result).c_str()));
2250 MDB_val k = {
sizeof(txid), (
void *)&txid};
2254 throw1(
DB_ERROR(lmdb_error(
"Error finding txpool tx meta to update: ", result).c_str()));
2257 throw1(
DB_ERROR(lmdb_error(
"Error adding removal of txpool tx metadata to db transaction: ", result).c_str()));
2258 v =
MDB_val({
sizeof(meta), (
void *)&meta});
2261 throw1(
DB_ERROR(
"Attempting to add txpool tx metadata that's already in the db"));
2263 throw1(
DB_ERROR(lmdb_error(
"Error adding txpool tx metadata to db transaction: ", result).c_str()));
2277 if (include_unrelayed_txes)
2281 if ((result =
mdb_stat(m_txn, m_txpool_meta, &db_stats)))
2282 throw0(
DB_ERROR(lmdb_error(
"Failed to query m_txpool_meta: ", result).c_str()));
2301 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate txpool tx metadata: ", result).c_str()));
2320 MDB_val k = {
sizeof(txid), (
void *)&txid};
2323 throw1(
DB_ERROR(lmdb_error(
"Error finding txpool tx meta: ", result).c_str()));
2337 MDB_val k = {
sizeof(txid), (
void *)&txid};
2340 throw1(
DB_ERROR(lmdb_error(
"Error finding txpool tx meta to remove: ", result).c_str()));
2345 throw1(
DB_ERROR(lmdb_error(
"Error adding removal of txpool tx metadata to db transaction: ", result).c_str()));
2349 throw1(
DB_ERROR(lmdb_error(
"Error finding txpool tx blob to remove: ", result).c_str()));
2354 throw1(
DB_ERROR(lmdb_error(
"Error adding removal of txpool tx blob to db transaction: ", result).c_str()));
2366 MDB_val k = {
sizeof(txid), (
void *)&txid};
2372 throw1(
DB_ERROR(lmdb_error(
"Error finding txpool tx meta: ", result).c_str()));
2387 MDB_val k = {
sizeof(txid), (
void *)&txid};
2393 throw1(
DB_ERROR(lmdb_error(
"Error finding txpool tx blob: ", result).c_str()));
2404 throw1(
DB_ERROR(
"Tx not found in txpool: "));
2421 throw0(
DB_ERROR(lmdb_error(
"Failed to retrieve pruning seed: ", result).c_str()));
2423 throw0(
DB_ERROR(
"Failed to retrieve or create pruning seed: unexpected value size"));
2427 return pruning_seed;
2435 throw0(
DB_ERROR(lmdb_error(
"Failed to find transaction pruned data: ", ret).c_str()));
2437 throw0(
DB_ERROR(
"Invalid transaction pruned data"));
2443 bool BlockchainLMDB::prune_worker(
int mode,
uint32_t pruning_seed)
2448 throw0(DB_ERROR(
"Pruning seed not in range"));
2451 throw0(DB_ERROR(
"Pruning seed not in range"));
2456 size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0, commit_counter = 0;
2462 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
2465 if ((result =
mdb_stat(txn, m_txs_prunable, &db_stats)))
2466 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs_prunable: ", result).c_str()));
2471 result =
mdb_get(txn, m_properties, &k, &v);
2472 bool prune_tip_table =
false;
2480 MDEBUG(
"Pruning not enabled, nothing to do");
2483 if (pruning_seed == 0)
2487 v.
mv_size =
sizeof(pruning_seed);
2488 result =
mdb_put(txn, m_properties, &k, &v, 0);
2490 throw0(DB_ERROR(
"Failed to save pruning seed"));
2491 prune_tip_table =
false;
2493 else if (result == 0)
2497 throw0(DB_ERROR(
"Failed to retrieve or create pruning seed: unexpected value size"));
2499 if (pruning_seed == 0)
2502 throw0(DB_ERROR(
"Blockchain already pruned with different seed"));
2504 throw0(DB_ERROR(
"Blockchain already pruned with different base"));
2510 throw0(DB_ERROR(lmdb_error(
"Failed to retrieve or create pruning seed: ", result).c_str()));
2514 MINFO(
"Checking blockchain pruning...");
2516 MINFO(
"Pruning blockchain...");
2518 MDB_cursor *c_txs_pruned, *c_txs_prunable, *c_txs_prunable_tip;
2521 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_pruned: ", result).c_str()));
2524 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable: ", result).c_str()));
2525 result =
mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip);
2527 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable_tip: ", result).c_str()));
2530 if (prune_tip_table)
2540 throw0(DB_ERROR(lmdb_error(
"Failed to enumerate transactions: ", ret).c_str()));
2549 ++n_prunable_records;
2552 MWARNING(
"Already pruned at height " << block_height <<
"/" << blockchain_height);
2554 throw0(DB_ERROR(lmdb_error(
"Failed to find transaction prunable data: ", result).c_str()));
2557 MDEBUG(
"Pruning at height " << block_height <<
"/" << blockchain_height);
2563 throw0(DB_ERROR(lmdb_error(
"Failed to delete transaction prunable data: ", result).c_str()));
2568 throw0(DB_ERROR(lmdb_error(
"Failed to delete transaction tip data: ", result).c_str()));
2572 MDEBUG(
"Committing txn at checkpoint...");
2576 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
2579 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_pruned: ", result).c_str()));
2582 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable: ", result).c_str()));
2583 result =
mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip);
2585 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable_tip: ", result).c_str()));
2596 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for tx_indices: ", result).c_str()));
2605 throw0(DB_ERROR(lmdb_error(
"Failed to enumerate transactions: ", ret).c_str()));
2611 const uint64_t block_height = ti.data.block_id;
2620 throw0(DB_ERROR(lmdb_error(
"Error looking for transaction prunable data: ", result).c_str()));
2622 MERROR(
"Transaction not found in prunable tip table for height " << block_height <<
"/" << blockchain_height <<
2629 throw0(DB_ERROR(lmdb_error(
"Error looking for transaction prunable data: ", result).c_str()));
2637 throw0(DB_ERROR(lmdb_error(
"Error looking for transaction prunable data: ", result).c_str()));
2641 MERROR(
"Prunable data found for pruned height " << block_height <<
"/" << blockchain_height <<
2646 ++n_prunable_records;
2648 MWARNING(
"Already pruned at height " << block_height <<
"/" << blockchain_height);
2651 MDEBUG(
"Pruning at height " << block_height <<
"/" << blockchain_height);
2653 n_bytes += kp.mv_size + v.
mv_size;
2656 throw0(DB_ERROR(lmdb_error(
"Failed to delete transaction prunable data: ", result).c_str()));
2668 throw0(DB_ERROR(lmdb_error(
"Error looking for transaction prunable data: ", result).c_str()));
2670 MERROR(
"Prunable data not found for unpruned height " << block_height <<
"/" << blockchain_height <<
2677 MDEBUG(
"Committing txn at checkpoint...");
2681 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
2684 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_pruned: ", result).c_str()));
2687 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable: ", result).c_str()));
2688 result =
mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip);
2690 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable_tip: ", result).c_str()));
2693 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for tx_indices: ", result).c_str()));
2699 throw0(DB_ERROR(lmdb_error(
"Failed to restore cursor for tx_indices: ", result).c_str()));
2706 if ((result =
mdb_stat(txn, m_txs_prunable, &db_stats)))
2707 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs_prunable: ", result).c_str()));
2709 const size_t db_bytes = (pages0 - pages1) * db_stats.
ms_psize;
2720 t <<
" ms: " << (n_bytes/1024.0f/1024.0f) <<
" MB (" << db_bytes/1024.0f/1024.0f <<
" MB) pruned in " <<
2721 n_pruned_records <<
" records (" << pages0 - pages1 <<
"/" << pages0 <<
" " << db_stats.
ms_psize <<
" byte pages), " <<
2722 n_prunable_records <<
"/" << n_total_records <<
" pruned records");
2762 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate txpool tx metadata: ", result).c_str()));
2765 if (!include_unrelayed_txes && meta.do_not_relay)
2775 throw0(
DB_ERROR(
"Failed to find txpool tx blob to match metadata"));
2777 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate txpool tx blob: ", result).c_str()));
2782 if (!f(txid, meta, passed_bd)) {
2808 else if (get_result)
2809 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch block index from hash", get_result).c_str()));
2843 throw1(
BLOCK_DNE(
"Attempted to retrieve non-existent block height"));
2844 else if (get_result)
2845 throw0(
DB_ERROR(
"Error attempting to retrieve a block height from the db"));
2875 throw0(
BLOCK_DNE(
std::string(
"Attempt to get block from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- block not in db").c_str()));
2877 else if (get_result)
2878 throw0(
DB_ERROR(
"Error attempting to retrieve a block from the db"));
2881 bd.assign(
reinterpret_cast<char*
>(result.
mv_data), result.
mv_size);
2900 throw0(
BLOCK_DNE(
std::string(
"Attempt to get timestamp from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- timestamp not in db").c_str()));
2902 else if (get_result)
2903 throw0(
DB_ERROR(
"Error attempting to retrieve a timestamp from the db"));
2915 std::vector<uint64_t>
res;
2918 if (heights.empty())
2920 res.reserve(heights.size());
2926 if ((result =
mdb_stat(m_txn, m_blocks, &db_stats)))
2927 throw0(
DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
2928 for (
size_t i = 0; i < heights.size(); ++i)
2935 uint64_t range_begin = 0, range_end = 0;
2944 if (
height == prev_height + 1)
2950 if (height < range_begin || height >= range_end)
2959 range_end = range_begin + 1;
2962 throw0(
DB_ERROR(lmdb_error(
"Error attempting to retrieve rct distribution from the db: ", result).c_str()));
3000 throw0(
BLOCK_DNE(
std::string(
"Attempt to get block size from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- block size not in db").c_str()));
3002 else if (get_result)
3003 throw0(
DB_ERROR(
"Error attempting to retrieve a block size from the db"));
3011 std::vector<uint64_t> BlockchainLMDB::get_block_info_64bit_fields(
uint64_t start_height,
size_t count, off_t offset)
const
3020 if (start_height >= h)
3023 std::vector<uint64_t> ret;
3027 uint64_t range_begin = 0, range_end = 0;
3043 if (height < range_begin || height >= range_end)
3052 range_end = range_begin + 1;
3055 throw0(DB_ERROR(lmdb_error(
"Error attempting to retrieve block_info from the db: ", result).c_str()));
3058 ret.push_back(*(
const uint64_t*)(((
const char*)bi) + offset));
3065 uint64_t BlockchainLMDB::get_max_block_size()
3076 return std::numeric_limits<uint64_t>::max();
3078 throw0(DB_ERROR(lmdb_error(
"Failed to retrieve max block size: ", result).c_str()));
3080 throw0(DB_ERROR(
"Failed to retrieve or create max block size: unexpected value size"));
3084 return max_block_size;
3087 void BlockchainLMDB::add_max_block_size(
uint64_t sz)
3099 throw0(DB_ERROR(lmdb_error(
"Failed to retrieve max block size: ", result).c_str()));
3104 throw0(DB_ERROR(
"Failed to retrieve or create max block size: unexpected value size"));
3107 if (sz > max_block_size)
3108 max_block_size = sz;
3109 v.
mv_data = (
void*)&max_block_size;
3110 v.
mv_size =
sizeof(max_block_size);
3113 throw0(DB_ERROR(lmdb_error(
"Failed to set max_block_size: ", result).c_str()));
3119 return get_block_info_64bit_fields(start_height,
count, offsetof(
mdb_block_info, bi_weight));
3124 return get_block_info_64bit_fields(start_height,
count, offsetof(
mdb_block_info, bi_long_term_block_weight));
3141 throw0(
BLOCK_DNE(
std::string(
"Attempt to set cumulative difficulty from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- difficulty not in db").c_str()));
3144 throw0(
DB_ERROR(
"Error attempting to set a cumulative difficulty"));
3154 bi.bi_hash = result_bi->
bi_hash;
3159 throw0(
DB_ERROR(lmdb_error(
"Failed to set cumulative difficulty to db transaction: ", result).c_str()));
3175 throw0(
BLOCK_DNE(
std::string(
"Attempt to get cumulative difficulty from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- difficulty not in db").c_str()));
3177 else if (get_result)
3178 throw0(
DB_ERROR(
"Error attempting to retrieve a cumulative difficulty from the db"));
3202 return diff1 - diff2;
3217 throw0(
BLOCK_DNE(
std::string(
"Attempt to get generated coins from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- block size not in db").c_str()));
3219 else if (get_result)
3220 throw0(
DB_ERROR(
"Error attempting to retrieve a total generated coins from the db"));
3240 throw0(
BLOCK_DNE(
std::string(
"Attempt to get block long term weight from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- block info not in db").c_str()));
3242 else if (get_result)
3243 throw0(
DB_ERROR(
"Error attempting to retrieve a long term block weight from the db"));
3263 throw0(
BLOCK_DNE(
std::string(
"Attempt to get hash from height ").append(boost::lexical_cast<std::string>(
height)).append(
" failed -- hash not in db").c_str()));
3265 else if (get_result)
3266 throw0(
DB_ERROR(lmdb_error(
"Error attempting to retrieve a block hash from the db: ", get_result).c_str()));
3278 std::vector<block> v;
3292 std::vector<crypto::hash> v;
3308 *block_height = m_height - 1;
3341 if ((result =
mdb_stat(m_txn, m_blocks, &db_stats)))
3342 throw0(
DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
3346 uint64_t BlockchainLMDB::num_outputs()
const
3360 else if (result == 0)
3363 throw0(
DB_ERROR(lmdb_error(
"Failed to query m_output_txs: ", result).c_str()));
3377 bool tx_found =
false;
3381 if (get_result == 0)
3426 else if (get_result)
3427 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch transaction from hash", get_result).c_str()));
3446 else if (get_result)
3447 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch tx data from hash: ", get_result).c_str()));
3468 if (get_result == 0)
3473 if (get_result == 0)
3480 else if (get_result)
3481 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch tx from hash", get_result).c_str()));
3483 bd.assign(
reinterpret_cast<char*
>(result0.
mv_data), result0.
mv_size);
3484 bd.append(
reinterpret_cast<char*
>(result1.
mv_data), result1.
mv_size);
3503 if (get_result == 0)
3511 else if (get_result)
3512 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch tx from hash", get_result).c_str()));
3514 bd.assign(
reinterpret_cast<char*
>(result.
mv_data), result.
mv_size);
3533 if (get_result == 0)
3541 else if (get_result)
3542 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch tx from hash", get_result).c_str()));
3544 bd.assign(
reinterpret_cast<char*
>(result.
mv_data), result.
mv_size);
3561 MDB_val result, val_tx_prunable_hash;
3563 if (get_result == 0)
3571 else if (get_result)
3572 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch tx prunable hash from tx hash", get_result).c_str()));
3590 if ((result =
mdb_stat(m_txn, m_txs_pruned, &db_stats)))
3591 throw0(
DB_ERROR(lmdb_error(
"Failed to query m_txs_pruned: ", result).c_str()));
3602 std::vector<transaction> v;
3604 for (
auto& h : hlist)
3626 else if (get_result)
3627 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to fetch tx height from hash", get_result).c_str()));
3643 MDB_val_copy<uint64_t> k(amount);
3652 throw0(
DB_ERROR(
"DB error attempting to get number of outputs of an amount"));
3671 throw1(
OUTPUT_DNE(
std::string(
"Attempting to get output pubkey by index, but key does not exist: amount " +
3673 else if (get_result)
3674 throw0(
DB_ERROR(
"Error attempting to retrieve an output pubkey from the db"));
3685 memcpy(&ret, &okp->
data,
sizeof(pre_rct_output_data_t));;
3686 if (include_commitmemt)
3705 throw1(
OUTPUT_DNE(
"output with given index not in db"));
3706 else if (get_result)
3707 throw0(
DB_ERROR(
"DB error attempting to fetch output tx hash"));
3719 std::vector < uint64_t > offsets;
3720 std::vector<tx_out_index> indices;
3721 offsets.push_back(index);
3723 if (!indices.size())
3724 throw1(
OUTPUT_DNE(
"Attempting to get an output index by amount and amount index, but amount not found"));
3740 std::vector<std::vector<uint64_t>> amount_output_indices_set;
3741 amount_output_indices_set.reserve(n_txes);
3744 while (n_txes-- > 0)
3748 LOG_PRINT_L0(
"WARNING: Unexpected: tx has no amount indices stored in "
3749 "tx_outputs, but it should have an empty entry even if it's a tx without "
3752 throw0(
DB_ERROR(lmdb_error(
"DB error attempting to get data for tx_outputs[tx_index]", result).c_str()));
3759 amount_output_indices_set.resize(amount_output_indices_set.size() + 1);
3760 std::vector<uint64_t> &amount_output_indices = amount_output_indices_set.back();
3761 amount_output_indices.reserve(num_outputs);
3762 for (
size_t i = 0; i < num_outputs; ++i)
3764 amount_output_indices.push_back(indices[i]);
3769 return amount_output_indices_set;
3782 MDB_val k = {
sizeof(img), (
void *)&img};
3809 throw0(
DB_ERROR(
"Failed to enumerate key images"));
3837 k =
MDB_val{
sizeof(h1), (
void*)&h1};
3850 throw0(
DB_ERROR(
"Failed to enumerate blocks"));
3856 throw0(
DB_ERROR(
"Failed to parse block from blob retrieved from the db"));
3859 throw0(
DB_ERROR(
"Failed to get block hash from blob retrieved from the db"));
3895 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate transactions: ", ret).c_str()));
3906 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate transactions: ", ret).c_str()));
3913 throw0(
DB_ERROR(
"Failed to parse tx from blob retrieved from the db"));
3919 throw0(
DB_ERROR(lmdb_error(
"Failed to get prunable tx data the db: ", ret).c_str()));
3922 throw0(
DB_ERROR(
"Failed to parse tx from blob retrieved from the db"));
3955 throw0(
DB_ERROR(
"Failed to enumerate outputs"));
3959 if (!f(amount, toi.first, ok->data.height, toi.second)) {
3990 throw0(
DB_ERROR(
"Failed to enumerate outputs"));
3992 if (amount != out_amount)
3994 MERROR(
"Amount is not the expected amount");
4014 if (! m_batch_transactions)
4015 throw0(
DB_ERROR(
"batch transactions not enabled"));
4018 if (m_write_batch_txn !=
nullptr)
4021 throw0(
DB_ERROR(
"batch transaction attempted, but m_write_txn already in use"));
4024 m_writer = boost::this_thread::get_id();
4025 check_and_resize_for_batch(batch_num_blocks, batch_bytes);
4030 if (
auto mdb_res =
lmdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn))
4032 delete m_write_batch_txn;
4033 m_write_batch_txn =
nullptr;
4034 throw0(
DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", mdb_res).c_str()));
4039 m_write_txn = m_write_batch_txn;
4041 m_batch_active =
true;
4042 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4045 if (m_tinfo->m_ti_rflags.m_rf_txn)
4047 memset(&m_tinfo->m_ti_rflags, 0,
sizeof(m_tinfo->m_ti_rflags));
4057 if (! m_batch_transactions)
4058 throw0(
DB_ERROR(
"batch transactions not enabled"));
4059 if (! m_batch_active)
4060 throw1(
DB_ERROR(
"batch transaction not in progress"));
4061 if (m_write_batch_txn ==
nullptr)
4062 throw1(
DB_ERROR(
"batch transaction not in progress"));
4063 if (m_writer != boost::this_thread::get_id())
4064 throw1(
DB_ERROR(
"batch transaction owned by other thread"));
4075 m_write_txn =
nullptr;
4076 delete m_write_batch_txn;
4077 m_write_batch_txn =
nullptr;
4078 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4081 void BlockchainLMDB::cleanup_batch()
4084 m_write_txn =
nullptr;
4085 delete m_write_batch_txn;
4086 m_write_batch_txn =
nullptr;
4087 m_batch_active =
false;
4088 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4094 if (! m_batch_transactions)
4095 throw0(
DB_ERROR(
"batch transactions not enabled"));
4096 if (! m_batch_active)
4097 throw1(
DB_ERROR(
"batch transaction not in progress"));
4098 if (m_write_batch_txn ==
nullptr)
4099 throw1(
DB_ERROR(
"batch transaction not in progress"));
4100 if (m_writer != boost::this_thread::get_id())
4101 throw1(
DB_ERROR(
"batch transaction owned by other thread"));
4112 catch (
const std::exception &e)
4123 if (! m_batch_transactions)
4124 throw0(
DB_ERROR(
"batch transactions not enabled"));
4125 if (! m_batch_active)
4126 throw1(
DB_ERROR(
"batch transaction not in progress"));
4127 if (m_write_batch_txn ==
nullptr)
4128 throw1(
DB_ERROR(
"batch transaction not in progress"));
4129 if (m_writer != boost::this_thread::get_id())
4130 throw1(
DB_ERROR(
"batch transaction owned by other thread"));
4133 m_write_txn =
nullptr;
4135 m_write_batch_txn->
abort();
4136 delete m_write_batch_txn;
4137 m_write_batch_txn =
nullptr;
4138 m_batch_active =
false;
4139 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4146 if ((batch_transactions) && (m_batch_transactions))
4148 MINFO(
"batch transaction mode already enabled, but asked to enable batch mode");
4150 m_batch_transactions = batch_transactions;
4151 MINFO(
"batch transactions " << (m_batch_transactions ?
"enabled" :
"disabled"));
4159 if (m_write_txn && m_writer == boost::this_thread::get_id()) {
4160 *mtxn = m_write_txn->
m_txn;
4170 m_tinfo.reset(tinfo);
4174 throw0(
DB_ERROR_TXN_START(lmdb_error(
"Failed to create a read transaction for the db: ", mdb_res).c_str()));
4179 throw0(
DB_ERROR_TXN_START(lmdb_error(
"Failed to renew a read transaction for the db: ", mdb_res).c_str()));
4196 memset(&m_tinfo->m_ti_rflags, 0,
sizeof(m_tinfo->m_ti_rflags));
4216 if (! m_batch_active && m_write_txn)
4218 if (! m_batch_active)
4220 m_writer = boost::this_thread::get_id();
4225 m_write_txn =
nullptr;
4226 throw0(
DB_ERROR_TXN_START(lmdb_error(
"Failed to create a transaction for the db: ", mdb_res).c_str()));
4228 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4231 if (m_tinfo->m_ti_rflags.m_rf_txn)
4233 memset(&m_tinfo->m_ti_rflags, 0,
sizeof(m_tinfo->m_ti_rflags));
4235 }
else if (m_writer != boost::this_thread::get_id())
4244 if (m_writer != boost::this_thread::get_id())
4247 if (! m_batch_active)
4255 m_write_txn =
nullptr;
4256 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4266 if (m_writer != boost::this_thread::get_id())
4269 if (! m_batch_active)
4272 m_write_txn =
nullptr;
4273 memset(&m_wcursors, 0,
sizeof(m_wcursors));
4281 memset(&m_tinfo->m_ti_rflags, 0,
sizeof(m_tinfo->m_ti_rflags));
4285 const std::vector<std::pair<transaction, blobdata>>& txs)
4291 if (m_height % 1024 == 0)
4294 if (! m_batch_active && need_resize())
4296 LOG_PRINT_L0(
"LMDB memory map needs to be resized, doing that now.");
4303 BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs);
4313 void BlockchainLMDB::pop_block(
block& blk, std::vector<transaction>& txs)
4322 BlockchainDB::pop_block(blk, txs);
4333 std::vector<tx_out_index> &tx_out_indices)
const
4337 tx_out_indices.clear();
4338 tx_out_indices.reserve(global_indices.size());
4343 for (
const uint64_t &output_id : global_indices)
4349 throw1(
OUTPUT_DNE(
"output with given index not in db"));
4350 else if (get_result)
4351 throw0(
DB_ERROR(
"DB error attempting to fetch output tx hash"));
4362 if (amounts.
size() != 1 && amounts.
size() != offsets.size())
4363 throw0(
DB_ERROR(
"Invalid sizes of amounts and offets"));
4369 outputs.reserve(offsets.size());
4375 for (
size_t i = 0; i < offsets.size(); ++i)
4377 const uint64_t amount = amounts.
size() == 1 ? amounts[0] : amounts[i];
4386 MDEBUG(
"Partial result: " << outputs.size() <<
"/" << offsets.size());
4389 throw1(
OUTPUT_DNE((
std::string(
"Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) +
", index " + boost::lexical_cast<std::string>(offsets[i]) +
", count " + boost::lexical_cast<std::string>(
get_num_outputs(amount)) +
"), but key does not exist (current height " + boost::lexical_cast<std::string>(
height()) +
")").c_str()));
4391 else if (get_result)
4392 throw0(
DB_ERROR(lmdb_error(
"Error attempting to retrieve an output pubkey from the db", get_result).c_str()));
4397 outputs.push_back(okp->
data);
4402 outputs.resize(outputs.size() + 1);
4404 memcpy(&data, &okp->
data,
sizeof(pre_rct_output_data_t));
4421 std::vector <uint64_t> tx_indices;
4422 tx_indices.reserve(offsets.size());
4428 for (
const uint64_t &index : offsets)
4434 throw1(
OUTPUT_DNE(
"Attempting to get output by index, but key does not exist"));
4435 else if (get_result)
4436 throw0(
DB_ERROR(lmdb_error(
"Error attempting to retrieve an output from the db", get_result).c_str()));
4443 if(tx_indices.size() > 0)
4459 std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram;
4463 if (amounts.empty())
4473 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate outputs: ", ret).c_str()));
4477 if (num_elems >= min_count)
4483 for (
const auto &amount: amounts)
4485 MDB_val_copy<uint64_t> k(amount);
4496 if (num_elems >= min_count)
4501 throw0(
DB_ERROR(lmdb_error(
"Failed to enumerate outputs: ", ret).c_str()));
4506 if (unlocked || recent_cutoff > 0) {
4508 for (std::map<
uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>::iterator i = histogram.begin(); i != histogram.end(); ++i) {
4510 uint64_t num_elems = std::get<0>(i->second);
4511 while (num_elems > 0) {
4519 std::get<1>(i->second) = num_elems;
4521 if (recent_cutoff > 0)
4524 while (num_elems > 0) {
4528 if (ts < recent_cutoff)
4534 std::get<2>(i->second) = recent;
4552 distribution.clear();
4554 if (from_height >= db_height)
4556 distribution.resize(db_height - from_height, 0);
4570 throw0(
DB_ERROR(
"Failed to enumerate outputs"));
4574 distribution[
height - from_height]++;
4577 if (to_height > 0 &&
height > to_height)
4581 distribution[0] += base;
4582 for (
size_t n = 1; n < distribution.size(); ++n)
4583 distribution[n] += distribution[n - 1];
4591 void BlockchainLMDB::check_hard_fork_info()
4595 void BlockchainLMDB::drop_hard_fork_info()
4602 auto result =
mdb_drop(*txn_ptr, m_hf_starting_heights, 1);
4604 throw1(DB_ERROR(lmdb_error(
"Error dropping hard fork starting heights db: ", result).c_str()));
4605 result =
mdb_drop(*txn_ptr, m_hf_versions, 1);
4607 throw1(DB_ERROR(lmdb_error(
"Error dropping hard fork versions db: ", result).c_str()));
4612 void BlockchainLMDB::set_validator_list(
std::string validator_list,
uint32_t expiration_date) {
4619 v.validators = std::vector<uint8_t>(validator_list.begin(), validator_list.end());
4620 v.expiration_date = expiration_date;
4622 MDB_val_copy<uint64_t> val_key(0);
4623 MDB_val_copy<blobdata> val_value(validator_to_blob(v));
4627 result =
mdb_put(*txn_ptr, m_validators, &val_key, &val_value, 0);
4629 throw1(DB_ERROR(lmdb_error(
"Error adding validator list to db transaction: ", result).c_str()));
4634 std::string BlockchainLMDB::get_validator_list()
const {
4641 MDB_val_copy<uint64_t> val_key(0);
4645 LOG_PRINT_L1(
"Error attempting to retrieve the list of validators from the db.");
4651 ret.assign(
reinterpret_cast<const char*
>(val_ret.
mv_data), val_ret.
mv_size);
4653 validator_db v = validator_from_blob(ret);
4655 if((v.expiration_date) -
time(
nullptr) <= 0) {
4661 return std::string(v.validators.begin(), v.validators.end());
4671 MDB_val_copy<uint64_t> val_key(
height);
4672 MDB_val_copy<uint8_t> val_value(
version);
4676 result =
mdb_put(*txn_ptr, m_hf_versions, &val_key, &val_value, 0);
4678 throw1(DB_ERROR(lmdb_error(
"Error adding hard fork version to db transaction: ", result).c_str()));
4691 MDB_val_copy<uint64_t> val_key(
height);
4695 throw0(DB_ERROR(lmdb_error(
"Error attempting to retrieve a hard fork version at height " + boost::lexical_cast<std::string>(
height) +
" from the db: ", result).c_str()));
4702 bool BlockchainLMDB::is_read_only()
const
4707 throw0(DB_ERROR(lmdb_error(
"Error getting database environment info: ", result).c_str()));
4715 uint64_t BlockchainLMDB::get_database_size()
const
4718 boost::filesystem::path datafile(m_folder);
4725 void BlockchainLMDB::fixup()
4732 #define RENAME_DB(name) do { \
4735 n2[sizeof(n2)-2]--; \
4737 result = mdb_dbi_open(txn, n2, MDB_CREATE, &tdbi); \
4739 throw0(DB_ERROR(lmdb_error("Failed to create " + std::string(n2) + ": ", result).c_str())); \
4740 result = mdb_drop(txn, tdbi, 1); \
4742 throw0(DB_ERROR(lmdb_error("Failed to delete " + std::string(n2) + ": ", result).c_str())); \
4743 k.mv_data = (void *)name; \
4744 k.mv_size = sizeof(name)-1; \
4745 result = mdb_cursor_open(txn, 1, &c_cur); \
4747 throw0(DB_ERROR(lmdb_error("Failed to open a cursor for " name ": ", result).c_str())); \
4748 result = mdb_cursor_get(c_cur, &k, NULL, MDB_SET_KEY); \
4750 throw0(DB_ERROR(lmdb_error("Failed to get DB record for " name ": ", result).c_str())); \
4751 ptr = (char *)k.mv_data; \
4752 ptr[sizeof(name)-2]++; } while(0)
4754 #define LOGIF(y) if (ELPP->vRegistry()->allowed(y, "global"))
4756 void BlockchainLMDB::migrate_0_1()
4761 mdb_txn_safe txn(
false);
4765 MGINFO_YELLOW(
"Migrating blockchain from DB version 0 to 1 - this may take a while:");
4766 MINFO(
"updating blocks, hf_versions, outputs, txs, and spent_keys tables...");
4771 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
4774 if ((result =
mdb_stat(txn, m_blocks, &db_stats)))
4775 throw0(DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
4777 MINFO(
"Total number of blocks: " << m_height);
4778 MINFO(
"block migration will update block_heights, block_info, and hf_versions...");
4780 MINFO(
"migrating block_heights:");
4786 throw0(DB_ERROR(lmdb_error(
"Failed to retrieve block_heights flags: ", result).c_str()));
4799 o_heights = m_block_heights;
4816 std::cout << i <<
" / " << z <<
" \r" << std::flush;
4821 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
4825 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_heightr: ", result).c_str()));
4828 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_heights: ", result).c_str()));
4831 result =
mdb_stat(txn, m_block_heights, &ms);
4833 throw0(DB_ERROR(lmdb_error(
"Failed to query block_heights table: ", result).c_str()));
4843 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_heights: ", result).c_str()));
4848 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into block_heightr: ", result).c_str()));
4855 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_heights: ", result).c_str()));
4861 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
4863 result =
mdb_drop(txn, o_heights, 1);
4865 throw0(DB_ERROR(lmdb_error(
"Failed to delete old block_heights table: ", result).c_str()));
4886 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
4897 lmdb_db_open(txn,
"block_diffs", 0, diffs,
"Failed to open db handle for block_diffs");
4898 lmdb_db_open(txn,
"block_hashes", 0,
hashes,
"Failed to open db handle for block_hashes");
4899 lmdb_db_open(txn,
"block_sizes", 0, sizes,
"Failed to open db handle for block_sizes");
4900 lmdb_db_open(txn,
"block_timestamps", 0, timestamps,
"Failed to open db handle for block_timestamps");
4901 MDB_cursor *c_cur, *c_coins, *c_diffs, *c_hashes, *c_sizes, *c_timestamps;
4909 std::cout << i <<
" / " << z <<
" \r" << std::flush;
4914 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
4918 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_info: ", result).c_str()));
4921 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_coins: ", result).c_str()));
4924 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_diffs: ", result).c_str()));
4927 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_hashes: ", result).c_str()));
4930 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_coins: ", result).c_str()));
4933 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_timestamps: ", result).c_str()));
4936 result =
mdb_stat(txn, m_block_info, &ms);
4938 throw0(DB_ERROR(lmdb_error(
"Failed to query block_info table: ", result).c_str()));
4946 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_coins: ", result).c_str()));
4951 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_diffs: ", result).c_str()));
4955 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_hashes: ", result).c_str()));
4959 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_sizes: ", result).c_str()));
4966 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_timestamps: ", result).c_str()));
4970 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into block_info: ", result).c_str()));
4973 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_coins: ", result).c_str()));
4976 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_diffs: ", result).c_str()));
4979 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_hashes: ", result).c_str()));
4982 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_sizes: ", result).c_str()));
4985 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_timestamps: ", result).c_str()));
4993 result =
mdb_drop(txn, timestamps, 1);
4995 throw0(DB_ERROR(lmdb_error(
"Failed to delete block_timestamps from the db: ", result).c_str()));
4998 throw0(DB_ERROR(lmdb_error(
"Failed to delete block_sizes from the db: ", result).c_str()));
5001 throw0(DB_ERROR(lmdb_error(
"Failed to delete block_hashes from the db: ", result).c_str()));
5004 throw0(DB_ERROR(lmdb_error(
"Failed to delete block_diffs from the db: ", result).c_str()));
5007 throw0(DB_ERROR(lmdb_error(
"Failed to delete block_coins from the db: ", result).c_str()));
5018 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5021 throw0(DB_ERROR(lmdb_error(
"Failed to retrieve hf_versions flags: ", result).c_str()));
5032 o_hfv = m_hf_versions;
5033 lmdb_db_open(txn,
"hf_versionr",
MDB_INTEGERKEY |
MDB_CREATE, m_hf_versions,
"Failed to open db handle for hf_versionr");
5043 std::cout << i <<
" / " << z <<
" \r" << std::flush;
5048 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5052 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for spent_keyr: ", result).c_str()));
5055 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for spent_keys: ", result).c_str()));
5058 result =
mdb_stat(txn, m_hf_versions, &ms);
5060 throw0(DB_ERROR(lmdb_error(
"Failed to query hf_versions table: ", result).c_str()));
5070 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from hf_versions: ", result).c_str()));
5073 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into hf_versionr: ", result).c_str()));
5076 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from hf_versions: ", result).c_str()));
5082 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5086 throw0(DB_ERROR(lmdb_error(
"Failed to delete old hf_versions table: ", result).c_str()));
5089 lmdb_db_open(txn,
"hf_versions",
MDB_INTEGERKEY, m_hf_versions,
"Failed to open db handle for hf_versions");
5101 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5111 #define DELETE_DB(x) do { \
5112 LOG_PRINT_L1(" " x ":"); \
5113 result = mdb_txn_begin(m_env, NULL, 0, txn); \
5115 throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str())); \
5116 result = mdb_dbi_open(txn, x, 0, &dbi); \
5118 result = mdb_drop(txn, dbi, 1); \
5120 throw0(DB_ERROR(lmdb_error("Failed to delete " x ": ", result).c_str())); \
5136 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5139 lmdb_db_open(txn, LMDB_TX_OUTPUTS,
MDB_INTEGERKEY |
MDB_CREATE, m_tx_outputs,
"Failed to open db handle for m_tx_outputs");
5153 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5156 throw0(DB_ERROR(lmdb_error(
"Failed to retrieve txs flags: ", result).c_str()));
5175 MDB_cursor *c_blocks, *c_txs, *c_props, *c_cur;
5182 txn.m_txn = m_write_txn->
m_txn;
5189 std::cout << i <<
" / " << z <<
" \r" << std::flush;
5195 throw0(DB_ERROR(lmdb_error(
"Failed to update txblk property: ", result).c_str()));
5199 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5200 m_write_txn->
m_txn = txn.m_txn;
5201 m_write_batch_txn->
m_txn = txn.m_txn;
5202 memset(&m_wcursors, 0,
sizeof(m_wcursors));
5206 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for blocks: ", result).c_str()));
5209 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for properties: ", result).c_str()));
5212 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs: ", result).c_str()));
5215 result =
mdb_stat(txn, m_txs, &ms);
5217 throw0(DB_ERROR(lmdb_error(
"Failed to query txs table: ", result).c_str()));
5223 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from properties: ", result).c_str()));
5230 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from blocks: ", result).c_str()));
5238 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from props: ", result).c_str()));
5241 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from props: ", result).c_str()));
5245 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from blocks: ", result).c_str()));
5249 throw0(DB_ERROR(
"Failed to parse block from blob retrieved from the db"));
5252 for (
unsigned int j = 0; j<b.tx_hashes.size(); j++) {
5257 throw0(DB_ERROR(lmdb_error(
"Failed to get record from txs: ", result).c_str()));
5260 throw0(DB_ERROR(
"Failed to parse tx from blob retrieved from the db"));
5264 throw0(DB_ERROR(lmdb_error(
"Failed to get record from txs: ", result).c_str()));
5271 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5274 throw0(DB_ERROR(lmdb_error(
"Failed to delete txs from the db: ", result).c_str()));
5280 lmdb_db_open(txn,
"txs",
MDB_INTEGERKEY, m_txs,
"Failed to open db handle for txs");
5291 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5292 result =
mdb_put(txn, m_properties, &vk, &v, 0);
5294 throw0(DB_ERROR(lmdb_error(
"Failed to update version for the db: ", result).c_str()));
5298 void BlockchainLMDB::migrate_1_2()
5303 mdb_txn_safe txn(
false);
5307 MGINFO_YELLOW(
"Migrating blockchain from DB version 1 to 2 - this may take a while:");
5308 MINFO(
"updating txs_pruned and txs_prunable tables...");
5313 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5318 MDB_stat db_stats_txs_prunable_hash;
5319 if ((result =
mdb_stat(txn, m_txs, &db_stats_txs)))
5320 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs: ", result).c_str()));
5321 if ((result =
mdb_stat(txn, m_txs_pruned, &db_stats_txs_pruned)))
5322 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs_pruned: ", result).c_str()));
5323 if ((result =
mdb_stat(txn, m_txs_prunable, &db_stats_txs_prunable)))
5324 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs_prunable: ", result).c_str()));
5325 if ((result =
mdb_stat(txn, m_txs_prunable_hash, &db_stats_txs_prunable_hash)))
5326 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs_prunable_hash: ", result).c_str()));
5328 throw0(DB_ERROR(
"Mismatched sizes for txs_pruned and txs_prunable"));
5332 MINFO(
"txs already migrated");
5336 MINFO(
"updating txs tables:");
5338 MDB_cursor *c_old, *c_cur0, *c_cur1, *c_cur2;
5344 result =
mdb_stat(txn, m_txs, &db_stats_txs);
5346 throw0(DB_ERROR(lmdb_error(
"Failed to query m_txs: ", result).c_str()));
5348 std::cout << i <<
" / " << (i + db_stats_txs.
ms_entries) <<
" \r" << std::flush;
5353 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5357 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_pruned: ", result).c_str()));
5360 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable: ", result).c_str()));
5363 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs_prunable_hash: ", result).c_str()));
5366 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for txs: ", result).c_str()));
5378 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from txs: ", result).c_str()));
5384 throw0(DB_ERROR(
"Failed to parse tx from blob retrieved from the db"));
5385 std::stringstream ss;
5387 bool r = tx.serialize_base(ba);
5389 throw0(DB_ERROR(
"Failed to serialize pruned tx"));
5392 if (pruned.size() > bd.size())
5393 throw0(DB_ERROR(
"Pruned tx is larger than raw tx"));
5394 if (memcmp(pruned.data(), bd.data(), pruned.size()))
5395 throw0(DB_ERROR(
"Pruned tx is not a prefix of the raw tx"));
5398 nv.
mv_data = (
void*)pruned.data();
5402 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into txs_pruned: ", result).c_str()));
5404 nv.
mv_data = (
void*)(bd.data() + pruned.size());
5405 nv.
mv_size = bd.size() - pruned.size();
5408 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into txs_prunable: ", result).c_str()));
5412 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from txs: ", result).c_str()));
5424 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5425 result =
mdb_put(txn, m_properties, &vk, &v, 0);
5427 throw0(DB_ERROR(lmdb_error(
"Failed to update version for the db: ", result).c_str()));
5431 void BlockchainLMDB::migrate_2_3()
5436 mdb_txn_safe txn(
false);
5440 MGINFO_YELLOW(
"Migrating blockchain from DB version 2 to 3 - this may take a while:");
5447 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5450 if ((result =
mdb_stat(txn, m_blocks, &db_stats)))
5451 throw0(DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
5454 MDEBUG(
"enumerating rct outputs...");
5455 std::vector<uint64_t> distribution(blockchain_height, 0);
5457 if (
height >= blockchain_height)
5459 MERROR(
"Output found claiming height >= blockchain height");
5466 throw0(DB_ERROR(
"Failed to build rct output distribution"));
5467 for (
size_t i = 1; i < distribution.size(); ++i)
5468 distribution[i] += distribution[i - 1];
5474 MDB_dbi o_block_info = m_block_info;
5484 std::cout << i <<
" / " << blockchain_height <<
" \r" << std::flush;
5489 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5493 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_infn: ", result).c_str()));
5496 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_info: ", result).c_str()));
5499 result =
mdb_stat(txn, m_block_info, &db_stats);
5501 throw0(DB_ERROR(lmdb_error(
"Failed to query m_block_info: ", result).c_str()));
5511 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_info: ", result).c_str()));
5515 bi.bi_timestamp = bi_old->bi_timestamp;
5516 bi.bi_coins = bi_old->bi_coins;
5517 bi.bi_weight = bi_old->bi_weight;
5518 bi.bi_diff = bi_old->bi_diff;
5519 bi.bi_hash = bi_old->bi_hash;
5520 if (bi_old->bi_height >= distribution.size())
5521 throw0(DB_ERROR(
"Bad height in block_info record"));
5522 bi.bi_cum_rct = distribution[bi_old->bi_height];
5526 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into block_infn: ", result).c_str()));
5533 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_info: ", result).c_str()));
5539 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5541 result =
mdb_drop(txn, o_block_info, 1);
5543 throw0(DB_ERROR(lmdb_error(
"Failed to delete old block_info table: ", result).c_str()));
5560 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5561 result =
mdb_put(txn, m_properties, &vk, &v, 0);
5563 throw0(DB_ERROR(lmdb_error(
"Failed to update version for the db: ", result).c_str()));
5567 void BlockchainLMDB::migrate_3_4()
5572 mdb_txn_safe txn(
false);
5575 bool past_long_term_weight =
false;
5577 MGINFO_YELLOW(
"Migrating blockchain from DB version 3 to 4 - this may take a while:");
5584 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5587 if ((result =
mdb_stat(txn, m_blocks, &db_stats)))
5588 throw0(DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
5597 MDB_dbi o_block_info = m_block_info;
5605 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for blocks: ", result).c_str()));
5613 std::cout << i <<
" / " << blockchain_height <<
" \r" << std::flush;
5618 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5622 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_infn: ", result).c_str()));
5625 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_info: ", result).c_str()));
5628 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for blocks: ", result).c_str()));
5631 result =
mdb_stat(txn, m_block_info, &db_stats);
5633 throw0(DB_ERROR(lmdb_error(
"Failed to query m_block_info: ", result).c_str()));
5643 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_info: ", result).c_str()));
5647 bi.bi_timestamp = bi_old->bi_timestamp;
5648 bi.bi_coins = bi_old->bi_coins;
5649 bi.bi_weight = bi_old->bi_weight;
5650 bi.bi_diff = bi_old->bi_diff;
5651 bi.bi_hash = bi_old->bi_hash;
5652 bi.bi_cum_rct = bi_old->bi_cum_rct;
5655 if (!past_long_term_weight)
5657 MDB_val_copy<uint64_t> kb(bi.bi_height);
5661 throw0(DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
5663 throw0(DB_ERROR(
"Invalid data from m_blocks"));
5666 past_long_term_weight =
true;
5670 if (past_long_term_weight)
5672 std::vector<uint64_t> weights(long_term_block_weights.begin(), long_term_block_weights.end());
5674 long_term_block_weight = std::min<uint64_t>(bi.bi_weight, long_term_effective_block_median_weight + long_term_effective_block_median_weight * 2 / 5);
5678 long_term_block_weight = bi.bi_weight;
5680 long_term_block_weights.push_back(long_term_block_weight);
5681 bi.bi_long_term_block_weight = long_term_block_weight;
5686 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into block_infn: ", result).c_str()));
5693 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_info: ", result).c_str()));
5699 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5701 result =
mdb_drop(txn, o_block_info, 1);
5703 throw0(DB_ERROR(lmdb_error(
"Failed to delete old block_info table: ", result).c_str()));
5720 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5721 result =
mdb_put(txn, m_properties, &vk, &v, 0);
5723 throw0(DB_ERROR(lmdb_error(
"Failed to update version for the db: ", result).c_str()));
5727 void BlockchainLMDB::migrate_4_5()
5732 mdb_txn_safe txn(
false);
5736 MGINFO_YELLOW(
"Migrating blockchain from DB version 4 to 5 - this may take a while:");
5743 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5746 if ((result =
mdb_stat(txn, m_blocks, &db_stats)))
5747 throw0(DB_ERROR(lmdb_error(
"Failed to query m_blocks: ", result).c_str()));
5754 MDB_dbi o_block_info = m_block_info;
5762 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for blocks: ", result).c_str()));
5770 std::cout << i <<
" / " << blockchain_height <<
" \r" << std::flush;
5775 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5779 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_infn: ", result).c_str()));
5782 throw0(DB_ERROR(lmdb_error(
"Failed to open a cursor for block_info: ", result).c_str()));
5785 result =
mdb_stat(txn, m_block_info, &db_stats);
5787 throw0(DB_ERROR(lmdb_error(
"Failed to query m_block_info: ", result).c_str()));
5797 throw0(DB_ERROR(lmdb_error(
"Failed to get a record from block_info: ", result).c_str()));
5801 bi.bi_timestamp = bi_old->bi_timestamp;
5802 bi.bi_coins = bi_old->bi_coins;
5803 bi.bi_weight = bi_old->bi_weight;
5804 bi.bi_diff_lo = bi_old->bi_diff;
5806 bi.bi_hash = bi_old->bi_hash;
5807 bi.bi_cum_rct = bi_old->bi_cum_rct;
5808 bi.bi_long_term_block_weight = bi_old->bi_long_term_block_weight;
5813 throw0(DB_ERROR(lmdb_error(
"Failed to put a record into block_infn: ", result).c_str()));
5820 throw0(DB_ERROR(lmdb_error(
"Failed to delete a record from block_info: ", result).c_str()));
5826 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5828 result =
mdb_drop(txn, o_block_info, 1);
5830 throw0(DB_ERROR(lmdb_error(
"Failed to delete old block_info table: ", result).c_str()));
5847 throw0(DB_ERROR(lmdb_error(
"Failed to create a transaction for the db: ", result).c_str()));
5848 result =
mdb_put(txn, m_properties, &vk, &v, 0);
5850 throw0(DB_ERROR(lmdb_error(
"Failed to update version for the db: ", result).c_str()));
5854 void BlockchainLMDB::migrate(
const uint32_t oldversion)
int compare_uint64(const MDB_val *a, const MDB_val *b)
thrown when a requested block does not exist
The BlockchainDB backing store interface declaration/contract.
virtual block get_block_from_height(const uint64_t &height) const
fetch a block by height
bool m_open
Whether or not the BlockchainDB is open/ready for use.
virtual block get_block(const crypto::hash &h) const
fetches the block with the given hash
epee::critical_section m_synchronization_lock
A lock, currently for when BlockchainLMDB needs to resize the backing db file.
void add_transaction(const crypto::hash &blk_hash, const std::pair< transaction, blobdata > &tx, const crypto::hash *tx_hash_ptr=NULL, const crypto::hash *tx_prunable_hash_ptr=NULL)
helper function for add_transactions, to add each individual transaction
virtual void fixup()
fix up anything that may be wrong due to past bugs
virtual transaction get_tx(const crypto::hash &h) const
fetches the transaction with the given hash
uint64_t time_tx_exists
a performance metric
uint64_t time_commit1
a performance metric
virtual bool update_pruning()
prunes recent blockchain changes as needed, iff pruning is enabled
virtual size_t get_block_weight(const uint64_t &height) const
fetch a block's weight
static int compare_string(const MDB_val *a, const MDB_val *b)
virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta)
add a txpool transaction
virtual bool for_blocks_range(const uint64_t &h1, const uint64_t &h2, std::function< bool(uint64_t, const crypto::hash &, const cryptonote::block &)>) const
runs a function over a range of blocks
virtual uint32_t get_blockchain_pruning_seed() const
get the blockchain pruning seed
virtual std::vector< uint64_t > get_block_cumulative_rct_outputs(const std::vector< uint64_t > &heights) const
fetch a block's cumulative number of rct outputs
virtual void batch_stop()
ends a batch transaction
virtual void open(const std::string &filename, const int mdb_flags=0)
open a db, or create it if necessary.
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t &index) const
gets an output's tx hash and index
virtual void set_batch_transactions(bool batch_transactions)
sets whether or not to batch transactions
virtual void reset()
Remove everything from the BlockchainDB.
virtual void unlock()
This function releases the BlockchainDB lock.
virtual block get_top_block() const
fetch the top block
virtual void block_rtxn_stop() const
virtual difficulty_type get_block_difficulty(const uint64_t &height) const
fetch a block's difficulty
virtual std::vector< std::vector< uint64_t > > get_tx_amount_output_indices(const uint64_t tx_id, size_t n_txes) const
gets output indices (amount-specific) for a transaction's outputs
virtual tx_input_t get_tx_input(const crypto::hash tx_hash, const uint32_t relative_out_index)
static int compare_data(const MDB_val *a, const MDB_val *b)
virtual uint64_t add_block(const std::pair< block, blobdata > &blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type &cumulative_difficulty, const uint64_t &coins_generated, const std::vector< std::pair< transaction, blobdata >> &txs)
handles the addition of a new block to BlockchainDB
virtual block_header get_block_header(const crypto::hash &h) const
fetch a block header
virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes=true) const
get the number of transactions in the txpool
std::map< uint64_t, std::tuple< uint64_t, uint64_t, uint64_t > > get_output_histogram(const std::vector< uint64_t > &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const
return a histogram of outputs on the blockchain
virtual void batch_abort()
aborts a batch transaction
virtual bool get_txpool_tx_meta(const crypto::hash &txid, txpool_tx_meta_t &meta) const
get a txpool transaction's metadata
virtual bool has_key_image(const crypto::key_image &img) const
check if a key image is stored as spent
virtual crypto::hash top_block_hash(uint64_t *block_height=NULL) const
fetch the top block's hash
virtual void block_wtxn_abort()
virtual uint64_t get_block_already_generated_coins(const uint64_t &height) const
fetch a block's already generated coins
virtual std::vector< uint64_t > get_long_term_block_weights(uint64_t start_height, size_t count) const
fetch the last N blocks' long term weights
static int compare_publickey(const MDB_val *a, const MDB_val *b)
virtual uint64_t get_tx_count() const
fetches the total number of transactions ever
virtual bool block_exists(const crypto::hash &h, uint64_t *height=NULL) const
checks if a block exists
virtual bool get_txpool_tx_blob(const crypto::hash &txid, cryptonote::blobdata &bd) const
get a txpool transaction's blob
virtual bool get_pruned_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const
fetches the pruned transaction blob with the given hash
virtual uint64_t get_num_outputs(const uint64_t &amount) const
fetches the number of outputs of a given amount
virtual uint64_t get_block_height(const crypto::hash &h) const
gets the height of the block with a given hash
virtual std::vector< block > get_blocks_range(const uint64_t &h1, const uint64_t &h2) const
fetch a list of blocks
virtual bool tx_exists(const crypto::hash &h) const
check if a transaction with a given hash exists
virtual bool block_rtxn_start() const
virtual void block_rtxn_abort() const
virtual std::vector< uint64_t > get_block_weights(uint64_t start_height, size_t count) const
fetch the last N blocks' weights
virtual std::vector< transaction > get_tx_list(const std::vector< crypto::hash > &hlist) const
fetches a list of transactions based on their hashes
virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0)
tells the BlockchainDB to start a new "batch" of blocks
virtual void set_block_cumulative_difficulty(uint64_t height, difficulty_type diff)
sets a block's cumulative difficulty
virtual void remove_txpool_tx(const crypto::hash &txid)
remove a txpool transaction
virtual bool for_all_transactions(std::function< bool(const crypto::hash &, const cryptonote::transaction &)>, bool pruned) const
runs a function over all transactions stored
virtual uint64_t get_block_long_term_weight(const uint64_t &height) const
fetch a block's long term weight
virtual std::vector< address_outputs > get_addr_output_all(const crypto::public_key &combined_key)
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t &height) const
fetch a block's cumulative difficulty
virtual bool txpool_has_tx(const crypto::hash &txid) const
check whether a txid is in the txpool
virtual bool check_pruning()
checks pruning was done correctly, iff enabled
virtual void batch_commit()
virtual uint64_t get_block_timestamp(const uint64_t &height) const
fetch a block's timestamp
virtual bool lock()
acquires the BlockchainDB lock
virtual bool get_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const
fetches the transaction blob with the given hash
virtual bool get_prunable_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const
fetches the prunable transaction blob with the given hash
virtual output_data_t get_output_key(const uint64_t &amount, const uint64_t &index, bool include_commitmemt) const
get some of an output's data
virtual uint64_t get_top_block_timestamp() const
fetch the top block's timestamp
virtual void safesyncmode(const bool onoff)
toggle safe syncs for the DB
virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta)
update a txpool transaction's metadata
virtual bool get_prunable_tx_hash(const crypto::hash &tx_hash, crypto::hash &prunable_hash) const
fetches the prunable transaction hash
virtual std::string get_db_name() const
gets the name of the folder the BlockchainDB's file(s) should be in
virtual void block_wtxn_start()
virtual tx_out_index get_output_tx_and_index(const uint64_t &amount, const uint64_t &index) const
gets an output's tx hash and index
virtual cryptonote::blobdata get_block_blob(const crypto::hash &h) const
fetches the block with the given hash
virtual void close()
close the BlockchainDB
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t &height) const
fetch a block blob by height
virtual uint64_t height() const
fetch the current blockchain height
virtual void block_wtxn_stop()
virtual bool for_all_outputs(std::function< bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const
runs a function over all outputs stored
virtual std::vector< crypto::hash > get_hashes_range(const uint64_t &h1, const uint64_t &h2) const
fetch a list of block hashes
virtual std::vector< address_outputs > get_addr_output_batch(const crypto::public_key &combined_key, uint64_t start_db_index=0, uint64_t batch_size=100, bool desc=false)
bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector< uint64_t > &distribution, uint64_t &base) const
virtual std::vector< std::string > get_filenames() const
get all files used by the BlockchainDB (if any)
virtual uint64_t get_balance(const crypto::public_key &combined_key)
virtual bool prune_blockchain(uint32_t pruning_seed=0)
prunes the blockchain
virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const
fetch a block's hash
static int compare_hash32(const MDB_val *a, const MDB_val *b)
virtual bool for_all_key_images(std::function< bool(const crypto::key_image &)>) const
runs a function over all key images stored
virtual uint64_t get_tx_block_height(const crypto::hash &h) const
fetches the height of a transaction's block
static int compare_uint64(const MDB_val *a, const MDB_val *b)
virtual void sync()
sync the BlockchainDB with disk
virtual uint64_t get_tx_unlock_time(const crypto::hash &h) const
fetch a transaction's unlock time/height
virtual bool for_all_txpool_txes(std::function< bool(const crypto::hash &, const txpool_tx_meta_t &, const cryptonote::blobdata *)> f, bool include_blob=false, bool include_unrelayed_txes=true) const
runs a function over all txpool transactions
virtual bool remove_data_file(const std::string &folder) const
remove file(s) storing the database
thrown when there is an error starting a DB transaction
A generic BlockchainDB exception.
thrown when opening the BlockchainDB fails
thrown when a requested output does not exist
thrown when a requested transaction does not exist
std::atomic< unsigned int > unprunable_size
Non-owning sequence of data. Does not deep copy.
constexpr std::size_t size() const noexcept
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5
#define ETN_DEFAULT_TX_SPENDABLE_AGE_V8
#define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME
#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT
#define CRYPTONOTE_PRUNING_LOG_STRIPES
#define CRYPTONOTE_PRUNING_TIP_BLOCKS
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
#define CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE
#define TXN_POSTFIX_RDONLY()
#define TXN_PREFIX_RDONLY()
#define TXN_POSTFIX_SUCCESS()
#define TXN_BLOCK_PREFIX(flags)
#define TXN_PREFIX(flags)
#define TXN_BLOCK_POSTFIX_SUCCESS()
#define MDB_val_sized(var, val)
#define MDB_val_str(var, val)
#define MDB_val_set(var, val)
#define m_cur_txpool_blob
#define m_cur_txpool_meta
#define m_cur_hf_versions
#define m_cur_addr_outputs
#define m_cur_txs_prunable_tip
#define m_cur_txs_prunable
#define m_cur_block_heights
#define m_cur_txs_prunable_hash
#define m_cur_output_amounts
std::string message("Message requiring signing")
void * memcpy(void *a, const void *b, size_t c)
void mdb_txn_reset(MDB_txn *txn)
Reset a read-only transaction.
MDB_cursor_op
Cursor Get operations.
int mdb_cursor_count(MDB_cursor *cursor, mdb_size_t *countp)
Return count of duplicates for current key.
int mdb_env_info(MDB_env *env, MDB_envinfo *stat)
Return information about the LMDB environment.
int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, unsigned int flags)
Store by cursor.
int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags)
Delete current key/data pair.
int mdb_env_get_flags(MDB_env *env, unsigned int *flags)
Get environment flags.
int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
Open an environment handle.
void mdb_env_close(MDB_env *env)
Close the environment and release the memory map.
int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, MDB_cursor_op op)
Retrieve by cursor.
int mdb_env_set_mapsize(MDB_env *env, mdb_size_t size)
Set the size of the memory map to use for this environment.
int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned int flags)
Store items into a database.
void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
Close a database handle. Normally unnecessary. Use with care:
char * mdb_strerror(int err)
Return a string describing a given error code.
int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
Set a custom key comparison function for a database.
int mdb_txn_renew(MDB_txn *txn)
Renew a read-only transaction.
void mdb_txn_abort(MDB_txn *txn)
Abandon all the operations of the transaction instead of saving them.
int mdb_env_set_flags(MDB_env *env, unsigned int flags, int onoff)
Set environment flags.
int mdb_txn_commit(MDB_txn *txn)
Commit all the operations of a transaction into the database.
int mdb_env_sync(MDB_env *env, int force)
Flush the data buffers to disk.
int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
Get items from a database.
int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags)
Retrieve the DB flags for a database handle.
int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor)
Create a cursor handle.
int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
Set the maximum number of named databases for the environment.
int mdb_env_create(MDB_env **env)
Create an LMDB environment handle.
int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
Empty or delete+close a database.
int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
Open a database in the environment.
int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
Set a custom data comparison function for a MDB_DUPSORT database.
void mdb_cursor_close(MDB_cursor *cursor)
Close a cursor handle.
int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Create a transaction for use with the environment.
int mdb_env_set_maxreaders(MDB_env *env, unsigned int readers)
Set the maximum number of threads/reader slots for the environment.
int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat)
Retrieve statistics for a database.
MDB_env * mdb_txn_env(MDB_txn *txn)
Returns the transaction's MDB_env.
int mdb_env_stat(MDB_env *env, MDB_stat *stat)
Return statistics about the LMDB environment.
mdb_size_t ms_branch_pages
mdb_size_t ms_overflow_pages
struct MDB_val MDB_val
Generic structure used for passing keys and data in and out of the database.
struct MDB_env MDB_env
Opaque structure for a database environment.
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
unsigned int MDB_dbi
A handle for an individual database in the DB environment.
struct MDB_cursor MDB_cursor
Opaque structure for navigating through a database.
#define MCLOG_RED(level, cat, x)
Holds cryptonote related classes and helpers.
struct cryptonote::mdb_block_info_2 mdb_block_info_2
struct cryptonote::outkey outkey
boost::multiprecision::uint128_t difficulty_type
struct cryptonote::blk_height blk_height
struct cryptonote::mdb_block_info_1 mdb_block_info_1
void lmdb_resized(MDB_env *env)
struct cryptonote::pre_rct_outkey pre_rct_outkey
struct cryptonote::mdb_threadinfo mdb_threadinfo
bool get_block_hash(const block &b, crypto::hash &res)
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
int lmdb_txn_renew(MDB_txn *txn)
mdb_block_info_4 mdb_block_info
bool is_coinbase(const transaction &tx)
struct cryptonote::acc_outs_t acc_outs_t
blobdata block_to_blob(const block &b)
struct cryptonote::txindex txindex
blobdata tx_to_blob(const transaction &tx)
std::pair< crypto::hash, uint64_t > tx_out_index
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
struct cryptonote::outtx outtx
int lmdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
struct cryptonote::mdb_block_info_4 mdb_block_info_4
struct cryptonote::mdb_txn_cursors mdb_txn_cursors
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
bool t_serializable_object_to_blob(const t_object &to, blobdata &b_blob)
struct cryptonote::mdb_block_info_3 mdb_block_info_3
@ Warning
Useful when application has potentially harmful situtaions.
@ Info
Mainly useful to represent current progress of application.
bool get_file_size(const std::string &path_to_file, uint64_t &size)
type_vec_type median(std::vector< type_vec_type > &v)
std::string to_string(t_connection_type type)
mdb_size_t count(MDB_cursor *cur)
version
Supported socks variants.
key zeroCommit(etn_amount amount)
bool serialize(Archive &ar, T &v)
const T & move(const T &t)
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
unsigned __int64 uint64_t
Information about the environment.
Statistics for a database in the environment.
Generic structure used for passing keys and data in and out of the database.
uint64_t relative_out_index
uint64_t relative_out_index
uint64_t bi_long_term_block_weight
uint64_t bi_long_term_block_weight
mdb_txn_cursors m_ti_rcursors
static void prevent_new_txns()
static void allow_new_txns()
uint64_t num_active_tx() const
static void wait_no_active_txns()
static std::atomic< uint64_t > num_active_txns
void commit(std::string message="")
static std::atomic_flag creation_gate
a struct containing output metadata
uint64_t height
the height of the block which created the output
rct::key commitment
the output's amount commitment (for spend verification)
pre_rct_output_data_t data
#define CRITICAL_REGION_LOCAL(x)
struct hash_func hashes[]