Electroneum
blockchain_depth.cpp File Reference
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string.hpp>
#include "common/command_line.h"
#include "common/varint.h"
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/db_types.h"
#include "version.h"
Include dependency graph for blockchain_depth.cpp:

Go to the source code of this file.

Macros

#define ELECTRONEUM_DEFAULT_LOG_CATEGORY   "bcutil"
 

Functions

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

Macro Definition Documentation

◆ ELECTRONEUM_DEFAULT_LOG_CATEGORY

#define ELECTRONEUM_DEFAULT_LOG_CATEGORY   "bcutil"

Definition at line 41 of file blockchain_depth.cpp.

Function Documentation

◆ main()

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

Definition at line 47 of file blockchain_depth.cpp.

48 {
49  TRY_ENTRY();
50 
52 
53  std::string default_db_type = "lmdb";
54 
55  std::string available_dbs = cryptonote::blockchain_db_types(", ");
56  available_dbs = "available: " + available_dbs;
57 
58  uint32_t log_level = 0;
59 
61 
62  boost::filesystem::path output_file_path;
63 
64  po::options_description desc_cmd_only("Command line options");
65  po::options_description desc_cmd_sett("Command line options and settings options");
66  const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
67  const command_line::arg_descriptor<std::string> arg_database = {
68  "database", available_dbs.c_str(), default_db_type
69  };
70  const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get min depth for this txid", ""};
71  const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get min depth for all txes at this height", 0};
72  const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Include coinbase in the average", false};
73 
77  command_line::add_arg(desc_cmd_sett, arg_log_level);
78  command_line::add_arg(desc_cmd_sett, arg_database);
79  command_line::add_arg(desc_cmd_sett, arg_txid);
80  command_line::add_arg(desc_cmd_sett, arg_height);
81  command_line::add_arg(desc_cmd_sett, arg_include_coinbase);
83 
84  po::options_description desc_options("Allowed options");
85  desc_options.add(desc_cmd_only).add(desc_cmd_sett);
86 
87  po::variables_map vm;
88  bool r = command_line::handle_error_helper(desc_options, [&]()
89  {
90  auto parser = po::command_line_parser(argc, argv).options(desc_options);
91  po::store(parser.run(), vm);
92  po::notify(vm);
93  return true;
94  });
95  if (! r)
96  return 1;
97 
99  {
100  std::cout << "Electroneum '" << ELECTRONEUM_RELEASE_NAME << "' (v" << ELECTRONEUM_VERSION_FULL << ")" << ENDL << ENDL;
101  std::cout << desc_options << std::endl;
102  return 1;
103  }
104 
105  mlog_configure(mlog_get_default_log_path("electroneum-blockchain-depth.log"), true);
108  else
109  mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
110 
111  LOG_PRINT_L0("Starting...");
112 
114  bool opt_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
115  bool opt_stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
116  network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
117  std::string opt_txid_string = command_line::get_arg(vm, arg_txid);
118  uint64_t opt_height = command_line::get_arg(vm, arg_height);
119  bool opt_include_coinbase = command_line::get_arg(vm, arg_include_coinbase);
120 
121  if (!opt_txid_string.empty() && opt_height)
122  {
123  std::cerr << "txid and height cannot be given at the same time" << std::endl;
124  return 1;
125  }
126  crypto::hash opt_txid = crypto::null_hash;
127  if (!opt_txid_string.empty())
128  {
129  if (!epee::string_tools::hex_to_pod(opt_txid_string, opt_txid))
130  {
131  std::cerr << "Invalid txid" << std::endl;
132  return 1;
133  }
134  }
135 
136  std::string db_type = command_line::get_arg(vm, arg_database);
138  {
139  std::cerr << "Invalid database type: " << db_type << std::endl;
140  return 1;
141  }
142 
143  // If we wanted to use the memory pool, we would set up a fake_core.
144 
145  // Use Blockchain instead of lower-level BlockchainDB for two reasons:
146  // 1. Blockchain has the init() method for easy setup
147  // 2. exporter needs to use get_current_blockchain_height(), get_block_id_by_height(), get_block_by_hash()
148  //
149  // cannot match blockchain_storage setup above with just one line,
150  // e.g.
151  // Blockchain* core_storage = new Blockchain(NULL);
152  // because unlike blockchain_storage constructor, which takes a pointer to
153  // tx_memory_pool, Blockchain's constructor takes tx_memory_pool object.
154  LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
155  std::unique_ptr<Blockchain> core_storage;
156  tx_memory_pool m_mempool(*core_storage);
157  core_storage.reset(new Blockchain(m_mempool));
158  BlockchainDB *db = new_db(db_type);
159  if (db == NULL)
160  {
161  LOG_ERROR("Attempted to use non-existent database type: " << db_type);
162  throw std::runtime_error("Attempting to use non-existent database type");
163  }
164  LOG_PRINT_L0("database: " << db_type);
165 
166  const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
167  LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
168 
169  try
170  {
171  db->open(filename, DBF_RDONLY);
172  }
173  catch (const std::exception& e)
174  {
175  LOG_PRINT_L0("Error opening database: " << e.what());
176  return 1;
177  }
178  r = core_storage->init(db, net_type);
179 
180  CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize source blockchain storage");
181  LOG_PRINT_L0("Source blockchain storage initialized OK");
182 
183  std::vector<crypto::hash> start_txids;
184  if (!opt_txid_string.empty())
185  {
186  start_txids.push_back(opt_txid);
187  }
188  else
189  {
190  const cryptonote::blobdata bd = db->get_block_blob_from_height(opt_height);
193  {
194  LOG_PRINT_L0("Bad block from db");
195  return 1;
196  }
197  for (const crypto::hash &txid: b.tx_hashes)
198  start_txids.push_back(txid);
199  if (opt_include_coinbase)
200  start_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
201  }
202 
203  if (start_txids.empty())
204  {
205  LOG_PRINT_L0("No transaction(s) to check");
206  return 1;
207  }
208 
209  std::vector<uint64_t> depths;
210  for (const crypto::hash &start_txid: start_txids)
211  {
212  uint64_t depth = 0;
213  bool coinbase = false;
214 
215  LOG_PRINT_L0("Checking depth for txid " << start_txid);
216  std::vector<crypto::hash> txids(1, start_txid);
217  while (!coinbase)
218  {
219  LOG_PRINT_L0("Considering "<< txids.size() << " transaction(s) at depth " << depth);
220  std::vector<crypto::hash> new_txids;
221  for (const crypto::hash &txid: txids)
222  {
224  if (!db->get_pruned_tx_blob(txid, bd))
225  {
226  LOG_PRINT_L0("Failed to get txid " << txid << " from db");
227  return 1;
228  }
231  {
232  LOG_PRINT_L0("Bad tx: " << txid);
233  return 1;
234  }
235  for (size_t ring = 0; ring < tx.vin.size(); ++ring)
236  {
237  if (tx.vin[ring].type() == typeid(cryptonote::txin_gen))
238  {
239  MDEBUG(txid << " is a coinbase transaction");
240  coinbase = true;
241  goto done;
242  }
243  if (tx.vin[ring].type() == typeid(cryptonote::txin_to_key))
244  {
245  const cryptonote::txin_to_key &txin = boost::get<cryptonote::txin_to_key>(tx.vin[ring]);
246  const uint64_t amount = txin.amount;
247  auto absolute_offsets = cryptonote::relative_output_offsets_to_absolute(txin.key_offsets);
248  for (uint64_t offset: absolute_offsets)
249  {
250  const output_data_t od = db->get_output_key(amount, offset);
251  const crypto::hash block_hash = db->get_block_hash_from_height(od.height);
252  bd = db->get_block_blob(block_hash);
255  {
256  LOG_PRINT_L0("Bad block from db");
257  return 1;
258  }
259  // find the tx which created this output
260  bool found = false;
261  for (size_t out = 0; out < b.miner_tx.vout.size(); ++out)
262  {
263  if (b.miner_tx.vout[out].target.type() == typeid(cryptonote::txout_to_key))
264  {
265  const auto &txout = boost::get<cryptonote::txout_to_key>(b.miner_tx.vout[out].target);
266  if (txout.key == od.pubkey)
267  {
268  found = true;
269  new_txids.push_back(cryptonote::get_transaction_hash(b.miner_tx));
270  MDEBUG("adding txid: " << cryptonote::get_transaction_hash(b.miner_tx));
271  break;
272  }
273  }
274  else
275  {
276  LOG_PRINT_L0("Bad vout type in txid " << cryptonote::get_transaction_hash(b.miner_tx));
277  return 1;
278  }
279  }
280  for (const crypto::hash &block_txid: b.tx_hashes)
281  {
282  if (found)
283  break;
284  if (!db->get_pruned_tx_blob(block_txid, bd))
285  {
286  LOG_PRINT_L0("Failed to get txid " << block_txid << " from db");
287  return 1;
288  }
291  {
292  LOG_PRINT_L0("Bad tx: " << block_txid);
293  return 1;
294  }
295  for (size_t out = 0; out < tx2.vout.size(); ++out)
296  {
297  if (tx2.vout[out].target.type() == typeid(cryptonote::txout_to_key))
298  {
299  const auto &txout = boost::get<cryptonote::txout_to_key>(tx2.vout[out].target);
300  if (txout.key == od.pubkey)
301  {
302  found = true;
303  new_txids.push_back(block_txid);
304  MDEBUG("adding txid: " << block_txid);
305  break;
306  }
307  }
308  else
309  {
310  LOG_PRINT_L0("Bad vout type in txid " << block_txid);
311  return 1;
312  }
313  }
314  }
315  if (!found)
316  {
317  LOG_PRINT_L0("Output originating transaction not found");
318  return 1;
319  }
320  }
321  }
322  else
323  {
324  LOG_PRINT_L0("Bad vin type in txid " << txid);
325  return 1;
326  }
327  }
328  }
329  if (!coinbase)
330  {
331  std::swap(txids, new_txids);
332  ++depth;
333  }
334  }
335 done:
336  LOG_PRINT_L0("Min depth for txid " << start_txid << ": " << depth);
337  depths.push_back(depth);
338  }
339 
340  uint64_t cumulative_depth = 0;
341  for (uint64_t depth: depths)
342  cumulative_depth += depth;
343  LOG_PRINT_L0("Average min depth for " << start_txids.size() << " transaction(s): " << cumulative_depth/(float)depths.size());
344  LOG_PRINT_L0("Median min depth for " << start_txids.size() << " transaction(s): " << epee::misc_utils::median(depths));
345 
346  core_storage->deinit();
347  return 0;
348 
349  CATCH_ENTRY("Depth query error", 1);
350 }
const char *const ELECTRONEUM_RELEASE_NAME
std::vector< crypto::hash > tx_hashes
virtual std::string get_db_name() const =0
gets the name of the folder the BlockchainDB&#39;s file(s) should be in
#define DBF_RDONLY
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t &height) const =0
fetch a block blob by height
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
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
Definition: misc_log_ex.h:181
virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const =0
fetch a block&#39;s hash
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
#define LOG_PRINT_L0(x)
Definition: misc_log_ex.h:99
const command_line::arg_descriptor< bool, false > arg_stagenet_on
const arg_descriptor< bool > arg_help
std::vector< uint64_t > key_offsets
std::string blockchain_db_types(const std::string &sep)
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
virtual void open(const std::string &filename, const int db_flags=0)=0
open a db, or create it if necessary.
virtual bool get_pruned_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const =0
fetches the pruned transaction blob with the given hash
#define MDEBUG(x)
Definition: misc_log_ex.h:76
bool on_startup()
Definition: util.cpp:778
const char *const ELECTRONEUM_VERSION_FULL
bool blockchain_valid_db_type(const std::string &db_type)
const command_line::arg_descriptor< bool, false > arg_testnet_on
virtual output_data_t get_output_key(const uint64_t &amount, const uint64_t &index, bool include_commitmemt=true) const =0
get some of an output&#39;s data
BlockchainDB * new_db(const std::string &db_type)
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
crypto::public_key pubkey
the output&#39;s public key (for spend verification)
unsigned __int64 uint64_t
Definition: stdint.h:136
const command_line::arg_descriptor< std::string, false, true, 2 > arg_data_dir
uint64_t height
the height of the block which created the output
bool hex_to_pod(const std::string &hex_str, t_pod_type &s)
Definition: string_tools.h:324
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
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
Transaction pool, handles transactions which are not part of a block.
Definition: tx_pool.h:94
std::string blobdata
Definition: blobdatatype.h:39
type_vec_type median(std::vector< type_vec_type > &v)
The BlockchainDB backing store interface declaration/contract.
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
#define ENDL
Definition: misc_log_ex.h:149
crypto::hash get_transaction_hash(const transaction &t)
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
bool init(BlockchainDB *db, const network_type nettype=MAINNET, bool offline=false, const cryptonote::test_options *test_options=NULL, difficulty_type fixed_difficulty=0, const GetCheckpointsCallback &get_checkpoints=nullptr, bool ignore_bsig=false, bool fallback_to_pow=false)
Initialize the Blockchain state.
Definition: blockchain.cpp:334
POD_CLASS hash
Definition: hash.h:50
std::string to_string(t_connection_type type)
a struct containing output metadata
virtual cryptonote::blobdata get_block_blob(const crypto::hash &h) const =0
fetches the block with the given hash
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
bool deinit()
Uninitializes the blockchain state.
Definition: blockchain.cpp:660
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
Here is the call graph for this function: