29 #include <boost/range/adaptor/transformed.hpp> 30 #include <boost/algorithm/string.hpp> 46 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 47 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "bcutil" 49 namespace po = boost::program_options;
53 static const char zerokey[8] = {0};
54 static const MDB_val zerokval = {
sizeof(zerokey), (
void *)zerokey };
56 static uint64_t records_per_sync = 200;
58 static MDB_dbi dbi_relative_rings;
60 static MDB_dbi dbi_processed_txidx;
63 static MDB_dbi dbi_ring_instances;
85 static bool parse_db_sync_mode(
std::string db_sync_mode)
87 std::vector<std::string> options;
89 boost::split(options, db_sync_mode, boost::is_any_of(
" :"));
91 for(
const auto &
option : options)
97 if(options.size() == 0)
100 db_flags = DEFAULT_FLAGS;
103 bool safemode =
false;
104 if(options.size() >= 1)
106 if(options[0] ==
"safe")
111 else if(options[0] ==
"fast")
115 else if(options[0] ==
"fastest")
118 records_per_sync = 1000;
121 db_flags = DEFAULT_FLAGS;
124 if(options.size() >= 2 && !safemode)
127 uint64_t bps = strtoull(options[1].c_str(), &endptr, 0);
129 records_per_sync = bps;
139 dir = dir.remove_filename();
140 dir /=
".shared-ringdb";
144 static std::string get_cache_filename(boost::filesystem::path filename)
146 if (!boost::filesystem::is_directory(filename))
147 filename.remove_filename();
148 return filename.string();
155 for (
int n = 7; n >= 0; n--)
159 return va[n] < vb[n] ? -1 : 1;
169 return (va < vb) ? -1 : va > vb;
180 return va < vb ? -1 : va > vb;
182 return va < vb ? -1 : va > vb;
185 static int resize_env(
const char *db_path)
191 size_t needed = 1000ul * 1024 * 1024;
205 boost::filesystem::path path(db_path);
206 boost::filesystem::space_info si = boost::filesystem::space(path);
207 if(si.available < needed)
209 MERROR(
"!! WARNING: Insufficient free space to extend database !!: " << (si.available >> 20L) <<
" MB available");
216 MWARNING(
"Unable to query free disk space.");
227 bool tx_active =
false;
230 MINFO(
"Creating spent output cache in " << cache_filename);
244 const std::string actual_filename = get_cache_filename(cache_filename);
245 dbr =
mdb_env_open(env, actual_filename.c_str(), flags, 0664);
302 const size_t sz = s.size();
303 s.resize(s.size() + 12 * ring.size());
304 char *ptr = (
char*)s.data() + sz;
307 if (ptr > s.data() + sz + 12 * ring.size())
308 throw std::runtime_error(
"varint output overflow");
309 s.resize(ptr - s.data());
315 char s[12], *ptr = s;
317 if (ptr > s +
sizeof(s))
318 throw std::runtime_error(
"varint output overflow");
322 static std::vector<uint64_t> decompress_ring(
const std::string &s)
324 std::vector<uint64_t> ring;
326 for (std::string::const_iterator i = s.begin(); i != s.cend(); std::advance(i, read))
330 read = tools::read_varint(tmp.begin(), tmp.end(),
out);
344 bool tx_active =
false;
353 dbr =
mdb_env_open(env, actual_filename.c_str(), 0, 0664);
354 if (dbr)
throw std::runtime_error(
"Failed to open rings database file '" 388 throw std::runtime_error(
"Bad key size");
396 std::stringstream ss;
425 bool tx_active[2] = {
false,
false };
431 for (
int i = 0; i < 2; ++i)
437 const std::string actual_filename = i ? second_filename : first_filename;
438 dbr =
mdb_env_open(env[i], actual_filename.c_str(), 0, 0664);
439 if (dbr)
throw std::runtime_error(
"Failed to open rings database file '" 454 dbr =
mdb_stat(txn[i], dbi[i], &stat);
459 if (n_txes[0] == 0 || n_txes[1] == 0)
460 throw std::runtime_error(
"No transaction in the database");
461 uint64_t lo = 0, hi = std::min(n_txes[0], n_txes[1]) - 1;
472 if (v[0].mv_size == v[1].mv_size && !memcmp(v[0].mv_data, v[1].mv_data, v[0].mv_size))
478 for (
int i = 0; i < 2; ++i)
483 tx_active[i] =
false;
490 static std::vector<uint64_t> canonicalize(
const std::vector<uint64_t> &v)
492 std::vector<uint64_t> c;
495 for (
size_t n = 1; n < v.size(); ++n)
500 if (c.size() < v.size())
502 MINFO(
"Ring has duplicate member(s): " <<
508 static uint64_t get_num_spent_outputs()
511 bool tx_active =
false;
562 bool spent = dbr == 0;
566 static std::vector<output_data> get_spent_outputs(
MDB_txn *txn)
580 std::vector<output_data> outs;
646 bool tx_active =
false;
657 dbr =
mdb_get(txn, dbi_processed_txidx, &k, &v);
678 int dbr =
mdb_put(txn, dbi_processed_txidx, &k, &v, 0);
687 int dbr =
mdb_get(txn, dbi_relative_rings, &k, &v);
701 v.
mv_data = (
void*)sring.c_str();
703 int dbr =
mdb_put(txn, dbi_relative_rings, &k, &v, 0);
718 const std::string sring = keep_under_511(compress_ring(amount, ring));
720 k.
mv_data = (
void*)sring.data();
722 int dbr =
mdb_get(txn, dbi_ring_instances, &k, &v);
731 uint64_t instances = get_ring_instances(txn, amount, ring);
732 if (ring.size() > 11)
736 std::vector<uint64_t> subset;
737 subset.reserve(ring.size());
741 for (
size_t i = 0; i < ring.size(); ++i)
743 subset.push_back(ring[i]);
744 extra += get_ring_instances(txn, amount, subset);
746 return instances + extra;
751 const std::string sring = keep_under_511(compress_ring(amount, ring));
753 k.
mv_data = (
void*)sring.data();
756 int dbr =
mdb_get(txn, dbi_ring_instances, &k, &v);
767 dbr =
mdb_put(txn, dbi_ring_instances, &k, &v, 0);
773 static std::vector<crypto::key_image> get_key_images(
MDB_txn *txn,
const output_data &od)
778 int dbr =
mdb_get(txn, dbi_outputs, &k, &v);
783 std::vector<crypto::key_image> key_images;
784 key_images.reserve(v.
mv_size / 32);
787 key_images.push_back(*ki++);
796 int dbr =
mdb_get(txn, dbi_outputs, &k, &v);
806 v.
mv_data = (
void*)data.data();
808 dbr =
mdb_put(txn, dbi_outputs, &k, &v, 0);
817 int dbr =
mdb_get(txn, dbi_stats, &k, &v);
833 int dbr =
mdb_put(txn, dbi_stats, &k, &v, 0);
837 static void inc_stat(
MDB_txn *txn,
const char *
key)
840 if (!get_stat(txn,
key, data))
843 set_stat(txn,
key, data);
861 MINFO(
"Opening electroneum blockchain at " << actual_filename);
862 dbr =
mdb_env_open(*env, actual_filename.c_str(), flags, 0664);
888 MDB_val k = {
sizeof(amount), (
void*)&amount }, v;
915 bool tx_active =
false;
922 dbr =
mdb_env_open(env, actual_filename.c_str(), 0, 0664);
923 if (dbr)
throw std::runtime_error(
"Failed to open rings database file '" 936 dbr =
mdb_get(txn, dbi, &k, &v);
943 return genesis_block_hash;
946 static std::vector<std::pair<uint64_t, uint64_t>> load_outputs(
const std::string &filename)
948 std::vector<std::pair<uint64_t, uint64_t>> outputs;
949 uint64_t amount = std::numeric_limits<uint64_t>::max();
952 f = fopen(filename.c_str(),
"r");
955 MERROR(
"Failed to load outputs from " << filename <<
": " << strerror(errno));
961 if (!fgets(s,
sizeof(s), f))
963 MERROR(
"Error reading from " << filename <<
": " << strerror(errno));
968 const size_t len = strlen(s);
969 if (len > 0 && s[len - 1] ==
'\n')
973 std::pair<uint64_t, uint64_t> output;
975 if (sscanf(s,
"@%" PRIu64, &amount) == 1)
979 if (amount == std::numeric_limits<uint64_t>::max())
981 MERROR(
"Bad format in " << filename);
984 if (sscanf(s,
"%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits<uint64_t>::max() - offset)
986 while (num_offsets-- > 0)
987 outputs.push_back(std::make_pair(amount, offset++));
989 else if (sscanf(s,
"%" PRIu64, &offset) == 1)
991 outputs.push_back(std::make_pair(amount, offset));
995 MERROR(
"Bad format in " << filename);
1005 FILE *f = fopen(filename.c_str(),
"w");
1008 MERROR(
"Failed to open " << filename <<
": " << strerror(errno));
1012 uint64_t pending_amount = std::numeric_limits<uint64_t>::max();
1013 std::vector<uint64_t> pending_offsets;
1030 if (!pending_offsets.empty() && (amount != pending_amount || pending_offsets.back()+1 != offset))
1032 if (pending_offsets.size() == 1)
1033 fprintf(f,
"%" PRIu64 "\n", pending_offsets.front());
1035 fprintf(f,
"%" PRIu64 "*%zu\n", pending_offsets.front(), pending_offsets.size());
1036 pending_offsets.clear();
1038 if (pending_amount != amount)
1040 fprintf(f,
"@%" PRIu64 "\n", amount);
1041 pending_amount = amount;
1043 pending_offsets.push_back(offset);
1045 if (!pending_offsets.empty())
1047 if (pending_offsets.size() == 1)
1048 fprintf(f,
"%" PRIu64 "\n", pending_offsets.front());
1050 fprintf(f,
"%" PRIu64 "*%zu\n", pending_offsets.front(), pending_offsets.size());
1051 pending_offsets.clear();
1066 available_dbs =
"available: " + available_dbs;
1072 boost::filesystem::path output_file_path;
1074 po::options_description desc_cmd_only(
"Command line options");
1075 po::options_description desc_cmd_sett(
"Command line options and settings options");
1077 "spent-output-db-dir",
"Specify spent output database directory",
1078 get_default_db_path(),
1082 "database", available_dbs.c_str(), default_db_type
1090 ,
"Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]." 1095 const command_line::arg_descriptor<bool> arg_force_chain_reaction_pass = {
"force-chain-reaction-pass",
"Run the chain reaction pass even if no new blockchain data was processed"};
1110 po::options_description desc_options(
"Allowed options");
1111 desc_options.add(desc_cmd_only).add(desc_cmd_sett);
1113 po::positional_options_description positional_options;
1114 positional_options.add(arg_inputs.name, -1);
1116 po::variables_map vm;
1119 auto parser = po::command_line_parser(argc, argv).options(desc_options).positional(positional_options);
1120 po::store(parser.run(), vm);
1130 std::cout << desc_options << std::endl;
1149 std::vector<std::pair<uint64_t, uint64_t>> extra_spent_outputs = extra_spent_list.empty() ? std::vector<std::pair<uint64_t, uint64_t>>() : load_outputs(extra_spent_list);
1154 std::cerr <<
"Invalid database type: " << db_type << std::endl;
1159 if (!parse_db_sync_mode(db_sync_mode))
1161 MERROR(
"Invalid db sync mode: " << db_sync_mode);
1172 const std::string cache_dir = (output_file_path /
"spent-outputs-cache").
string();
1179 const uint64_t start_blackballed_outputs = get_num_spent_outputs();
1183 bool stop_requested =
false;
1185 stop_requested =
true;
1188 int dbr = resize_env(cache_dir.c_str());
1196 open_db(inputs[0], &env0, &txn0, &cur0, &dbi0);
1198 if (!extra_spent_outputs.empty())
1200 MINFO(
"Adding " << extra_spent_outputs.size() <<
" extra spent outputs");
1208 std::vector<std::pair<uint64_t, uint64_t>> blackballs;
1209 for (
const std::pair<uint64_t, uint64_t> &output: extra_spent_outputs)
1211 if (!is_output_spent(cur,
output_data(output.first, output.second)))
1213 blackballs.push_back(output);
1214 if (add_spent_output(cur,
output_data(output.first, output.second)))
1215 inc_stat(txn, output.first ?
"pre-rct-extra" :
"rct-ring-extra");
1218 if (!blackballs.empty())
1220 ringdb.blackball(blackballs);
1228 for (
size_t n = 0; n < inputs.size(); ++n)
1230 const std::string canonical = boost::filesystem::canonical(inputs[n]).string();
1231 uint64_t start_idx = get_processed_txidx(canonical);
1232 if (n > 0 && start_idx == 0)
1234 start_idx = find_first_diverging_transaction(inputs[0], inputs[n]);
1235 LOG_PRINT_L0(
"First diverging transaction at " << start_idx);
1237 LOG_PRINT_L0(
"Reading blockchain from " << inputs[n] <<
" from " << start_idx);
1246 std::vector<std::pair<uint64_t, uint64_t>> blackballs;
1250 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1251 const bool miner_tx = tx.
vin.size() == 1 && tx.
vin[0].type() ==
typeid(
txin_gen);
1252 for (
const auto &in: tx.
vin)
1256 const auto &txin = boost::get<txin_to_key>(in);
1257 if (opt_rct_only && txin.amount != 0)
1263 add_key_image(txn,
output_data(txin.amount, out), txin.k_image);
1265 std::vector<uint64_t> relative_ring;
1266 std::vector<uint64_t> new_ring = canonicalize(txin.key_offsets);
1267 const uint32_t ring_size = txin.key_offsets.size();
1268 const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring);
1269 uint64_t pa_total = 0, pa_spent = 0;
1271 get_per_amount_outputs(txn, txin.amount, pa_total, pa_spent);
1272 if (n == 0 && ring_size == 1)
1274 const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]);
1277 MINFO(
"Marking output " << output.first <<
"/" << output.second <<
" as spent, due to being used in a 1-ring");
1278 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1280 blackballs.push_back(output);
1281 if (add_spent_output(cur,
output_data(txin.amount, absolute[0])))
1282 inc_stat(txn, txin.amount ?
"pre-rct-ring-size-1" :
"rct-ring-size-1");
1284 else if (n == 0 && instances == new_ring.size())
1286 for (
size_t o = 0; o < new_ring.size(); ++o)
1288 const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
1291 MINFO(
"Marking output " << output.first <<
"/" << output.second <<
" as spent, due to being used in " << new_ring.size() <<
" identical " << new_ring.size() <<
"-rings");
1292 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1294 blackballs.push_back(output);
1295 if (add_spent_output(cur,
output_data(txin.amount, absolute[o])))
1296 inc_stat(txn, txin.amount ?
"pre-rct-duplicate-rings" :
"rct-duplicate-rings");
1299 else if (n == 0 && !opt_rct_only && pa_spent + 1 == pa_total)
1301 for (
size_t o = 0; o < pa_total; ++o)
1303 const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, o);
1306 MINFO(
"Marking output " << output.first <<
"/" << output.second <<
" as spent, due to as many outputs of that amount being spent as exist so far");
1307 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1309 blackballs.push_back(output);
1310 if (add_spent_output(cur,
output_data(txin.amount, o)))
1311 inc_stat(txn, txin.amount ?
"pre-rct-full-count" :
"rct-full-count");
1314 else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size())
1316 for (
size_t o = 0; o < new_ring.size(); ++o)
1318 const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
1321 MINFO(
"Marking output " << output.first <<
"/" << output.second <<
" as spent, due to being used in " << new_ring.size() <<
" subsets of " << new_ring.size() <<
"-rings");
1322 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1324 blackballs.push_back(output);
1325 if (add_spent_output(cur,
output_data(txin.amount, absolute[o])))
1326 inc_stat(txn, txin.amount ?
"pre-rct-subset-rings" :
"rct-subset-rings");
1329 else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring))
1331 MDEBUG(
"Key image " << txin.k_image <<
" already seen: rings " <<
1332 boost::join(relative_ring | boost::adaptors::transformed([](
uint64_t out){return std::to_string(out);}),
" ") <<
1333 ", " << boost::join(txin.key_offsets | boost::adaptors::transformed([](
uint64_t out){return std::to_string(out);}),
" "));
1334 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1335 if (relative_ring != txin.key_offsets)
1337 MDEBUG(
"Rings are different");
1338 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1341 std::vector<uint64_t> common;
1344 if (std::find(r1.begin(), r1.end(), out) != r1.end())
1345 common.push_back(out);
1349 MERROR(
"Rings for the same key image are disjoint");
1350 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1352 else if (common.size() == 1)
1354 const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, common[0]);
1357 MINFO(
"Marking output " << output.first <<
"/" << output.second <<
" as spent, due to being used in rings with a single common element");
1358 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1360 blackballs.push_back(output);
1361 if (add_spent_output(cur,
output_data(txin.amount, common[0])))
1362 inc_stat(txn, txin.amount ?
"pre-rct-key-image-attack" :
"rct-key-image-attack");
1366 MDEBUG(
"The intersection has more than one element, it's still ok");
1367 std::cout <<
"\r" << start_idx <<
"/" << n_txes <<
" \r" << std::flush;
1368 for (
const auto &out: r0)
1369 if (std::find(common.begin(), common.end(), out) != common.end())
1370 new_ring.push_back(out);
1377 set_relative_ring(txn, txin.k_image, new_ring);
1379 inc_per_amount_outputs(txn, txin.amount, 0, 1);
1382 set_processed_txidx(txn, canonical, start_idx+1);
1385 for (
const auto &out: tx.
vout)
1389 if (opt_rct_only && amount != 0)
1393 inc_per_amount_outputs(txn, amount, 1, 0);
1398 if (records >= records_per_sync)
1400 if (!blackballs.empty())
1402 ringdb.blackball(blackballs);
1408 int dbr = resize_env(cache_dir.c_str());
1419 MINFO(
"Stopping scan...");
1427 LOG_PRINT_L0(
"blockchain from " << inputs[n] <<
" processed till tx idx " << start_idx);
1432 std::vector<output_data> work_spent;
1435 goto skip_secondary_passes;
1437 if (opt_force_chain_reaction_pass || get_num_spent_outputs() > start_blackballed_outputs)
1442 work_spent = get_spent_outputs(txn);
1446 while (!work_spent.empty())
1448 LOG_PRINT_L0(
"Secondary pass on " << work_spent.size() <<
" spent outputs");
1450 int dbr = resize_env(cache_dir.c_str());
1460 std::vector<std::pair<uint64_t, uint64_t>> blackballs;
1461 std::vector<output_data> scan_spent =
std::move(work_spent);
1465 std::vector<crypto::key_image> key_images = get_key_images(txn, od);
1468 std::vector<uint64_t> relative_ring;
1476 if (is_output_spent(cur, new_od))
1481 if (known == absolute.size() - 1)
1483 const std::pair<uint64_t, uint64_t> output = std::make_pair(od.
amount, last_unknown);
1486 MINFO(
"Marking output " << output.first <<
"/" << output.second <<
" as spent, due to being used in a " <<
1487 absolute.size() <<
"-ring where all other outputs are known to be spent");
1489 blackballs.push_back(output);
1491 inc_stat(txn, od.
amount ?
"pre-rct-chain-reaction" :
"rct-chain-reaction");
1498 MINFO(
"Stopping secondary passes. Secondary passes are not incremental, they will re-run fully.");
1502 if (!blackballs.empty())
1504 ringdb.blackball(blackballs);
1512 skip_secondary_passes:
1513 uint64_t diff = get_num_spent_outputs() - start_blackballed_outputs;
1514 LOG_PRINT_L0(
std::to_string(diff) <<
" new outputs marked as spent, " << get_num_spent_outputs() <<
" total outputs marked as spent");
1520 get_num_outputs(txn0, cur0, dbi0, pre_rct,
rct);
1521 MINFO(
"Total pre-rct outputs: " << pre_rct);
1522 MINFO(
"Total rct outputs: " <<
rct);
1523 static const struct {
const char *
key;
uint64_t base; } stat_keys[] = {
1524 {
"pre-rct-ring-size-1", pre_rct }, {
"rct-ring-size-1",
rct },
1525 {
"pre-rct-duplicate-rings", pre_rct }, {
"rct-duplicate-rings",
rct },
1526 {
"pre-rct-subset-rings", pre_rct }, {
"rct-subset-rings",
rct },
1527 {
"pre-rct-full-count", pre_rct }, {
"rct-full-count",
rct },
1528 {
"pre-rct-key-image-attack", pre_rct }, {
"rct-key-image-attack",
rct },
1529 {
"pre-rct-extra", pre_rct }, {
"rct-ring-extra",
rct },
1530 {
"pre-rct-chain-reaction", pre_rct }, {
"rct-chain-reaction",
rct },
1532 for (
const auto &
key: stat_keys)
1535 if (!get_stat(txn,
key.key, data))
1537 float percent =
key.base ? 100.0f * data /
key.base : 0.0f;
1538 MINFO(
key.key <<
": " << data <<
" (" << percent <<
"%)");
1542 if (!opt_export.empty())
1550 export_spent_outputs(cur, opt_export);
1555 LOG_PRINT_L0(
"Blockchain spent output data exported OK");
1556 close_db(env0, txn0, cur0, dbi0);
const char *const ELECTRONEUM_RELEASE_NAME
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
int mdb_env_set_mapsize(MDB_env *env, mdb_size_t size)
Set the size of the memory map to use for this environment.
void mdb_cursor_close(MDB_cursor *cursor)
Close a cursor handle.
int mdb_cursor_count(MDB_cursor *cursor, mdb_size_t *countp)
Return count of duplicates for current key.
int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
Open a database in the environment.
int mdb_txn_commit(MDB_txn *txn)
Commit all the operations of a transaction into the database.
int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, unsigned int flags)
Store by cursor.
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat)
Retrieve statistics for a database.
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
Close a database handle. Normally unnecessary. Use with care:
void mlog_set_log(const char *log)
#define CATCH_ENTRY(location, return_val)
std::string mlog_get_default_log_path(const char *default_filename)
void mlog_configure(const std::string &filename_base, bool console, const std::size_t max_log_file_size=MAX_LOG_FILE_SIZE, const std::size_t max_log_files=MAX_LOG_FILES)
provides the implementation of varint's
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.
int mdb_env_info(MDB_env *env, MDB_envinfo *stat)
Return information about the LMDB environment.
const command_line::arg_descriptor< std::string > arg_db_sync_mode
int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
Set the maximum number of named databases for the environment.
const arg_descriptor< bool > arg_help
std::string blockchain_db_types(const std::string &sep)
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
output_data(uint64_t a, uint64_t i)
struct MDB_env MDB_env
Opaque structure for a database environment.
int main(int argc, char *argv[])
for(i=1;i< 1;++i) fe_sq(t0
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
std::vector< tx_out > vout
Holds cryptonote related classes and helpers.
const char *const ELECTRONEUM_VERSION_FULL
bool blockchain_valid_db_type(const std::string &db_type)
std::vector< txin_v > vin
int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
Open an environment handle.
mdb_size_t count(MDB_cursor *cur)
Statistics for a database in the environment.
Information about the environment.
bool handle_error_helper(const boost::program_options::options_description &desc, F parser)
const command_line::arg_descriptor< std::string > arg_log_level
std::vector< uint64_t > absolute_output_offsets_to_relative(const std::vector< uint64_t > &off)
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
unsigned __int64 uint64_t
int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
Set a custom key comparison function for a database.
unsigned int MDB_dbi
A handle for an individual database in the DB environment.
int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Create a transaction for use with the environment.
MDB_cursor_op
Cursor Get operations.
void do_serialize(boost::mpl::false_, Archive &a, epee::net_utils::network_address &na)
void cn_fast_hash(const void *data, size_t length, char *hash)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, MDB_cursor_op op)
Retrieve by cursor.
int mdb_env_create(MDB_env **env)
Create an LMDB environment handle.
void mdb_txn_abort(MDB_txn *txn)
Abandon all the operations of the transaction instead of saving them.
bool operator==(const output_data &other) const
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
struct MDB_cursor MDB_cursor
Opaque structure for navigating through a database.
Generic structure used for passing keys and data in and out of the database.
const T & move(const T &t)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
int mdb_env_stat(MDB_env *env, MDB_stat *stat)
Return statistics about the LMDB environment.
char * mdb_strerror(int err)
Return a string describing a given error code.
std::string to_string(t_connection_type type)
int compare_uint64(const MDB_val *a, const MDB_val *b)
int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
Get items from a database.
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_env_close(MDB_env *env)
Close the environment and release the memory map.
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor)
Create a cursor handle.