Electroneum
blockchain_blackball.cpp File Reference
Include dependency graph for blockchain_blackball.cpp:

Go to the source code of this file.

Classes

struct  output_data
 

Macros

#define ELECTRONEUM_DEFAULT_LOG_CATEGORY   "bcutil"
 

Functions

int compare_uint64 (const MDB_val *a, const MDB_val *b)
 
int main (int argc, char *argv[])
 

Macro Definition Documentation

◆ ELECTRONEUM_DEFAULT_LOG_CATEGORY

#define ELECTRONEUM_DEFAULT_LOG_CATEGORY   "bcutil"

Definition at line 47 of file blockchain_blackball.cpp.

Function Documentation

◆ compare_uint64()

int compare_uint64 ( const MDB_val a,
const MDB_val b 
)

Definition at line 165 of file blockchain_blackball.cpp.

166 {
167  const uint64_t va = *(const uint64_t *)a->mv_data;
168  const uint64_t vb = *(const uint64_t *)b->mv_data;
169  return (va < vb) ? -1 : va > vb;
170 }
void * mv_data
Definition: lmdb.h:288
unsigned __int64 uint64_t
Definition: stdint.h:136
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 1057 of file blockchain_blackball.cpp.

1058 {
1059  TRY_ENTRY();
1060 
1062 
1063  std::string default_db_type = "lmdb";
1064 
1065  std::string available_dbs = cryptonote::blockchain_db_types(", ");
1066  available_dbs = "available: " + available_dbs;
1067 
1068  uint32_t log_level = 0;
1069 
1071 
1072  boost::filesystem::path output_file_path;
1073 
1074  po::options_description desc_cmd_only("Command line options");
1075  po::options_description desc_cmd_sett("Command line options and settings options");
1076  const command_line::arg_descriptor<std::string> arg_blackball_db_dir = {
1077  "spent-output-db-dir", "Specify spent output database directory",
1078  get_default_db_path(),
1079  };
1080  const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
1081  const command_line::arg_descriptor<std::string> arg_database = {
1082  "database", available_dbs.c_str(), default_db_type
1083  };
1084  const command_line::arg_descriptor<bool> arg_rct_only = {"rct-only", "Only work on ringCT outputs", false};
1085  const command_line::arg_descriptor<bool> arg_check_subsets = {"check-subsets", "Check ring subsets (very expensive)", false};
1086  const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output)", false};
1087  const command_line::arg_descriptor<std::vector<std::string> > arg_inputs = {"inputs", "Path to Electroneum DB, and path to any fork DBs"};
1089  "db-sync-mode"
1090  , "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]."
1091  , "fast:1000"
1092  };
1093  const command_line::arg_descriptor<std::string> arg_extra_spent_list = {"extra-spent-list", "Optional list of known spent outputs",""};
1094  const command_line::arg_descriptor<std::string> arg_export = {"export", "Filename to export the backball list to"};
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"};
1096 
1097  command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir);
1098  command_line::add_arg(desc_cmd_sett, arg_log_level);
1099  command_line::add_arg(desc_cmd_sett, arg_database);
1100  command_line::add_arg(desc_cmd_sett, arg_rct_only);
1101  command_line::add_arg(desc_cmd_sett, arg_check_subsets);
1102  command_line::add_arg(desc_cmd_sett, arg_verbose);
1103  command_line::add_arg(desc_cmd_sett, arg_db_sync_mode);
1104  command_line::add_arg(desc_cmd_sett, arg_extra_spent_list);
1105  command_line::add_arg(desc_cmd_sett, arg_export);
1106  command_line::add_arg(desc_cmd_sett, arg_force_chain_reaction_pass);
1107  command_line::add_arg(desc_cmd_sett, arg_inputs);
1109 
1110  po::options_description desc_options("Allowed options");
1111  desc_options.add(desc_cmd_only).add(desc_cmd_sett);
1112 
1113  po::positional_options_description positional_options;
1114  positional_options.add(arg_inputs.name, -1);
1115 
1116  po::variables_map vm;
1117  bool r = command_line::handle_error_helper(desc_options, [&]()
1118  {
1119  auto parser = po::command_line_parser(argc, argv).options(desc_options).positional(positional_options);
1120  po::store(parser.run(), vm);
1121  po::notify(vm);
1122  return true;
1123  });
1124  if (! r)
1125  return 1;
1126 
1128  {
1129  std::cout << "Electroneum '" << ELECTRONEUM_RELEASE_NAME << "' (v" << ELECTRONEUM_VERSION_FULL << ")" << ENDL << ENDL;
1130  std::cout << desc_options << std::endl;
1131  return 1;
1132  }
1133 
1134  mlog_configure(mlog_get_default_log_path("electroneum-blockchain-mark-spent-outputs.log"), true);
1137  else
1138  mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
1139 
1140  LOG_PRINT_L0("Starting...");
1141 
1142  output_file_path = command_line::get_arg(vm, arg_blackball_db_dir);
1143  bool opt_rct_only = command_line::get_arg(vm, arg_rct_only);
1144  bool opt_check_subsets = command_line::get_arg(vm, arg_check_subsets);
1145  bool opt_verbose = command_line::get_arg(vm, arg_verbose);
1146  bool opt_force_chain_reaction_pass = command_line::get_arg(vm, arg_force_chain_reaction_pass);
1147  std::string opt_export = command_line::get_arg(vm, arg_export);
1148  std::string extra_spent_list = command_line::get_arg(vm, arg_extra_spent_list);
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);
1150 
1151  std::string db_type = command_line::get_arg(vm, arg_database);
1153  {
1154  std::cerr << "Invalid database type: " << db_type << std::endl;
1155  return 1;
1156  }
1157 
1159  if (!parse_db_sync_mode(db_sync_mode))
1160  {
1161  MERROR("Invalid db sync mode: " << db_sync_mode);
1162  return 1;
1163  }
1164 
1165  const std::vector<std::string> inputs = command_line::get_arg(vm, arg_inputs);
1166  if (inputs.empty())
1167  {
1168  LOG_PRINT_L0("No inputs given");
1169  return 1;
1170  }
1171 
1172  const std::string cache_dir = (output_file_path / "spent-outputs-cache").string();
1173  init(cache_dir);
1174 
1175  LOG_PRINT_L0("Scanning for spent outputs...");
1176 
1177  size_t done = 0;
1178 
1179  const uint64_t start_blackballed_outputs = get_num_spent_outputs();
1180 
1181  tools::ringdb ringdb(output_file_path.string(), epee::string_tools::pod_to_hex(get_genesis_block_hash(inputs[0])));
1182 
1183  bool stop_requested = false;
1184  tools::signal_handler::install([&stop_requested](int type) {
1185  stop_requested = true;
1186  });
1187 
1188  int dbr = resize_env(cache_dir.c_str());
1189  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr)));
1190 
1191  // open first db
1192  MDB_env *env0;
1193  MDB_txn *txn0;
1194  MDB_dbi dbi0;
1195  MDB_cursor *cur0;
1196  open_db(inputs[0], &env0, &txn0, &cur0, &dbi0);
1197 
1198  if (!extra_spent_outputs.empty())
1199  {
1200  MINFO("Adding " << extra_spent_outputs.size() << " extra spent outputs");
1201  MDB_txn *txn;
1202  int dbr = mdb_txn_begin(env, NULL, 0, &txn);
1203  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1204  MDB_cursor *cur;
1205  dbr = mdb_cursor_open(txn, dbi_spent, &cur);
1206  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr)));
1207 
1208  std::vector<std::pair<uint64_t, uint64_t>> blackballs;
1209  for (const std::pair<uint64_t, uint64_t> &output: extra_spent_outputs)
1210  {
1211  if (!is_output_spent(cur, output_data(output.first, output.second)))
1212  {
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");
1216  }
1217  }
1218  if (!blackballs.empty())
1219  {
1220  ringdb.blackball(blackballs);
1221  blackballs.clear();
1222  }
1223  mdb_cursor_close(cur);
1224  dbr = mdb_txn_commit(txn);
1225  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
1226  }
1227 
1228  for (size_t n = 0; n < inputs.size(); ++n)
1229  {
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)
1233  {
1234  start_idx = find_first_diverging_transaction(inputs[0], inputs[n]);
1235  LOG_PRINT_L0("First diverging transaction at " << start_idx);
1236  }
1237  LOG_PRINT_L0("Reading blockchain from " << inputs[n] << " from " << start_idx);
1238  MDB_txn *txn;
1239  int dbr = mdb_txn_begin(env, NULL, 0, &txn);
1240  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1241  MDB_cursor *cur;
1242  dbr = mdb_cursor_open(txn, dbi_spent, &cur);
1243  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr)));
1244  size_t records = 0;
1245  const std::string filename = inputs[n];
1246  std::vector<std::pair<uint64_t, uint64_t>> blackballs;
1247  uint64_t n_txes;
1248  for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool
1249  {
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)
1253  {
1254  if (in.type() != typeid(txin_to_key))
1255  continue;
1256  const auto &txin = boost::get<txin_to_key>(in);
1257  if (opt_rct_only && txin.amount != 0)
1258  continue;
1259 
1260  const std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
1261  if (n == 0)
1262  for (uint64_t out: absolute)
1263  add_key_image(txn, output_data(txin.amount, out), txin.k_image);
1264 
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;
1270  if (!opt_rct_only)
1271  get_per_amount_outputs(txn, txin.amount, pa_total, pa_spent);
1272  if (n == 0 && ring_size == 1)
1273  {
1274  const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]);
1275  if (opt_verbose)
1276  {
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;
1279  }
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");
1283  }
1284  else if (n == 0 && instances == new_ring.size())
1285  {
1286  for (size_t o = 0; o < new_ring.size(); ++o)
1287  {
1288  const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
1289  if (opt_verbose)
1290  {
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;
1293  }
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");
1297  }
1298  }
1299  else if (n == 0 && !opt_rct_only && pa_spent + 1 == pa_total)
1300  {
1301  for (size_t o = 0; o < pa_total; ++o)
1302  {
1303  const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, o);
1304  if (opt_verbose)
1305  {
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;
1308  }
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");
1312  }
1313  }
1314  else if (n == 0 && opt_check_subsets && get_ring_subset_instances(txn, txin.amount, new_ring) >= new_ring.size())
1315  {
1316  for (size_t o = 0; o < new_ring.size(); ++o)
1317  {
1318  const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]);
1319  if (opt_verbose)
1320  {
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;
1323  }
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");
1327  }
1328  }
1329  else if (n > 0 && get_relative_ring(txn, txin.k_image, relative_ring))
1330  {
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)
1336  {
1337  MDEBUG("Rings are different");
1338  std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
1339  const std::vector<uint64_t> r0 = cryptonote::relative_output_offsets_to_absolute(relative_ring);
1340  const std::vector<uint64_t> r1 = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
1341  std::vector<uint64_t> common;
1342  for (uint64_t out: r0)
1343  {
1344  if (std::find(r1.begin(), r1.end(), out) != r1.end())
1345  common.push_back(out);
1346  }
1347  if (common.empty())
1348  {
1349  MERROR("Rings for the same key image are disjoint");
1350  std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush;
1351  }
1352  else if (common.size() == 1)
1353  {
1354  const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, common[0]);
1355  if (opt_verbose)
1356  {
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;
1359  }
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");
1363  }
1364  else
1365  {
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);
1372  }
1373  }
1374  }
1375  if (n == 0)
1376  {
1377  set_relative_ring(txn, txin.k_image, new_ring);
1378  if (!opt_rct_only)
1379  inc_per_amount_outputs(txn, txin.amount, 0, 1);
1380  }
1381  }
1382  set_processed_txidx(txn, canonical, start_idx+1);
1383  if (!opt_rct_only)
1384  {
1385  for (const auto &out: tx.vout)
1386  {
1387  uint64_t amount = out.amount;
1388 
1389  if (opt_rct_only && amount != 0)
1390  continue;
1391  if (out.target.type() != typeid(txout_to_key))
1392  continue;
1393  inc_per_amount_outputs(txn, amount, 1, 0);
1394  }
1395  }
1396 
1397  ++records;
1398  if (records >= records_per_sync)
1399  {
1400  if (!blackballs.empty())
1401  {
1402  ringdb.blackball(blackballs);
1403  blackballs.clear();
1404  }
1405  mdb_cursor_close(cur);
1406  dbr = mdb_txn_commit(txn);
1407  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
1408  int dbr = resize_env(cache_dir.c_str());
1409  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr)));
1410  dbr = mdb_txn_begin(env, NULL, 0, &txn);
1411  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1412  dbr = mdb_cursor_open(txn, dbi_spent, &cur);
1413  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr)));
1414  records = 0;
1415  }
1416 
1417  if (stop_requested)
1418  {
1419  MINFO("Stopping scan...");
1420  return false;
1421  }
1422  return true;
1423  });
1424  mdb_cursor_close(cur);
1425  dbr = mdb_txn_commit(txn);
1426  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
1427  LOG_PRINT_L0("blockchain from " << inputs[n] << " processed till tx idx " << start_idx);
1428  if (stop_requested)
1429  break;
1430  }
1431 
1432  std::vector<output_data> work_spent;
1433 
1434  if (stop_requested)
1435  goto skip_secondary_passes;
1436 
1437  if (opt_force_chain_reaction_pass || get_num_spent_outputs() > start_blackballed_outputs)
1438  {
1439  MDB_txn *txn;
1440  dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
1441  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1442  work_spent = get_spent_outputs(txn);
1443  mdb_txn_abort(txn);
1444  }
1445 
1446  while (!work_spent.empty())
1447  {
1448  LOG_PRINT_L0("Secondary pass on " << work_spent.size() << " spent outputs");
1449 
1450  int dbr = resize_env(cache_dir.c_str());
1451  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to resize LMDB database: " + std::string(mdb_strerror(dbr)));
1452 
1453  MDB_txn *txn;
1454  dbr = mdb_txn_begin(env, NULL, 0, &txn);
1455  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1456  MDB_cursor *cur;
1457  dbr = mdb_cursor_open(txn, dbi_spent, &cur);
1458  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr)));
1459 
1460  std::vector<std::pair<uint64_t, uint64_t>> blackballs;
1461  std::vector<output_data> scan_spent = std::move(work_spent);
1462  work_spent.clear();
1463  for (const output_data &od: scan_spent)
1464  {
1465  std::vector<crypto::key_image> key_images = get_key_images(txn, od);
1466  for (const crypto::key_image &ki: key_images)
1467  {
1468  std::vector<uint64_t> relative_ring;
1469  CHECK_AND_ASSERT_THROW_MES(get_relative_ring(txn, ki, relative_ring), "Relative ring not found");
1470  std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(relative_ring);
1471  size_t known = 0;
1472  uint64_t last_unknown = 0;
1473  for (uint64_t out: absolute)
1474  {
1475  output_data new_od(od.amount, out);
1476  if (is_output_spent(cur, new_od))
1477  ++known;
1478  else
1479  last_unknown = out;
1480  }
1481  if (known == absolute.size() - 1)
1482  {
1483  const std::pair<uint64_t, uint64_t> output = std::make_pair(od.amount, last_unknown);
1484  if (opt_verbose)
1485  {
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");
1488  }
1489  blackballs.push_back(output);
1490  if (add_spent_output(cur, output_data(od.amount, last_unknown)))
1491  inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction");
1492  work_spent.push_back(output_data(od.amount, last_unknown));
1493  }
1494  }
1495 
1496  if (stop_requested)
1497  {
1498  MINFO("Stopping secondary passes. Secondary passes are not incremental, they will re-run fully.");
1499  return 0;
1500  }
1501  }
1502  if (!blackballs.empty())
1503  {
1504  ringdb.blackball(blackballs);
1505  blackballs.clear();
1506  }
1507  mdb_cursor_close(cur);
1508  dbr = mdb_txn_commit(txn);
1509  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
1510  }
1511 
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");
1515 
1516  MDB_txn *txn;
1517  dbr = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
1518  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1519  uint64_t pre_rct = 0, rct = 0;
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 },
1531  };
1532  for (const auto &key: stat_keys)
1533  {
1534  uint64_t data;
1535  if (!get_stat(txn, key.key, data))
1536  data = 0;
1537  float percent = key.base ? 100.0f * data / key.base : 0.0f;
1538  MINFO(key.key << ": " << data << " (" << percent << "%)");
1539  }
1540  mdb_txn_abort(txn);
1541 
1542  if (!opt_export.empty())
1543  {
1544  MDB_txn *txn;
1545  int dbr = mdb_txn_begin(env, NULL, 0, &txn);
1546  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
1547  MDB_cursor *cur;
1548  dbr = mdb_cursor_open(txn, dbi_spent, &cur);
1549  CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr)));
1550  export_spent_outputs(cur, opt_export);
1551  mdb_cursor_close(cur);
1552  mdb_txn_abort(txn);
1553  }
1554 
1555  LOG_PRINT_L0("Blockchain spent output data exported OK");
1556  close_db(env0, txn0, cur0, dbi0);
1557  close();
1558  return 0;
1559 
1560  CATCH_ENTRY("Error", 1);
1561 }
const char *const ELECTRONEUM_RELEASE_NAME
#define MERROR(x)
Definition: misc_log_ex.h:73
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
Definition: misc_log_ex.h:173
void mdb_cursor_close(MDB_cursor *cursor)
Close a cursor handle.
#define MDB_RDONLY
Definition: lmdb.h:320
#define MINFO(x)
Definition: misc_log_ex.h:75
bool set_module_name_and_folder(const std::string &path_to_process_)
Definition: string_tools.h:249
::std::string string
Definition: gtest-port.h:1097
int mdb_txn_commit(MDB_txn *txn)
Commit all the operations of a transaction into the database.
void mlog_set_log(const char *log)
Definition: mlog.cpp:288
#define CATCH_ENTRY(location, return_val)
Definition: misc_log_ex.h:152
std::string mlog_get_default_log_path(const char *default_filename)
Definition: mlog.cpp:72
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)
Definition: mlog.cpp:148
const char * key
Definition: hmac_keccak.cpp:39
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
const command_line::arg_descriptor< std::string > arg_db_sync_mode
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)
struct MDB_env MDB_env
Opaque structure for a database environment.
Definition: lmdb.h:260
#define MDEBUG(x)
Definition: misc_log_ex.h:76
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
Definition: lmdb.h:267
bool on_startup()
Definition: util.cpp:778
const char *const ELECTRONEUM_VERSION_FULL
bool blockchain_valid_db_type(const std::string &db_type)
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
bool handle_error_helper(const boost::program_options::options_description &desc, F parser)
Definition: command_line.h:237
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
unsigned int uint32_t
Definition: stdint.h:126
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)
unsigned __int64 uint64_t
Definition: stdint.h:136
unsigned int MDB_dbi
A handle for an individual database in the DB environment.
Definition: lmdb.h:270
int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Create a transaction for use with the environment.
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
Definition: command_line.h:188
void mdb_txn_abort(MDB_txn *txn)
Abandon all the operations of the transaction instead of saving them.
static bool install(T t)
installs a signal handler
Definition: util.h:164
struct MDB_cursor MDB_cursor
Opaque structure for navigating through a database.
Definition: lmdb.h:273
const T & move(const T &t)
Definition: gtest-port.h:1317
#define ENDL
Definition: misc_log_ex.h:149
POD_CLASS key_image
Definition: crypto.h:102
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
char * mdb_strerror(int err)
Return a string describing a given error code.
std::string to_string(t_connection_type type)
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:265
int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor)
Create a cursor handle.
Here is the call graph for this function: