diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c85600..4437c5b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,13 +6,12 @@ set(SOURCE_HEADERS MicroCore.h tools.h monero_headers.h - tx_details.h) + ) set(SOURCE_FILES MicroCore.cpp tools.cpp CmdLineOptions.cpp - tx_details.cpp page.h rpccalls.cpp rpccalls.h version.h.in) diff --git a/src/MicroCore.h b/src/MicroCore.h index 4255c22..678d0b6 100644 --- a/src/MicroCore.h +++ b/src/MicroCore.h @@ -8,8 +8,7 @@ #include #include "monero_headers.h" -#include "tx_details.h" - +#include "tools.h" namespace xmreg { diff --git a/src/page.h b/src/page.h index fea483d..d19dabf 100644 --- a/src/page.h +++ b/src/page.h @@ -2210,6 +2210,23 @@ public: if (it != tx_key_imgs.end()) { uint64_t xmr_amount = (*it).amount; + + // RingCT, i.e., tx version is 2 + // thus need to decode the amounts + // otherwise they all appear to be zero. + // so to find the amount, first we need to find + // our output in the key images's mixins, and then + + if (tx.version == 2) + { +// bool r = decode_ringct(tx.rct_signatures, +// txd.pk, +// prv_view_key, +// i, +// tx.rct_signatures.ecdhInfo[i].mask, +// rct_amount); + } + key_img_info["amount"] = xmreg::xmr_amount_to_str(xmr_amount); total_xmr += xmr_amount; } diff --git a/src/tools.cpp b/src/tools.cpp index d49db75..422967d 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -9,894 +9,944 @@ namespace xmreg { - /** - * Parse key string, e.g., a viewkey in a string - * into crypto::secret_key or crypto::public_key - * depending on the template argument. - */ - template - bool - parse_str_secret_key(const string& key_str, T& secret_key) - { - - // hash and keys have same structure, so to parse string of - // a key, e.g., a view key, we can first parse it into the hash - // object using parse_hash256 function, and then copy the reslting - // hash data into secret key. - crypto::hash hash_; - - if(!parse_hash256(key_str, hash_)) - { - cerr << "Cant parse a key (e.g. viewkey): " << key_str << endl; - return false; - } +/** + * Parse key string, e.g., a viewkey in a string + * into crypto::secret_key or crypto::public_key + * depending on the template argument. + */ +template +bool +parse_str_secret_key(const string& key_str, T& secret_key) +{ - // crypto::hash and crypto::secret_key have basicly same - // structure. They both keep they key/hash as c-style char array - // of fixed size. Thus we can just copy data from hash - // to key - copy(begin(hash_.data), end(hash_.data), secret_key.data); + // hash and keys have same structure, so to parse string of + // a key, e.g., a view key, we can first parse it into the hash + // object using parse_hash256 function, and then copy the reslting + // hash data into secret key. + crypto::hash hash_; - return true; + if(!parse_hash256(key_str, hash_)) + { + cerr << "Cant parse a key (e.g. viewkey): " << key_str << endl; + return false; } - // explicit instantiations of get template function - template bool parse_str_secret_key(const string& key_str, crypto::secret_key& secret_key); - template bool parse_str_secret_key(const string& key_str, crypto::public_key& secret_key); - template bool parse_str_secret_key(const string& key_str, crypto::hash& secret_key); + // crypto::hash and crypto::secret_key have basicly same + // structure. They both keep they key/hash as c-style char array + // of fixed size. Thus we can just copy data from hash + // to key + copy(begin(hash_.data), end(hash_.data), secret_key.data); - /** - * Get transaction tx using given tx hash. Hash is represent as string here, - * so before we can tap into the blockchain, we need to pare it into - * crypto::hash object. - */ - bool - get_tx_pub_key_from_str_hash(Blockchain& core_storage, const string& hash_str, transaction& tx) - { - crypto::hash tx_hash; - parse_hash256(hash_str, tx_hash); + return true; +} - try - { - // get transaction with given hash - tx = core_storage.get_db().get_tx(tx_hash); - } - catch (const TX_DNE& e) - { - cerr << e.what() << endl; - return false; - } +// explicit instantiations of get template function +template bool parse_str_secret_key(const string& key_str, crypto::secret_key& secret_key); +template bool parse_str_secret_key(const string& key_str, crypto::public_key& secret_key); +template bool parse_str_secret_key(const string& key_str, crypto::hash& secret_key); + +/** + * Get transaction tx using given tx hash. Hash is represent as string here, + * so before we can tap into the blockchain, we need to pare it into + * crypto::hash object. + */ +bool +get_tx_pub_key_from_str_hash(Blockchain& core_storage, const string& hash_str, transaction& tx) +{ + crypto::hash tx_hash; + parse_hash256(hash_str, tx_hash); - return true; + try + { + // get transaction with given hash + tx = core_storage.get_db().get_tx(tx_hash); } - - /** - * Parse monero address in a string form into - * cryptonote::account_public_address object - */ - bool - parse_str_address(const string& address_str, - account_public_address& address, - bool testnet) + catch (const TX_DNE& e) { - - if (!get_account_address_from_str(address, testnet, address_str)) - { - cerr << "Error getting address: " << address_str << endl; - return false; - } - - return true; + cerr << e.what() << endl; + return false; } + return true; +} + +/** + * Parse monero address in a string form into + * cryptonote::account_public_address object + */ +bool +parse_str_address(const string& address_str, + account_public_address& address, + bool testnet) +{ - /** - * Return string representation of monero address - */ - string - print_address(const account_public_address& address, bool testnet) + if (!get_account_address_from_str(address, testnet, address_str)) { - return "<" + get_account_address_as_str(testnet, address) + ">"; + cerr << "Error getting address: " << address_str << endl; + return false; } - string - print_sig (const signature& sig) - { - stringstream ss; + return true; +} - ss << "c: <" << epee::string_tools::pod_to_hex(sig.c) << "> " - << "r: <" << epee::string_tools::pod_to_hex(sig.r) << ">"; - return ss.str(); - } +/** + * Return string representation of monero address + */ +string +print_address(const account_public_address& address, bool testnet) +{ + return "<" + get_account_address_as_str(testnet, address) + ">"; +} + +string +print_sig (const signature& sig) +{ + stringstream ss; + + ss << "c: <" << epee::string_tools::pod_to_hex(sig.c) << "> " + << "r: <" << epee::string_tools::pod_to_hex(sig.r) << ">"; + + return ss.str(); +} + +/** + * Check if a character is a path seprator + */ +inline bool +is_separator(char c) +{ + // default linux path separator + const char separator = PATH_SEPARARTOR; + + return c == separator; +} - /** - * Check if a character is a path seprator - */ - inline bool - is_separator(char c) - { - // default linux path separator - const char separator = PATH_SEPARARTOR; - return c == separator; - } +/** + * Remove trailinig path separator. + */ +string +remove_trailing_path_separator(const string& in_path) +{ + string new_string = in_path; + if (!new_string.empty() && is_separator(new_string[new_string.size() - 1])) + new_string.erase(new_string.size() - 1); + return new_string; +} + +bf::path +remove_trailing_path_separator(const bf::path& in_path) +{ + string path_str = in_path.native(); + return bf::path(remove_trailing_path_separator(path_str)); +} +string +timestamp_to_str(time_t timestamp, const char* format) +{ + auto a_time_point = chrono::system_clock::from_time_t(timestamp); - /** - * Remove trailinig path separator. - */ - string - remove_trailing_path_separator(const string& in_path) + try { - string new_string = in_path; - if (!new_string.empty() && is_separator(new_string[new_string.size() - 1])) - new_string.erase(new_string.size() - 1); - return new_string; - } + auto utc = date::to_utc_time(chrono::system_clock::from_time_t(timestamp)); + auto sys_time = date::to_sys_time(utc); - bf::path - remove_trailing_path_separator(const bf::path& in_path) + return date::format(format, date::floor(sys_time)); + } + catch (std::runtime_error& e) { - string path_str = in_path.native(); - return bf::path(remove_trailing_path_separator(path_str)); + cerr << "xmreg::timestamp_to_str: " << e.what() << endl; + cerr << "Seems cant convert to UTC timezone using date libary. " + "So just use local timezone." <(sys_time)); - } - catch (std::runtime_error& e) - { - cerr << "xmreg::timestamp_to_str: " << e.what() << endl; - cerr << "Seems cant convert to UTC timezone using date libary. " - "So just use local timezone." </.bitmonero + string default_monero_dir = tools::get_default_data_dir(); - crypto::derive_secret_key(derivation, i, - sec_key, - in_ephemeral.sec); - } - catch(const std::exception& e) - { - cerr << "Error generate secret image: " << e.what() << endl; - return false; - } + if (testnet) + default_monero_dir += "/testnet"; - try - { - crypto::generate_key_image(in_ephemeral.pub, - in_ephemeral.sec, - key_img); - } - catch(const std::exception& e) - { - cerr << "Error generate key image: " << e.what() << endl; - return false; - } + // the default folder of the lmdb blockchain database + // is therefore as follows + return default_monero_dir + string("/lmdb"); +} - return true; - } +/* + * Ge blockchain exception from command line option + * + * If not given, provide default path + */ +bool +get_blockchain_path(const boost::optional& bc_path, + bf::path& blockchain_path, + bool testnet) +{ + // the default folder of the lmdb blockchain database + string default_lmdb_dir = xmreg::get_default_lmdb_folder(testnet); + + blockchain_path = bc_path + ? bf::path(*bc_path) + : bf::path(default_lmdb_dir); - string - get_default_lmdb_folder(bool testnet) + if (!bf::is_directory(blockchain_path)) { - // default path to monero folder - // on linux this is /home//.bitmonero - string default_monero_dir = tools::get_default_data_dir(); + cerr << "Given path \"" << blockchain_path << "\" " + << "is not a folder or does not exist" << " " + << endl; - if (testnet) - default_monero_dir += "/testnet"; + return false; + } + blockchain_path = xmreg::remove_trailing_path_separator(blockchain_path); - // the default folder of the lmdb blockchain database - // is therefore as follows - return default_monero_dir + string("/lmdb"); - } + return true; +} - /* - * Ge blockchain exception from command line option - * - * If not given, provide default path - */ - bool - get_blockchain_path(const boost::optional& bc_path, - bf::path& blockchain_path, - bool testnet) - { - // the default folder of the lmdb blockchain database - string default_lmdb_dir = xmreg::get_default_lmdb_folder(testnet); +uint64_t +sum_money_in_outputs(const transaction& tx) +{ + uint64_t sum_xmr {0}; - blockchain_path = bc_path - ? bf::path(*bc_path) - : bf::path(default_lmdb_dir); + for (const tx_out& txout: tx.vout) + { + sum_xmr += txout.amount; + } - if (!bf::is_directory(blockchain_path)) - { - cerr << "Given path \"" << blockchain_path << "\" " - << "is not a folder or does not exist" << " " - << endl; + return sum_xmr; +} - return false; - } - blockchain_path = xmreg::remove_trailing_path_separator(blockchain_path); - return true; - } +uint64_t +sum_money_in_inputs(const transaction& tx) +{ + uint64_t sum_xmr {0}; + size_t input_no = tx.vin.size(); - uint64_t - sum_money_in_outputs(const transaction& tx) + for (size_t i = 0; i < input_no; ++i) { - uint64_t sum_xmr {0}; - for (const tx_out& txout: tx.vout) + if(tx.vin[i].type() != typeid(cryptonote::txin_to_key)) { - sum_xmr += txout.amount; + continue; } - return sum_xmr; - } + // get tx input key + const cryptonote::txin_to_key& tx_in_to_key + = boost::get(tx.vin[i]); + sum_xmr += tx_in_to_key.amount; + } + return sum_xmr; +} - uint64_t - sum_money_in_inputs(const transaction& tx) - { - uint64_t sum_xmr {0}; - size_t input_no = tx.vin.size(); - for (size_t i = 0; i < input_no; ++i) - { +array +sum_money_in_tx(const transaction& tx) +{ + array sum_xmr; - if(tx.vin[i].type() != typeid(cryptonote::txin_to_key)) - { - continue; - } + sum_xmr[0] = sum_money_in_inputs(tx); + sum_xmr[1] = sum_money_in_outputs(tx); - // get tx input key - const cryptonote::txin_to_key& tx_in_to_key - = boost::get(tx.vin[i]); - - sum_xmr += tx_in_to_key.amount; - } - - return sum_xmr; - } + return sum_xmr; +}; +array +sum_money_in_txs(const vector& txs) +{ + array sum_xmr {0,0}; - array - sum_money_in_tx(const transaction& tx) + for (const transaction& tx: txs) { - array sum_xmr; + sum_xmr[0] += sum_money_in_inputs(tx); + sum_xmr[1] += sum_money_in_outputs(tx); + } - sum_xmr[0] = sum_money_in_inputs(tx); - sum_xmr[1] = sum_money_in_outputs(tx); + return sum_xmr; +}; - return sum_xmr; - }; +uint64_t +sum_fees_in_txs(const vector& txs) +{ + uint64_t fees_sum {0}; - array - sum_money_in_txs(const vector& txs) + for (const transaction& tx: txs) { - array sum_xmr {0,0}; + fees_sum += get_tx_fee(tx); + } - for (const transaction& tx: txs) - { - sum_xmr[0] += sum_money_in_inputs(tx); - sum_xmr[1] += sum_money_in_outputs(tx); - } + return fees_sum; +} - return sum_xmr; - }; - uint64_t - sum_fees_in_txs(const vector& txs) - { - uint64_t fees_sum {0}; +vector> +get_ouputs(const transaction& tx) +{ + vector> outputs; - for (const transaction& tx: txs) + for (const tx_out& txout: tx.vout) + { + if (txout.target.type() != typeid(txout_to_key)) { - fees_sum += get_tx_fee(tx); + continue; } - return fees_sum; + // get tx input key + const txout_to_key& txout_key + = boost::get(txout.target); + + outputs.push_back(make_pair(txout_key, txout.amount)); } + return outputs; +}; - vector> - get_ouputs(const transaction& tx) +vector> +get_ouputs_tuple(const transaction& tx) +{ + vector> outputs; + + for (uint64_t n = 0; n < tx.vout.size(); ++n) { - vector> outputs; - for (const tx_out& txout: tx.vout) + if (tx.vout[n].target.type() != typeid(txout_to_key)) { - if (txout.target.type() != typeid(txout_to_key)) - { - continue; - } + continue; + } - // get tx input key - const txout_to_key& txout_key - = boost::get(txout.target); + // get tx input key + const txout_to_key& txout_key + = boost::get(tx.vout[n].target); - outputs.push_back(make_pair(txout_key, txout.amount)); - } + outputs.push_back(make_tuple(txout_key, tx.vout[n].amount, n)); + } - return outputs; + return outputs; +}; - }; +uint64_t +get_mixin_no(const transaction& tx) +{ + uint64_t mixin_no {0}; - vector> - get_ouputs_tuple(const transaction& tx) + size_t input_no = tx.vin.size(); + + for (size_t i = 0; i < input_no; ++i) { - vector> outputs; - for (uint64_t n = 0; n < tx.vout.size(); ++n) + if(tx.vin[i].type() != typeid(cryptonote::txin_to_key)) { + continue; + } - if (tx.vout[n].target.type() != typeid(txout_to_key)) - { - continue; - } + // get tx input key + const txin_to_key& tx_in_to_key + = boost::get(tx.vin[i]); - // get tx input key - const txout_to_key& txout_key - = boost::get(tx.vout[n].target); + mixin_no = tx_in_to_key.key_offsets.size(); - outputs.push_back(make_tuple(txout_key, tx.vout[n].amount, n)); + // look for first mixin number. + // all inputs in a single transaction have same number + if (mixin_no > 0) + { + break; } + } - return outputs; - }; - - uint64_t - get_mixin_no(const transaction& tx) - { - uint64_t mixin_no {0}; - - size_t input_no = tx.vin.size(); + return mixin_no; +} - for (size_t i = 0; i < input_no; ++i) - { +vector +get_mixin_no_in_txs(const vector& txs) +{ + vector mixin_no; - if(tx.vin[i].type() != typeid(cryptonote::txin_to_key)) - { - continue; - } + for (const transaction& tx: txs) + { + mixin_no.push_back(get_mixin_no(tx)); + } - // get tx input key - const txin_to_key& tx_in_to_key - = boost::get(tx.vin[i]); + return mixin_no; +} - mixin_no = tx_in_to_key.key_offsets.size(); - // look for first mixin number. - // all inputs in a single transaction have same number - if (mixin_no > 0) - { - break; - } - } +vector +get_key_images(const transaction& tx) +{ + vector key_images; - return mixin_no; - } + size_t input_no = tx.vin.size(); - vector - get_mixin_no_in_txs(const vector& txs) + for (size_t i = 0; i < input_no; ++i) { - vector mixin_no; - for (const transaction& tx: txs) + if(tx.vin[i].type() != typeid(txin_to_key)) { - mixin_no.push_back(get_mixin_no(tx)); + continue; } - return mixin_no; - } - - - vector - get_key_images(const transaction& tx) - { - vector key_images; + // get tx input key + const txin_to_key& tx_in_to_key + = boost::get(tx.vin[i]); - size_t input_no = tx.vin.size(); + key_images.push_back(tx_in_to_key); + } - for (size_t i = 0; i < input_no; ++i) - { + return key_images; +} - if(tx.vin[i].type() != typeid(txin_to_key)) - { - continue; - } - // get tx input key - const txin_to_key& tx_in_to_key - = boost::get(tx.vin[i]); +bool +get_payment_id(const vector& extra, + crypto::hash& payment_id, + crypto::hash8& payment_id8) +{ + payment_id = null_hash; + payment_id8 = null_hash8; - key_images.push_back(tx_in_to_key); - } + std::vector tx_extra_fields; - return key_images; + if(!parse_tx_extra(extra, tx_extra_fields)) + { + return false; } + tx_extra_nonce extra_nonce; - bool - get_payment_id(const vector& extra, - crypto::hash& payment_id, - crypto::hash8& payment_id8) + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - payment_id = null_hash; - payment_id8 = null_hash8; - - std::vector tx_extra_fields; - - if(!parse_tx_extra(extra, tx_extra_fields)) + // first check for encrypted id and then for normal one + if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) { - return false; + return true; } - - tx_extra_nonce extra_nonce; - - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) { - // first check for encrypted id and then for normal one - if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) - { - return true; - } - else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - { - return true; - } + return true; } - - return false; - } - - - bool - get_payment_id(const transaction& tx, - crypto::hash& payment_id, - crypto::hash8& payment_id8) - { - return get_payment_id(tx.extra, payment_id, payment_id8); } + return false; +} - array - timestamp_difference(uint64_t t1, uint64_t t2) - { - uint64_t timestamp_diff = t1 - t2; +bool +get_payment_id(const transaction& tx, + crypto::hash& payment_id, + crypto::hash8& payment_id8) +{ + return get_payment_id(tx.extra, payment_id, payment_id8); +} - // calculate difference of timestamps from current block to the mixin one - if (t2 > t1) - { - timestamp_diff = t2 - t1; - } - uint64_t time_diff_years = timestamp_diff / 31536000; +array +timestamp_difference(uint64_t t1, uint64_t t2) +{ - timestamp_diff -= time_diff_years * 31536000; + uint64_t timestamp_diff = t1 - t2; - uint64_t time_diff_days = timestamp_diff / 86400; + // calculate difference of timestamps from current block to the mixin one + if (t2 > t1) + { + timestamp_diff = t2 - t1; + } - timestamp_diff -= time_diff_days * 86400; + uint64_t time_diff_years = timestamp_diff / 31536000; - uint64_t time_diff_hours = timestamp_diff / 3600; + timestamp_diff -= time_diff_years * 31536000; - timestamp_diff -= time_diff_hours * 3600; + uint64_t time_diff_days = timestamp_diff / 86400; - uint64_t time_diff_minutes = timestamp_diff / 60; + timestamp_diff -= time_diff_days * 86400; - timestamp_diff -= time_diff_minutes * 60; + uint64_t time_diff_hours = timestamp_diff / 3600; - uint64_t time_diff_seconds = timestamp_diff ; + timestamp_diff -= time_diff_hours * 3600; - return array {time_diff_years, time_diff_days, - time_diff_hours, time_diff_minutes, - time_diff_seconds}; + uint64_t time_diff_minutes = timestamp_diff / 60; - }; + timestamp_diff -= time_diff_minutes * 60; + uint64_t time_diff_seconds = timestamp_diff ; - string - read(string filename) - { - if (!bf::exists(bf::path(filename))) - { - cerr << "File does not exist: " << filename << endl; - return string(); - } + return array {time_diff_years, time_diff_days, + time_diff_hours, time_diff_minutes, + time_diff_seconds}; - std::ifstream t(filename); - return string(std::istreambuf_iterator(t), - std::istreambuf_iterator()); - } +}; - pair - timestamps_time_scale(const vector& timestamps, - uint64_t timeN, - uint64_t resolution, - uint64_t time0) - { - string empty_time = string(resolution, '_'); - size_t time_axis_length = empty_time.size(); +string +read(string filename) +{ + if (!bf::exists(bf::path(filename))) + { + cerr << "File does not exist: " << filename << endl; + return string(); + } + + std::ifstream t(filename); + return string(std::istreambuf_iterator(t), + std::istreambuf_iterator()); +} - uint64_t interval_length = timeN-time0; +pair +timestamps_time_scale(const vector& timestamps, + uint64_t timeN, + uint64_t resolution, + uint64_t time0) +{ + string empty_time = string(resolution, '_'); - double scale = double(interval_length) / double(time_axis_length); + size_t time_axis_length = empty_time.size(); - for (const auto& timestamp: timestamps) - { + uint64_t interval_length = timeN-time0; - if (timestamp < time0 || timestamp > timeN) - { - cout << "Out of range" << endl; - continue; - } + double scale = double(interval_length) / double(time_axis_length); - uint64_t timestamp_place = double(timestamp-time0) - / double(interval_length)*(time_axis_length - 1); + for (const auto& timestamp: timestamps) + { - empty_time[timestamp_place + 1] = '*'; + if (timestamp < time0 || timestamp > timeN) + { + cout << "Out of range" << endl; + continue; } - return make_pair(empty_time, scale); - } + uint64_t timestamp_place = double(timestamp-time0) + / double(interval_length)*(time_axis_length - 1); - // useful reference to get epoch time in correct timezon - // http://www.boost.org/doc/libs/1_41_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch - time_t - ptime_to_time_t(const pt::ptime& in_ptime) - { - static pt::ptime epoch(gt::date(1970, 1, 1)); - pt::time_duration::sec_type no_seconds = (in_ptime - epoch).total_seconds(); - return time_t(no_seconds); + empty_time[timestamp_place + 1] = '*'; } - bool - decode_ringct(const rct::rctSig& rv, - const crypto::public_key pub, - const crypto::secret_key &sec, - unsigned int i, - rct::key & mask, - uint64_t & amount) - { - crypto::key_derivation derivation; + return make_pair(empty_time, scale); +} - bool r = crypto::generate_key_derivation(pub, sec, derivation); +// useful reference to get epoch time in correct timezon +// http://www.boost.org/doc/libs/1_41_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch +time_t +ptime_to_time_t(const pt::ptime& in_ptime) +{ + static pt::ptime epoch(gt::date(1970, 1, 1)); + pt::time_duration::sec_type no_seconds = (in_ptime - epoch).total_seconds(); + return time_t(no_seconds); +} - if (!r) - { - cerr <<"Failed to generate key derivation to decode rct output " << i << endl; - return false; - } +bool +decode_ringct(const rct::rctSig& rv, + const crypto::public_key pub, + const crypto::secret_key &sec, + unsigned int i, + rct::key & mask, + uint64_t & amount) +{ + crypto::key_derivation derivation; - crypto::secret_key scalar1; + bool r = crypto::generate_key_derivation(pub, sec, derivation); - crypto::derivation_to_scalar(derivation, i, scalar1); + if (!r) + { + cerr <<"Failed to generate key derivation to decode rct output " << i << endl; + return false; + } - try - { - switch (rv.type) - { - case rct::RCTTypeSimple: - amount = rct::decodeRctSimple(rv, - rct::sk2rct(scalar1), - i, - mask); - break; - case rct::RCTTypeFull: - amount = rct::decodeRct(rv, - rct::sk2rct(scalar1), - i, - mask); - break; - default: - cerr << "Unsupported rct type: " << rv.type << endl; - return false; - } - } - catch (const std::exception &e) + crypto::secret_key scalar1; + + crypto::derivation_to_scalar(derivation, i, scalar1); + + try + { + switch (rv.type) { - cerr << "Failed to decode input " << i << endl; - return false; + case rct::RCTTypeSimple: + amount = rct::decodeRctSimple(rv, + rct::sk2rct(scalar1), + i, + mask); + break; + case rct::RCTTypeFull: + amount = rct::decodeRct(rv, + rct::sk2rct(scalar1), + i, + mask); + break; + default: + cerr << "Unsupported rct type: " << rv.type << endl; + return false; } - - return true; } + catch (const std::exception &e) + { + cerr << "Failed to decode input " << i << endl; + return false; + } + + return true; +} - bool - url_decode(const std::string& in, std::string& out) +bool +url_decode(const std::string& in, std::string& out) +{ + out.clear(); + out.reserve(in.size()); + for (std::size_t i = 0; i < in.size(); ++i) { - out.clear(); - out.reserve(in.size()); - for (std::size_t i = 0; i < in.size(); ++i) + if (in[i] == '%') { - if (in[i] == '%') + if (i + 3 <= in.size()) { - if (i + 3 <= in.size()) + int value = 0; + std::istringstream is(in.substr(i + 1, 2)); + if (is >> std::hex >> value) { - int value = 0; - std::istringstream is(in.substr(i + 1, 2)); - if (is >> std::hex >> value) - { - out += static_cast(value); - i += 2; - } - else - { - return false; - } + out += static_cast(value); + i += 2; } else { return false; } } - else if (in[i] == '+') - { - out += ' '; - } else { - out += in[i]; + return false; } } - return true; + else if (in[i] == '+') + { + out += ' '; + } + else + { + out += in[i]; + } } + return true; +} - map - parse_crow_post_data(const string& req_body) - { - map body; +map +parse_crow_post_data(const string& req_body) +{ + map body; - vector vec; - string tmp; - bool result = url_decode(req_body, tmp); - if (result) + vector vec; + string tmp; + bool result = url_decode(req_body, tmp); + if (result) + { + boost::algorithm::split(vec, tmp, [](char x) {return x == '&'; }); + for(auto &it : vec) { - boost::algorithm::split(vec, tmp, [](char x) {return x == '&'; }); - for(auto &it : vec) + auto pos = it.find("="); + if (pos != string::npos) { - auto pos = it.find("="); - if (pos != string::npos) - { - body[it.substr(0, pos)] = it.substr(pos + 1); - } - else - { - break; - } + body[it.substr(0, pos)] = it.substr(pos + 1); + } + else + { + break; } } - return body; } + return body; +} + +bool +get_dummy_account_keys(account_keys& dummy_keys, bool testnet) +{ + secret_key adress_prv_viewkey; + secret_key adress_prv_spendkey; + + account_public_address dummy_address; - bool - get_dummy_account_keys(account_keys& dummy_keys, bool testnet) + if (!get_account_address_from_str(dummy_address, + testnet, + "4BAyX63gVQgDqKS1wmqNVHdcCNjq1jooLYCXsKEY9w7VdGh45oZbPLvN7y8oVg2zmnhECkRBXpREWb97KtfAcT6p1UNXm9K")) { - secret_key adress_prv_viewkey; - secret_key adress_prv_spendkey; + return false; + } - account_public_address dummy_address; - if (!get_account_address_from_str(dummy_address, - testnet, - "4BAyX63gVQgDqKS1wmqNVHdcCNjq1jooLYCXsKEY9w7VdGh45oZbPLvN7y8oVg2zmnhECkRBXpREWb97KtfAcT6p1UNXm9K")) - { - return false; - } + if (!epee::string_tools::hex_to_pod("f238be69411631f35b76c5a9148b3b7e8327eb41bfd0b396e090aeba40235d01", adress_prv_viewkey)) + { + return false; + } + if (!epee::string_tools::hex_to_pod("5db8e1d2c505f888e54aca15b1a365c8814d7deebc1a246690db3bf71324950d", adress_prv_spendkey)) + { + return false; + } - if (!epee::string_tools::hex_to_pod("f238be69411631f35b76c5a9148b3b7e8327eb41bfd0b396e090aeba40235d01", adress_prv_viewkey)) - { - return false; - } - if (!epee::string_tools::hex_to_pod("5db8e1d2c505f888e54aca15b1a365c8814d7deebc1a246690db3bf71324950d", adress_prv_spendkey)) - { - return false; - } + dummy_keys = account_keys { + dummy_address, + adress_prv_spendkey, + adress_prv_viewkey + }; + return true; +} - dummy_keys = account_keys { - dummy_address, - adress_prv_spendkey, - adress_prv_viewkey - }; - return true; + +// from wallet2::decrypt +string +decrypt(const std::string &ciphertext, + const crypto::secret_key &skey, + bool authenticated) +{ + + const size_t prefix_size = sizeof(chacha8_iv) + + (authenticated ? sizeof(crypto::signature) : 0); + if (ciphertext.size() < prefix_size) + { + cerr << "Unexpected ciphertext size" << endl; + return {}; } + crypto::chacha8_key key; + crypto::generate_chacha8_key(&skey, sizeof(skey), key); + const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0]; - // from wallet2::decrypt - string - decrypt(const std::string &ciphertext, - const crypto::secret_key &skey, - bool authenticated) + std::string plaintext; + + plaintext.resize(ciphertext.size() - prefix_size); + + if (authenticated) { + crypto::hash hash; + crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash); + crypto::public_key pkey; + crypto::secret_key_to_public_key(skey, pkey); + + const crypto::signature &signature = + *(const crypto::signature*)&ciphertext[ciphertext.size() + - sizeof(crypto::signature)]; - const size_t prefix_size = sizeof(chacha8_iv) - + (authenticated ? sizeof(crypto::signature) : 0); - if (ciphertext.size() < prefix_size) + if (!crypto::check_signature(hash, pkey, signature)) { - cerr << "Unexpected ciphertext size" << endl; + cerr << "Failed to authenticate criphertext" << endl; return {}; } - crypto::chacha8_key key; - crypto::generate_chacha8_key(&skey, sizeof(skey), key); + } - const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0]; + crypto::chacha8(ciphertext.data() + sizeof(iv), + ciphertext.size() - prefix_size, + key, iv, &plaintext[0]); - std::string plaintext; + return plaintext; +} - plaintext.resize(ciphertext.size() - prefix_size); +// based on +// crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const +public_key +get_tx_pub_key_from_received_outs(const transaction &tx) +{ + std::vector tx_extra_fields; - if (authenticated) - { - crypto::hash hash; - crypto::cn_fast_hash(ciphertext.data(), ciphertext.size() - sizeof(signature), hash); - crypto::public_key pkey; - crypto::secret_key_to_public_key(skey, pkey); + if(!parse_tx_extra(tx.extra, tx_extra_fields)) + { + // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key + } - const crypto::signature &signature = - *(const crypto::signature*)&ciphertext[ciphertext.size() - - sizeof(crypto::signature)]; + // Due to a previous bug, there might be more than one tx pubkey in extra, one being + // the result of a previously discarded signature. + // For speed, since scanning for outputs is a slow process, we check whether extra + // contains more than one pubkey. If not, the first one is returned. If yes, they're + // checked for whether they yield at least one output + tx_extra_pub_key pub_key_field; - if (!crypto::check_signature(hash, pkey, signature)) - { - cerr << "Failed to authenticate criphertext" << endl; - return {}; - } + if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0)) + { + return null_pkey; + } - } + public_key tx_pub_key = pub_key_field.pub_key; - crypto::chacha8(ciphertext.data() + sizeof(iv), - ciphertext.size() - prefix_size, - key, iv, &plaintext[0]); + bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); - return plaintext; + if (!two_found) + { + // easy case, just one found + return tx_pub_key; + } + else + { + // just return second one if there are two. + // this does not require private view key, as + // its not needed for my use case. + return pub_key_field.pub_key; } - // based on - // crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const - public_key - get_tx_pub_key_from_received_outs(const transaction &tx) + return null_pkey; +} + +date::sys_seconds +parse(const std::string& str, string format) +{ + std::istringstream in(str); + date::sys_seconds tp; + in >> date::parse(format, tp); + if (in.fail()) { - std::vector tx_extra_fields; + in.clear(); + in.str(str); + in >> date::parse(format, tp); + } + return tp; +} - if(!parse_tx_extra(tx.extra, tx_extra_fields)) - { - // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key - } +/** +* Check if given output (specified by output_index) +* belongs is ours based +* on our private view key and public spend key +*/ +bool +is_output_ours(const size_t& output_index, + const transaction& tx, + const public_key& pub_tx_key, + const secret_key& private_view_key, + const public_key& public_spend_key) +{ + // public transaction key is combined with our viewkey + // to create, so called, derived key. + key_derivation derivation; - // Due to a previous bug, there might be more than one tx pubkey in extra, one being - // the result of a previously discarded signature. - // For speed, since scanning for outputs is a slow process, we check whether extra - // contains more than one pubkey. If not, the first one is returned. If yes, they're - // checked for whether they yield at least one output - tx_extra_pub_key pub_key_field; + if (!generate_key_derivation(pub_tx_key, private_view_key, derivation)) + { + cerr << "Cant get dervied key for: " << "\n" + << "pub_tx_key: " << pub_tx_key << " and " + << "prv_view_key" << private_view_key << endl; - if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 0)) - { - return null_pkey; - } + return false; + } - public_key tx_pub_key = pub_key_field.pub_key; - bool two_found = find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, 1); + // get the tx output public key + // that normally would be generated for us, + // if someone had sent us some xmr. + public_key pubkey; - if (!two_found) - { - // easy case, just one found - return tx_pub_key; - } - else - { - // just return second one if there are two. - // this does not require private view key, as - // its not needed for my use case. - return pub_key_field.pub_key; - } + derive_public_key(derivation, + output_index, + public_spend_key, + pubkey); + + //cout << "\n" << tx.vout.size() << " " << output_index << endl; + + // get tx output public key + const txout_to_key tx_out_to_key + = boost::get(tx.vout[output_index].target); - return null_pkey; - } - date::sys_seconds - parse(const std::string& str, string format) + if (tx_out_to_key.key == pubkey) { - std::istringstream in(str); - date::sys_seconds tp; - in >> date::parse(format, tp); - if (in.fail()) - { - in.clear(); - in.str(str); - in >> date::parse(format, tp); - } - return tp; + return true; } + return false; +} } diff --git a/src/tools.h b/src/tools.h index bc8f79f..0788d24 100644 --- a/src/tools.h +++ b/src/tools.h @@ -14,7 +14,6 @@ a_hash.substr(1, a_hash.size()-2) #include "monero_headers.h" -#include "tx_details.h" #include "../ext/infix_iterator.h" #include "../ext/date/tz.h" @@ -249,6 +248,13 @@ xmr_amount_to_str(const uint64_t& xmr_amount, string format="{:0.12f}") return fmt::format("{:0.12f}", XMR_AMOUNT(xmr_amount)); } +bool +is_output_ours(const size_t& output_index, + const transaction& tx, + const public_key& pub_tx_key, + const secret_key& private_view_key, + const public_key& public_spend_key); + } #endif //XMREG01_TOOLS_H diff --git a/src/tx_details.cpp b/src/tx_details.cpp deleted file mode 100644 index b839162..0000000 --- a/src/tx_details.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// -// Created by mwo on 14/11/15. -// - -#include "tx_details.h" - - -namespace xmreg -{ - - - - crypto::hash - transfer_details::tx_hash() const - { - return get_transaction_hash(m_tx); - }; - - - uint64_t - transfer_details::amount() const - { - return m_tx.vout[m_internal_output_index].amount; - } - - - ostream& - operator<<(ostream& os, const transfer_details& td) - { - os << "Block: " << td.m_block_height - << " time: " << timestamp_to_str(td.m_block_timestamp) - << " tx hash: " << td.tx_hash() - << " out idx: " << td.m_internal_output_index - << " amount: " << print_money(td.amount()); - - return os; - } - - - - - /** - * Get tx outputs associated with the given private view and public spend keys - * - * - */ - vector - get_belonging_outputs(const block& blk, - const transaction& tx, - const secret_key& private_view_key, - const public_key& public_spend_key, - uint64_t block_height) - { - // vector to be returned - vector our_outputs; - - - // get transaction's public key - public_key pub_tx_key = get_tx_pub_key_from_extra(tx); - - // check if transaction has valid public key - // if no, then skip - if (pub_tx_key == null_pkey) - { - return our_outputs; - } - - - // get the total number of outputs in a transaction. - size_t output_no = tx.vout.size(); - - // check if the given transaction has any outputs - // if no, then finish - if (output_no == 0) - { - return our_outputs; - } - - - // public transaction key is combined with our viewkey - // to create, so called, derived key. - key_derivation derivation; - - if (!generate_key_derivation(pub_tx_key, private_view_key, derivation)) - { - cerr << "Cant get dervied key for: " << "\n" - << "pub_tx_key: " << private_view_key << " and " - << "prv_view_key" << private_view_key << endl; - return our_outputs; - } - - - // each tx that we (or the address we are checking) received - // contains a number of outputs. - // some of them are ours, some not. so we need to go through - // all of them in a given tx block, to check which outputs are ours. - - - - // sum amount of xmr sent to us - // in the given transaction - uint64_t money_transfered {0}; - - // loop through outputs in the given tx - // to check which outputs our ours. we compare outputs' - // public keys with the public key that would had been - // generated for us if we had gotten the outputs. - // not sure this is the case though, but that's my understanding. - for (size_t i = 0; i < output_no; ++i) - { - // get the tx output public key - // that normally would be generated for us, - // if someone had sent us some xmr. - public_key pubkey; - - derive_public_key(derivation, - i, - public_spend_key, - pubkey); - - // get tx output public key - const txout_to_key tx_out_to_key - = boost::get(tx.vout[i].target); - - - //cout << "Output no: " << i << ", " << tx_out_to_key.key; - - // check if the output's public key is ours - if (tx_out_to_key.key == pubkey) - { - // if so, then add this output to the - // returned vector - //our_outputs.push_back(tx.vout[i]); - our_outputs.push_back( - xmreg::transfer_details {block_height, - blk.timestamp, - tx, i, false} - ); - } - } - - return our_outputs; - } - - - - /** - * Check if given output (specified by output_index) - * belongs is ours based - * on our private view key and public spend key - */ - bool - is_output_ours(const size_t& output_index, - const transaction& tx, - const secret_key& private_view_key, - const public_key& public_spend_key) - { - // get transaction's public key - public_key pub_tx_key = get_tx_pub_key_from_extra(tx); - - // check if transaction has valid public key - // if no, then skip - if (pub_tx_key == null_pkey) - { - return false; - } - - // public transaction key is combined with our viewkey - // to create, so called, derived key. - key_derivation derivation; - - if (!generate_key_derivation(pub_tx_key, private_view_key, derivation)) - { - cerr << "Cant get dervied key for: " << "\n" - << "pub_tx_key: " << pub_tx_key << " and " - << "prv_view_key" << private_view_key << endl; - - return false; - } - - - // get the tx output public key - // that normally would be generated for us, - // if someone had sent us some xmr. - public_key pubkey; - - derive_public_key(derivation, - output_index, - public_spend_key, - pubkey); - - //cout << "\n" << tx.vout.size() << " " << output_index << endl; - - // get tx output public key - const txout_to_key tx_out_to_key - = boost::get(tx.vout[output_index].target); - - - if (tx_out_to_key.key == pubkey) - { - return true; - } - - return false; - } - - -} - -template<> -csv::ofstream& -operator<<(csv::ofstream& ostm, const xmreg::transfer_details& td) -{ - - ostm << xmreg::timestamp_to_str(td.m_block_timestamp, "%F"); - ostm << xmreg::timestamp_to_str(td.m_block_timestamp, "%T"); - ostm << td.m_block_height; - ostm << td.tx_hash(); - ostm << td.m_internal_output_index; - ostm << cryptonote::print_money(td.amount()); - - return ostm; -} \ No newline at end of file diff --git a/src/tx_details.h b/src/tx_details.h deleted file mode 100644 index 6e7609d..0000000 --- a/src/tx_details.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// Created by mwo on 14/11/15. -// - -#ifndef XMR2CSV_TXDATA_H -#define XMR2CSV_TXDATA_H - - - -#include "../ext/minicsv.h" - -#include "monero_headers.h" -#include "tools.h" - -namespace xmreg -{ - - using namespace cryptonote; - using namespace crypto; - using namespace std; - - - struct transfer_details - { - uint64_t m_block_height; - uint64_t m_block_timestamp; - transaction m_tx; - size_t m_internal_output_index; - bool m_spent; - - - crypto::hash tx_hash() const; - - uint64_t amount() const; - }; - - - ostream& - operator<<(ostream& os, const transfer_details& dt); - - - vector - get_belonging_outputs(const block& blk, - const transaction& tx, - const secret_key& private_view_key, - const public_key& public_spend_key, - uint64_t block_height = 0); - - bool - is_output_ours(const size_t& output_index, - const transaction& tx, - const secret_key& private_view_key, - const public_key& public_spend_key); - - - -} - -template<> -csv::ostringstream& -operator<<(csv::ostringstream& ostm, const xmreg::transfer_details& td); - - -#endif //XMR2CSV_TXDATA_H