|
|
|
@ -16,11 +16,13 @@
|
|
|
|
|
#include "MicroCore.h"
|
|
|
|
|
#include "tools.h"
|
|
|
|
|
#include "rpccalls.h"
|
|
|
|
|
|
|
|
|
|
#include "../ext/crow/http_request.h"
|
|
|
|
|
|
|
|
|
|
#include "../ext/vpetrigocaches/cache.hpp"
|
|
|
|
|
#include "../ext/vpetrigocaches/lru_cache_policy.hpp"
|
|
|
|
|
#include "../ext/vpetrigocaches/fifo_cache_policy.hpp"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <ctime>
|
|
|
|
@ -74,7 +76,6 @@ struct tx_info_cache
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// indect overload of hash for tx_info_cache::key
|
|
|
|
|
namespace std
|
|
|
|
|
{
|
|
|
|
@ -139,23 +140,13 @@ struct tx_details
|
|
|
|
|
mstch::map
|
|
|
|
|
get_mstch_map() const
|
|
|
|
|
{
|
|
|
|
|
// remove "<" and ">" from the hash string
|
|
|
|
|
string tx_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", hash));
|
|
|
|
|
|
|
|
|
|
string tx_prefix_hash_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", prefix_hash));
|
|
|
|
|
|
|
|
|
|
string tx_pk_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", pk));
|
|
|
|
|
|
|
|
|
|
//cout << "payment_id: " << payment_id << endl;
|
|
|
|
|
|
|
|
|
|
string pid_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", payment_id));
|
|
|
|
|
string pid8_str = REMOVE_HASH_BRAKETS(fmt::format("{:s}", payment_id8));
|
|
|
|
|
|
|
|
|
|
string mixin_str {"N/A"};
|
|
|
|
|
string fee_str {"N/A"};
|
|
|
|
|
string fee_short_str {"N/A"};
|
|
|
|
|
|
|
|
|
|
const double& xmr_amount = XMR_AMOUNT(fee);
|
|
|
|
|
|
|
|
|
|
if (!input_key_imgs.empty())
|
|
|
|
|
{
|
|
|
|
|
mixin_str = std::to_string(mixin_no - 1);
|
|
|
|
@ -163,7 +154,6 @@ struct tx_details
|
|
|
|
|
fee_short_str = fmt::format("{:0.3f}", xmr_amount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const double& tx_size = static_cast<double>(size)/1024.0;
|
|
|
|
|
|
|
|
|
|
mstch::map txd_map {
|
|
|
|
@ -201,7 +191,6 @@ struct tx_details
|
|
|
|
|
string
|
|
|
|
|
get_extra_str() const
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
return epee::string_tools::buff_to_hex_nodelimer(
|
|
|
|
|
string{reinterpret_cast<const char*>(extra.data()), extra.size()});
|
|
|
|
|
}
|
|
|
|
@ -251,6 +240,7 @@ class page
|
|
|
|
|
|
|
|
|
|
atomic<time_t> server_timestamp;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool testnet;
|
|
|
|
|
|
|
|
|
|
bool enable_pusher;
|
|
|
|
@ -263,8 +253,8 @@ class page
|
|
|
|
|
bool enable_block_cache;
|
|
|
|
|
bool show_cache_times;
|
|
|
|
|
|
|
|
|
|
bool enable_autorefresh_option;
|
|
|
|
|
|
|
|
|
|
bool enable_autorefresh_option;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t no_of_mempool_tx_of_frontpage;
|
|
|
|
@ -272,6 +262,7 @@ class page
|
|
|
|
|
|
|
|
|
|
string testnet_url;
|
|
|
|
|
string mainnet_url;
|
|
|
|
|
|
|
|
|
|
// instead of constatnly reading template files
|
|
|
|
|
// from hard drive for each request, we can read
|
|
|
|
|
// them only once, when the explorer starts into this map
|
|
|
|
@ -324,6 +315,8 @@ class page
|
|
|
|
|
fifo_cache_t<uint64_t, vector<pair<crypto::hash, mstch::node>>> block_tx_cache;
|
|
|
|
|
|
|
|
|
|
lru_cache_t<tx_info_cache::key, tx_info_cache> tx_context_cache;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
page(MicroCore* _mcore,
|
|
|
|
@ -366,8 +359,6 @@ public:
|
|
|
|
|
|
|
|
|
|
no_of_mempool_tx_of_frontpage = 25;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// read template files for all the pages
|
|
|
|
|
// into template_file map
|
|
|
|
|
|
|
|
|
@ -389,7 +380,6 @@ public:
|
|
|
|
|
template_file["checkoutputkeys"] = get_full_page(xmreg::read(TMPL_MY_CHECKRAWOUTPUTKEYS));
|
|
|
|
|
template_file["address"] = get_full_page(xmreg::read(TMPL_ADDRESS));
|
|
|
|
|
template_file["search_results"] = get_full_page(xmreg::read(TMPL_SEARCH_RESULTS));
|
|
|
|
|
|
|
|
|
|
template_file["tx_details"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_details.html");
|
|
|
|
|
template_file["tx_table_header"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_table_header.html");
|
|
|
|
|
template_file["tx_table_row"] = xmreg::read(string(TMPL_PARIALS_DIR) + "/tx_table_row.html");
|
|
|
|
@ -408,6 +398,7 @@ public:
|
|
|
|
|
server_timestamp = std::time(nullptr);
|
|
|
|
|
|
|
|
|
|
uint64_t local_copy_server_timestamp = server_timestamp;
|
|
|
|
|
|
|
|
|
|
// number of last blocks to show
|
|
|
|
|
uint64_t no_of_last_blocks {no_blocks_on_index + 1};
|
|
|
|
|
|
|
|
|
@ -435,6 +426,7 @@ public:
|
|
|
|
|
{"enable_autorefresh_option", enable_autorefresh_option},
|
|
|
|
|
{"show_cache_times" , show_cache_times}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
context.emplace("txs", mstch::array()); // will keep tx to show
|
|
|
|
|
|
|
|
|
|
// get reference to txs mstch map to be field below
|
|
|
|
@ -458,6 +450,7 @@ public:
|
|
|
|
|
|
|
|
|
|
// loop index
|
|
|
|
|
int64_t i = end_height;
|
|
|
|
|
|
|
|
|
|
// iterate over last no_of_last_blocks of blocks
|
|
|
|
|
while (i >= start_height)
|
|
|
|
|
{
|
|
|
|
@ -686,6 +679,7 @@ public:
|
|
|
|
|
|
|
|
|
|
context["blk_size_median"] = fmt::format("{:0.2f}", blk_size_median);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// save computational times for disply in the frontend
|
|
|
|
|
|
|
|
|
|
context["construction_time_cached"] = fmt::format(
|
|
|
|
@ -710,10 +704,8 @@ public:
|
|
|
|
|
|
|
|
|
|
// render the page
|
|
|
|
|
return mstch::render(template_file["index2"], context);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Render mempool data
|
|
|
|
|
*/
|
|
|
|
@ -813,6 +805,7 @@ public:
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// get the above incormation from json of that tx
|
|
|
|
|
|
|
|
|
|
json j_tx;
|
|
|
|
|
|
|
|
|
|
if (enable_mempool_cache && mempool_tx_json_cache.Contains(_tx_info.id_hash))
|
|
|
|
@ -903,6 +896,7 @@ public:
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} // else if (mempool_tx_json_cache.Contains(_tx_info.id_hash))
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (std::invalid_argument& e)
|
|
|
|
|
{
|
|
|
|
@ -921,11 +915,8 @@ public:
|
|
|
|
|
{"no_inputs" , no_inputs},
|
|
|
|
|
{"no_outputs" , no_outputs},
|
|
|
|
|
{"no_nonrct_inputs", num_nonrct_inputs},
|
|
|
|
|
{"is_ringct" , is_ringct_str},
|
|
|
|
|
{"rct_type" , rct_type_str},
|
|
|
|
|
{"mixin" , fmt::format("{:d}", mixin_no)},
|
|
|
|
|
{"txsize" , fmt::format("{:0.2f}",
|
|
|
|
|
static_cast<double>(_tx_info.blob_size)/1024.0)}
|
|
|
|
|
{"mixin" , mixin_no},
|
|
|
|
|
{"txsize" , txsize}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
@ -1402,10 +1393,8 @@ public:
|
|
|
|
|
{"tx_details", template_file["tx_details"]},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add_css_style(context);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// render the page
|
|
|
|
|
return mstch::render(template_file["tx"], context, partials);
|
|
|
|
|
}
|
|
|
|
@ -2139,11 +2128,13 @@ public:
|
|
|
|
|
{"error_msg" , string {}},
|
|
|
|
|
{"data_prefix" , data_prefix},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
context.emplace("txs", mstch::array{});
|
|
|
|
|
|
|
|
|
|
string full_page = template_file["checkrawtx"];
|
|
|
|
|
|
|
|
|
|
add_css_style(context);
|
|
|
|
|
|
|
|
|
|
if (unsigned_tx_given)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
@ -2481,7 +2472,6 @@ public:
|
|
|
|
|
{"tx_details", template_file["tx_details"]},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add_css_style(context);
|
|
|
|
|
|
|
|
|
|
// render the page
|
|
|
|
@ -2731,7 +2721,6 @@ public:
|
|
|
|
|
{"tx_details", template_file["tx_details"]},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// render the page
|
|
|
|
|
return mstch::render(full_page, context, partials);
|
|
|
|
|
}
|
|
|
|
@ -3057,7 +3046,6 @@ public:
|
|
|
|
|
context.insert({"key_imgs" , mstch::array{}});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t no_key_images = (decoded_raw_data.size() - header_lenght) / record_lenght;
|
|
|
|
|
|
|
|
|
|
//vector<pair<crypto::key_image, crypto::signature>> signed_key_images;
|
|
|
|
@ -3082,7 +3070,6 @@ public:
|
|
|
|
|
// so this will be empty always for now.
|
|
|
|
|
vector<string> found_tx_hashes;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mstch::map key_img_info {
|
|
|
|
|
{"key_no" , fmt::format("{:03d}", n)},
|
|
|
|
|
{"key_image" , REMOVE_HASH_BRAKETS(fmt::format("{:s}", key_image))},
|
|
|
|
@ -3541,6 +3528,7 @@ public:
|
|
|
|
|
return result_html;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// first check if searching for block of given height
|
|
|
|
|
if (search_text.size() < 12)
|
|
|
|
|
{
|
|
|
|
@ -3577,6 +3565,7 @@ public:
|
|
|
|
|
|
|
|
|
|
result_html = default_txt;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check if monero address is given based on its length
|
|
|
|
|
// if yes, then we can only show its public components
|
|
|
|
|
if (search_str_length == 95)
|
|
|
|
@ -3629,11 +3618,8 @@ public:
|
|
|
|
|
// all_possible_tx_hashes was field using custom lmdb database
|
|
|
|
|
// it was dropped, so all_possible_tx_hashes will be alwasy empty
|
|
|
|
|
// for now
|
|
|
|
|
|
|
|
|
|
vector<pair<string, vector<string>>> all_possible_tx_hashes;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result_html = show_search_results(search_text, all_possible_tx_hashes);
|
|
|
|
|
|
|
|
|
|
return result_html;
|
|
|
|
@ -3762,7 +3748,6 @@ public:
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
show_search_results(const string& search_text,
|
|
|
|
|
const vector<pair<string, vector<string>>>& all_possible_tx_hashes)
|
|
|
|
@ -4641,6 +4626,7 @@ private:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
mark_real_mixins_on_timescales(
|
|
|
|
|
const vector<uint64_t>& real_output_indices,
|
|
|
|
@ -4681,7 +4667,6 @@ private:
|
|
|
|
|
mstch::map
|
|
|
|
|
construct_tx_context(transaction tx, uint16_t with_ring_signatures = 0)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
tx_details txd = get_tx_details(tx);
|
|
|
|
|
|
|
|
|
|
const crypto::hash& tx_hash = txd.hash;
|
|
|
|
@ -4693,6 +4678,7 @@ private:
|
|
|
|
|
bool tx_blk_found {false};
|
|
|
|
|
|
|
|
|
|
bool detailed_view {enable_mixins_details || static_cast<bool>(with_ring_signatures)};
|
|
|
|
|
|
|
|
|
|
if (core_storage->have_tx(tx_hash))
|
|
|
|
|
{
|
|
|
|
|
// currently get_tx_block_height seems to return a block hight
|
|
|
|
@ -4798,15 +4784,19 @@ private:
|
|
|
|
|
{
|
|
|
|
|
show_part_of_inputs = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<vector<uint64_t>> mixin_timestamp_groups;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// make timescale maps for mixins in input
|
|
|
|
|
for (const txin_to_key &in_key: txd.input_key_imgs)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (show_part_of_inputs && (input_idx > max_no_of_inputs_to_show))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get absolute offsets of mixins
|
|
|
|
|
std::vector<uint64_t> absolute_offsets
|
|
|
|
|
= cryptonote::relative_output_offsets_to_absolute(
|
|
|
|
@ -4855,6 +4845,8 @@ private:
|
|
|
|
|
boost::get<mstch::map>(inputs.back())["ring_sigs"]
|
|
|
|
|
= txd.get_ring_sig_for_input(input_idx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inputs_xmr_sum += in_key.amount;
|
|
|
|
|
|
|
|
|
|
if (in_key.amount == 0)
|
|
|
|
@ -4973,6 +4965,7 @@ private:
|
|
|
|
|
mixin_timestamp_groups.push_back(mixin_timestamps);
|
|
|
|
|
|
|
|
|
|
input_idx++;
|
|
|
|
|
|
|
|
|
|
} // for (const txin_to_key& in_key: txd.input_key_imgs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -5148,13 +5141,13 @@ private:
|
|
|
|
|
// get tx hash
|
|
|
|
|
txd.hash = get_transaction_hash(tx);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get tx public key from extra
|
|
|
|
|
// this check if there are two public keys
|
|
|
|
|
// due to previous bug with sining txs:
|
|
|
|
|
// https://github.com/monero-project/monero/pull/1358/commits/7abfc5474c0f86e16c405f154570310468b635c2
|
|
|
|
|
txd.pk = xmreg::get_tx_pub_key_from_received_outs(tx);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// sum xmr in inputs and ouputs in the given tx
|
|
|
|
|
const array<uint64_t, 4>& sum_data = summary_of_in_out_rct(
|
|
|
|
|
tx, txd.output_pub_keys, txd.input_key_imgs);
|
|
|
|
@ -5166,11 +5159,6 @@ private:
|
|
|
|
|
|
|
|
|
|
txd.fee = 0;
|
|
|
|
|
|
|
|
|
|
transaction tx_copy = tx;
|
|
|
|
|
|
|
|
|
|
txd.json_representation = obj_to_json_str(tx_copy);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!coinbase && tx.vin.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
// check if not miner tx
|
|
|
|
@ -5184,8 +5172,6 @@ private:
|
|
|
|
|
|
|
|
|
|
get_payment_id(tx, txd.payment_id, txd.payment_id8);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get tx size in bytes
|
|
|
|
|
txd.size = get_object_blobsize(tx);
|
|
|
|
|
|
|
|
|
@ -5207,6 +5193,7 @@ private:
|
|
|
|
|
// if blk_height is zero then search for tx block in
|
|
|
|
|
// the blockchain. but since often block height is know a priory
|
|
|
|
|
// this is not needed
|
|
|
|
|
|
|
|
|
|
txd.blk_height = core_storage->get_db().get_tx_block_height(txd.hash);
|
|
|
|
|
|
|
|
|
|
// get the current blockchain height. Just to check
|
|
|
|
|