|
|
@ -31,9 +31,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
#include "../ext/json.hpp"
|
|
|
|
#include "../ext/json.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
#include "../ext/vpetrigocaches/cache.hpp"
|
|
|
|
|
|
|
|
#include "../ext/vpetrigocaches/lru_cache_policy.hpp"
|
|
|
|
|
|
|
|
#include "../ext/vpetrigocaches/fifo_cache_policy.hpp"
|
|
|
|
|
|
|
|
#include "../ext/mstch/src/visitor/render_node.hpp"
|
|
|
|
#include "../ext/mstch/src/visitor/render_node.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
extern "C" uint64_t me_rx_seedheight(const uint64_t height);
|
|
|
|
extern "C" uint64_t me_rx_seedheight(const uint64_t height);
|
|
|
@ -85,31 +82,6 @@ extern __thread randomx_vm *rx_vm;
|
|
|
|
MAKE_ONIONEXPLORER_RPC_VERSION(ONIONEXPLORER_RPC_VERSION_MAJOR, ONIONEXPLORER_RPC_VERSION_MINOR)
|
|
|
|
MAKE_ONIONEXPLORER_RPC_VERSION(ONIONEXPLORER_RPC_VERSION_MAJOR, ONIONEXPLORER_RPC_VERSION_MINOR)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// basic info about tx to be stored in cashe.
|
|
|
|
|
|
|
|
// we need to store block_no and timestamp,
|
|
|
|
|
|
|
|
// as this time and number of confirmation needs
|
|
|
|
|
|
|
|
// to be updated between requests. Just cant
|
|
|
|
|
|
|
|
// get it from cash, as it will be old very soon
|
|
|
|
|
|
|
|
struct tx_info_cache
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
uint64_t block_no;
|
|
|
|
|
|
|
|
uint64_t timestamp;
|
|
|
|
|
|
|
|
mstch::map tx_map;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// custom key for use in cache.
|
|
|
|
|
|
|
|
// cache uses unordeded map for keys
|
|
|
|
|
|
|
|
struct key
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
crypto::hash tx_hash;
|
|
|
|
|
|
|
|
bool detailed;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool operator==(const key &other) const
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return (tx_hash == other.tx_hash && detailed == other.detailed);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// helper to ignore any number of template parametrs
|
|
|
|
// helper to ignore any number of template parametrs
|
|
|
|
template<typename...> using VoidT = void;
|
|
|
|
template<typename...> using VoidT = void;
|
|
|
@ -147,22 +119,6 @@ struct OutputIndicesReturnVectOfVectT<
|
|
|
|
>>: std::true_type
|
|
|
|
>>: std::true_type
|
|
|
|
{};
|
|
|
|
{};
|
|
|
|
|
|
|
|
|
|
|
|
// indect overload of hash for tx_info_cache::key
|
|
|
|
|
|
|
|
namespace std
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
|
|
struct hash<tx_info_cache::key>
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
size_t operator()(const tx_info_cache::key& k) const
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
size_t const h1 ( std::hash<crypto::hash>{}(k.tx_hash) );
|
|
|
|
|
|
|
|
size_t const h2 ( std::hash<bool>{}(k.detailed) );
|
|
|
|
|
|
|
|
return h1 ^ (h2 << 1);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* visitor to produce json representations of
|
|
|
|
* visitor to produce json representations of
|
|
|
@ -521,15 +477,10 @@ bool enable_pusher;
|
|
|
|
bool enable_key_image_checker;
|
|
|
|
bool enable_key_image_checker;
|
|
|
|
bool enable_output_key_checker;
|
|
|
|
bool enable_output_key_checker;
|
|
|
|
bool enable_mixins_details;
|
|
|
|
bool enable_mixins_details;
|
|
|
|
bool enable_tx_cache;
|
|
|
|
|
|
|
|
bool enable_block_cache;
|
|
|
|
|
|
|
|
bool enable_as_hex;
|
|
|
|
bool enable_as_hex;
|
|
|
|
bool show_cache_times;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool enable_autorefresh_option;
|
|
|
|
bool enable_autorefresh_option;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t no_of_mempool_tx_of_frontpage;
|
|
|
|
uint64_t no_of_mempool_tx_of_frontpage;
|
|
|
|
uint64_t no_blocks_on_index;
|
|
|
|
uint64_t no_blocks_on_index;
|
|
|
|
uint64_t mempool_info_timeout;
|
|
|
|
uint64_t mempool_info_timeout;
|
|
|
@ -548,24 +499,6 @@ string js_html_files_all_in_one;
|
|
|
|
// read operation in OS
|
|
|
|
// read operation in OS
|
|
|
|
map<string, string> template_file;
|
|
|
|
map<string, string> template_file;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// alias for easy class typing
|
|
|
|
|
|
|
|
template <typename Key, typename Value>
|
|
|
|
|
|
|
|
using lru_cache_t = caches::fixed_sized_cache<Key, Value, caches::LRUCachePolicy<Key>>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// alias for easy class typing
|
|
|
|
|
|
|
|
template <typename Key, typename Value>
|
|
|
|
|
|
|
|
using fifo_cache_t = caches::fixed_sized_cache<Key, Value, caches::FIFOCachePolicy<Key>>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// cache of txs_map of txs in blocks. this is useful for
|
|
|
|
|
|
|
|
// index2 page, so that we dont parse txs in each block
|
|
|
|
|
|
|
|
// for each request.
|
|
|
|
|
|
|
|
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:
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
|
|
page(MicroCore* _mcore,
|
|
|
|
page(MicroCore* _mcore,
|
|
|
@ -578,9 +511,6 @@ page(MicroCore* _mcore,
|
|
|
|
bool _enable_output_key_checker,
|
|
|
|
bool _enable_output_key_checker,
|
|
|
|
bool _enable_autorefresh_option,
|
|
|
|
bool _enable_autorefresh_option,
|
|
|
|
bool _enable_mixins_details,
|
|
|
|
bool _enable_mixins_details,
|
|
|
|
bool _enable_tx_cache,
|
|
|
|
|
|
|
|
bool _enable_block_cache,
|
|
|
|
|
|
|
|
bool _show_cache_times,
|
|
|
|
|
|
|
|
uint64_t _no_blocks_on_index,
|
|
|
|
uint64_t _no_blocks_on_index,
|
|
|
|
uint64_t _mempool_info_timeout,
|
|
|
|
uint64_t _mempool_info_timeout,
|
|
|
|
string _testnet_url,
|
|
|
|
string _testnet_url,
|
|
|
@ -597,16 +527,11 @@ page(MicroCore* _mcore,
|
|
|
|
enable_output_key_checker {_enable_output_key_checker},
|
|
|
|
enable_output_key_checker {_enable_output_key_checker},
|
|
|
|
enable_autorefresh_option {_enable_autorefresh_option},
|
|
|
|
enable_autorefresh_option {_enable_autorefresh_option},
|
|
|
|
enable_mixins_details {_enable_mixins_details},
|
|
|
|
enable_mixins_details {_enable_mixins_details},
|
|
|
|
enable_tx_cache {_enable_tx_cache},
|
|
|
|
|
|
|
|
enable_block_cache {_enable_block_cache},
|
|
|
|
|
|
|
|
show_cache_times {_show_cache_times},
|
|
|
|
|
|
|
|
no_blocks_on_index {_no_blocks_on_index},
|
|
|
|
no_blocks_on_index {_no_blocks_on_index},
|
|
|
|
mempool_info_timeout {_mempool_info_timeout},
|
|
|
|
mempool_info_timeout {_mempool_info_timeout},
|
|
|
|
testnet_url {_testnet_url},
|
|
|
|
testnet_url {_testnet_url},
|
|
|
|
stagenet_url {_stagenet_url},
|
|
|
|
stagenet_url {_stagenet_url},
|
|
|
|
mainnet_url {_mainnet_url},
|
|
|
|
mainnet_url {_mainnet_url}
|
|
|
|
block_tx_cache(200),
|
|
|
|
|
|
|
|
tx_context_cache(1000)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
mainnet = nettype == cryptonote::network_type::MAINNET;
|
|
|
|
mainnet = nettype == cryptonote::network_type::MAINNET;
|
|
|
|
testnet = nettype == cryptonote::network_type::TESTNET;
|
|
|
|
testnet = nettype == cryptonote::network_type::TESTNET;
|
|
|
@ -705,8 +630,7 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
{"enable_pusher" , enable_pusher},
|
|
|
|
{"enable_pusher" , enable_pusher},
|
|
|
|
{"enable_key_image_checker" , enable_key_image_checker},
|
|
|
|
{"enable_key_image_checker" , enable_key_image_checker},
|
|
|
|
{"enable_output_key_checker", enable_output_key_checker},
|
|
|
|
{"enable_output_key_checker", enable_output_key_checker},
|
|
|
|
{"enable_autorefresh_option", enable_autorefresh_option},
|
|
|
|
{"enable_autorefresh_option", enable_autorefresh_option}
|
|
|
|
{"show_cache_times" , show_cache_times}
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
context.emplace("txs", mstch::array()); // will keep tx to show
|
|
|
|
context.emplace("txs", mstch::array()); // will keep tx to show
|
|
|
@ -724,12 +648,6 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
|
|
|
|
|
|
|
|
vector<double> blk_sizes;
|
|
|
|
vector<double> blk_sizes;
|
|
|
|
|
|
|
|
|
|
|
|
// measure time of cache based execution, and non-cached execution
|
|
|
|
|
|
|
|
double duration_cached {0.0};
|
|
|
|
|
|
|
|
double duration_non_cached {0.0};
|
|
|
|
|
|
|
|
uint64_t cache_hits {0};
|
|
|
|
|
|
|
|
uint64_t cache_misses {0};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loop index
|
|
|
|
// loop index
|
|
|
|
int64_t i = end_height;
|
|
|
|
int64_t i = end_height;
|
|
|
|
|
|
|
|
|
|
|
@ -764,119 +682,6 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
|
|
|
|
|
|
|
|
context["age_format"] = age.second;
|
|
|
|
context["age_format"] = age.second;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (enable_block_cache && block_tx_cache.Contains(i))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// get txs info in the ith block from
|
|
|
|
|
|
|
|
// our cache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// start measure time here
|
|
|
|
|
|
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const vector<pair<crypto::hash, mstch::node>>& txd_pairs
|
|
|
|
|
|
|
|
= block_tx_cache.Get(i);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// copy tx maps from txs_maps_tmp into txs array,
|
|
|
|
|
|
|
|
// that will go to templates
|
|
|
|
|
|
|
|
for (const pair<crypto::hash, mstch::node>& txd_pair: txd_pairs)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// we need to check if the given transaction is still
|
|
|
|
|
|
|
|
// in the same block as when it was cached. it is possible
|
|
|
|
|
|
|
|
// the block got orphaned, and this tx is in mempool
|
|
|
|
|
|
|
|
// or different block, and what we have in cache
|
|
|
|
|
|
|
|
// is thus wrong
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// but we do this only for first top blocks. no sense
|
|
|
|
|
|
|
|
// doing it for all blocks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool is_tx_still_in_block_as_expected {true};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (i + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > height)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const crypto::hash& tx_hash = txd_pair.first;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (core_storage->have_tx(tx_hash))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
uint64_t tx_height_in_blockchain =
|
|
|
|
|
|
|
|
core_storage->get_db().get_tx_block_height(tx_hash);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check if height of the given tx that we have in cache,
|
|
|
|
|
|
|
|
// denoted by i, is same as what is acctually stored
|
|
|
|
|
|
|
|
// in blockchain
|
|
|
|
|
|
|
|
if (tx_height_in_blockchain == i)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
is_tx_still_in_block_as_expected = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// if no tx in the given block, just stop
|
|
|
|
|
|
|
|
// any futher search. no need. we are going
|
|
|
|
|
|
|
|
// to ditch the cache, in a monent
|
|
|
|
|
|
|
|
is_tx_still_in_block_as_expected = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
catch (const TX_DNE& e)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cerr << "Tx from cache" << pod_to_hex(tx_hash)
|
|
|
|
|
|
|
|
<< " is no longer in the blockchain "
|
|
|
|
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
is_tx_still_in_block_as_expected = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
is_tx_still_in_block_as_expected = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // if (i + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > height)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!is_tx_still_in_block_as_expected)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// if some tx in cache is not in blockchain
|
|
|
|
|
|
|
|
// where it should be, its probably better to
|
|
|
|
|
|
|
|
// ditch entire cache, as redo it below.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
block_tx_cache.Clear();
|
|
|
|
|
|
|
|
txs.clear();
|
|
|
|
|
|
|
|
i = end_height;
|
|
|
|
|
|
|
|
continue; // reado the main loop
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if we got to here, it means that everything went fine
|
|
|
|
|
|
|
|
// and no unexpeced things happended.
|
|
|
|
|
|
|
|
mstch::map txd_map = boost::get<mstch::map>(txd_pair.second);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// now we need to update age of txs from cashe
|
|
|
|
|
|
|
|
if (!boost::get<string>(txd_map["age"]).empty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
txd_map["age"] = age.first;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
txs.push_back(txd_map);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // for (const pair<crypto::hash, mstch::map>& txd_pair: txd_pairs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
|
|
|
|
|
|
|
(std::chrono::steady_clock::now() - start);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// cout << "block_tx_json_cache from cache" << endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
duration_cached += duration.count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
++cache_hits;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// this is new block. not in cashe.
|
|
|
|
|
|
|
|
// need to process its txs and add to cache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// start measure time here
|
|
|
|
// start measure time here
|
|
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
|
|
|
@ -895,7 +700,6 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t tx_i {0};
|
|
|
|
uint64_t tx_i {0};
|
|
|
|
|
|
|
|
|
|
|
|
// this vector will go into block_tx cache
|
|
|
|
|
|
|
|
// tx_hash , txd_map
|
|
|
|
// tx_hash , txd_map
|
|
|
|
vector<pair<crypto::hash, mstch::node>> txd_pairs;
|
|
|
|
vector<pair<crypto::hash, mstch::node>> txd_pairs;
|
|
|
|
|
|
|
|
|
|
|
@ -940,18 +744,6 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
|
|
|
(std::chrono::steady_clock::now() - start);
|
|
|
|
(std::chrono::steady_clock::now() - start);
|
|
|
|
|
|
|
|
|
|
|
|
duration_non_cached += duration.count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
++cache_misses;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (enable_block_cache)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// save in block_tx cache
|
|
|
|
|
|
|
|
block_tx_cache.Put(i, txd_pairs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // else if (block_tx_json_cache.Contains(i))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--i; // go to next block number
|
|
|
|
--i; // go to next block number
|
|
|
|
|
|
|
|
|
|
|
|
} // while (i <= end_height)
|
|
|
|
} // while (i <= end_height)
|
|
|
@ -959,30 +751,20 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
// calculate median size of the blocks shown
|
|
|
|
// calculate median size of the blocks shown
|
|
|
|
//double blk_size_median = xmreg::calc_median(blk_sizes.begin(), blk_sizes.end());
|
|
|
|
//double blk_size_median = xmreg::calc_median(blk_sizes.begin(), blk_sizes.end());
|
|
|
|
|
|
|
|
|
|
|
|
// save computational times for disply in the frontend
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
context["construction_time_cached"] = fmt::format(
|
|
|
|
|
|
|
|
"{:0.4f}", duration_cached/1.0e6);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
context["construction_time_non_cached"] = fmt::format(
|
|
|
|
|
|
|
|
"{:0.4f}", duration_non_cached/1.0e6);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
context["construction_time_total"] = fmt::format(
|
|
|
|
|
|
|
|
"{:0.4f}", (duration_non_cached+duration_cached)/1.0e6);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
context["cache_hits"] = cache_hits;
|
|
|
|
|
|
|
|
context["cache_misses"] = cache_misses;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get current network info from MemoryStatus thread.
|
|
|
|
// get current network info from MemoryStatus thread.
|
|
|
|
MempoolStatus::network_info current_network_info
|
|
|
|
MempoolStatus::network_info current_network_info
|
|
|
|
= MempoolStatus::current_network_info;
|
|
|
|
= MempoolStatus::current_network_info;
|
|
|
|
|
|
|
|
|
|
|
|
// perapre network info mstch::map for the front page
|
|
|
|
// perapre network info mstch::map for the front page
|
|
|
|
string hash_rate;
|
|
|
|
string hash_rate;
|
|
|
|
|
|
|
|
|
|
|
|
double hr_d;
|
|
|
|
double hr_d;
|
|
|
|
char metric_prefix;
|
|
|
|
char metric_prefix;
|
|
|
|
cryptonote::difficulty_type hr = make_difficulty(current_network_info.hash_rate, current_network_info.hash_rate_top64);
|
|
|
|
|
|
|
|
|
|
|
|
cryptonote::difficulty_type hr = make_difficulty(
|
|
|
|
|
|
|
|
current_network_info.hash_rate,
|
|
|
|
|
|
|
|
current_network_info.hash_rate_top64);
|
|
|
|
|
|
|
|
|
|
|
|
get_metric_prefix(hr, hr_d, metric_prefix);
|
|
|
|
get_metric_prefix(hr, hr_d, metric_prefix);
|
|
|
|
|
|
|
|
|
|
|
|
if (metric_prefix != 0)
|
|
|
|
if (metric_prefix != 0)
|
|
|
@ -1001,7 +783,7 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
context["network_info"] = mstch::map {
|
|
|
|
context["network_info"] = mstch::map {
|
|
|
|
{"difficulty" , make_difficulty(current_network_info.difficulty, current_network_info.difficulty_top64).str()},
|
|
|
|
{"difficulty" , current_network_info.difficulty},
|
|
|
|
{"hash_rate" , hash_rate},
|
|
|
|
{"hash_rate" , hash_rate},
|
|
|
|
{"fee_per_kb" , print_money(current_network_info.fee_per_kb)},
|
|
|
|
{"fee_per_kb" , print_money(current_network_info.fee_per_kb)},
|
|
|
|
{"alt_blocks_no" , current_network_info.alt_blocks_count},
|
|
|
|
{"alt_blocks_no" , current_network_info.alt_blocks_count},
|
|
|
@ -1101,7 +883,6 @@ mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25)
|
|
|
|
// initalise page tempate map with basic info about mempool
|
|
|
|
// initalise page tempate map with basic info about mempool
|
|
|
|
mstch::map context {
|
|
|
|
mstch::map context {
|
|
|
|
{"mempool_size" , static_cast<uint64_t>(total_no_of_mempool_tx)}, // total no of mempool txs
|
|
|
|
{"mempool_size" , static_cast<uint64_t>(total_no_of_mempool_tx)}, // total no of mempool txs
|
|
|
|
{"show_cache_times" , show_cache_times},
|
|
|
|
|
|
|
|
{"mempool_refresh_time" , MempoolStatus::mempool_refresh_time}
|
|
|
|
{"mempool_refresh_time" , MempoolStatus::mempool_refresh_time}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -1110,11 +891,6 @@ mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25)
|
|
|
|
// get reference to blocks template map to be field below
|
|
|
|
// get reference to blocks template map to be field below
|
|
|
|
mstch::array& txs = boost::get<mstch::array>(context["mempooltxs"]);
|
|
|
|
mstch::array& txs = boost::get<mstch::array>(context["mempooltxs"]);
|
|
|
|
|
|
|
|
|
|
|
|
double duration_cached {0.0};
|
|
|
|
|
|
|
|
double duration_non_cached {0.0};
|
|
|
|
|
|
|
|
uint64_t cache_hits {0};
|
|
|
|
|
|
|
|
uint64_t cache_misses {0};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t local_copy_server_timestamp = server_timestamp;
|
|
|
|
uint64_t local_copy_server_timestamp = server_timestamp;
|
|
|
|
|
|
|
|
|
|
|
|
// for each transaction in the memory pool
|
|
|
|
// for each transaction in the memory pool
|
|
|
@ -1145,7 +921,6 @@ mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25)
|
|
|
|
delta_time[3], delta_time[4]);
|
|
|
|
delta_time[3], delta_time[4]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// cout << "block_tx_json_cache from cache" << endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set output page template map
|
|
|
|
// set output page template map
|
|
|
|
txs.push_back(mstch::map {
|
|
|
|
txs.push_back(mstch::map {
|
|
|
@ -1583,150 +1358,9 @@ show_tx(string tx_hash_str, uint16_t with_ring_signatures = 0, bool refresh_page
|
|
|
|
|
|
|
|
|
|
|
|
mstch::map tx_context;
|
|
|
|
mstch::map tx_context;
|
|
|
|
|
|
|
|
|
|
|
|
if (enable_tx_cache && tx_context_cache.Contains({tx_hash, static_cast<bool>(with_ring_signatures)}))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// with_ring_signatures == 0 means that cache is not used
|
|
|
|
|
|
|
|
// when obtaining detailed information about tx is requested.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// we are going to measure time for the construction of the
|
|
|
|
|
|
|
|
// tx context from cashe. just for fun, to see if cache is any faster.
|
|
|
|
|
|
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const tx_info_cache& tx_info_cashed
|
|
|
|
|
|
|
|
= tx_context_cache.Get({tx_hash, static_cast<bool>(with_ring_signatures)});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context = tx_info_cashed.tx_map;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//cout << "get tx from cash: " << tx_hash_str <<endl;
|
|
|
|
|
|
|
|
//cout << " - tx_blk_height: " << boost::get<uint64_t>(tx_context["tx_blk_height"]) <<endl;
|
|
|
|
|
|
|
|
//cout << " - blk_timestamp_uint: " << boost::get<uint64_t>(tx_context["blk_timestamp_uint"]) <<endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// now have to update age and confirmation numbers of the tx.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t tx_blk_height = boost::get<uint64_t>(tx_context["tx_blk_height"]);
|
|
|
|
|
|
|
|
uint64_t blk_timestamp_uint = boost::get<uint64_t>(tx_context["blk_timestamp_uint"]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (tx_blk_height > 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// seems to be in blockchain. off course it could have been orphaned
|
|
|
|
|
|
|
|
// so double check if its for sure in blockchain
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (core_storage->have_tx(tx_hash))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// ok, it is still in blockchain
|
|
|
|
|
|
|
|
// update its age and number of confirmations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pair<string, string> age
|
|
|
|
|
|
|
|
= get_age(std::time(nullptr),
|
|
|
|
|
|
|
|
blk_timestamp_uint,
|
|
|
|
|
|
|
|
FULL_AGE_FORMAT);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["delta_time"] = age.first;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t bc_height = core_storage->get_current_blockchain_height();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["confirmations"] = bc_height - (tx_blk_height - 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// marke it as from cashe. useful if we want to show
|
|
|
|
|
|
|
|
// info about cashed/not cashed in frontend.
|
|
|
|
|
|
|
|
tx_context["from_cache"] = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
|
|
|
|
|
|
|
(std::chrono::steady_clock::now() - start);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["construction_time"] = fmt::format(
|
|
|
|
|
|
|
|
"{:0.4f}", static_cast<double>(duration.count())/1.0e6);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// normally we should update this into in the cache.
|
|
|
|
|
|
|
|
// but since we make this check all the time,
|
|
|
|
|
|
|
|
// we can skip updating cashed version
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // if (core_storage->have_tx(tx_hash))
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// its not in blockchain, but it was there when we cashed it.
|
|
|
|
|
|
|
|
// so we update it in cash, as it should be back in mempool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
|
|
|
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
|
|
|
|
|
|
|
|
|
|
|
tx_context_cache.Put(
|
|
|
|
|
|
|
|
{tx_hash, static_cast<bool>(with_ring_signatures)},
|
|
|
|
|
|
|
|
tx_info_cache {
|
|
|
|
|
|
|
|
boost::get<uint64_t>(tx_context["tx_blk_height"]),
|
|
|
|
|
|
|
|
boost::get<uint64_t>(tx_context["blk_timestamp_uint"]),
|
|
|
|
|
|
|
|
tx_context}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} // if (tx_blk_height > 0)
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// the tx was cashed when in mempool.
|
|
|
|
|
|
|
|
// since then, it might have been included in some block.
|
|
|
|
|
|
|
|
// so we check it.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (core_storage->have_tx(tx_hash))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// checking if in blockchain already
|
|
|
|
|
|
|
|
// it was before in mempool, but now maybe already in blockchain
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context_cache.Put(
|
|
|
|
|
|
|
|
{tx_hash, static_cast<bool>(with_ring_signatures)},
|
|
|
|
|
|
|
|
tx_info_cache {
|
|
|
|
|
|
|
|
boost::get<uint64_t>(tx_context["tx_blk_height"]),
|
|
|
|
|
|
|
|
boost::get<uint64_t>(tx_context["blk_timestamp_uint"]),
|
|
|
|
|
|
|
|
tx_context});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // if (core_storage->have_tx(tx_hash))
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// still seems to be in mempool only.
|
|
|
|
|
|
|
|
// so just get its time duration, as its read only
|
|
|
|
|
|
|
|
// from cache
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["from_cache"] = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
|
|
|
|
|
|
|
(std::chrono::steady_clock::now() - start);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["construction_time"] = fmt::format(
|
|
|
|
|
|
|
|
"{:0.4f}", static_cast<double>(duration.count())/1.0e6);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // else if (tx_blk_height > 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // if (tx_context_cache.Contains(tx_hash))
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// we are going to measure time for the construction of the
|
|
|
|
|
|
|
|
// tx context. just for fun, to see if cache is any faster.
|
|
|
|
|
|
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
|
|
|
|
|
|
|
(std::chrono::steady_clock::now() - start);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (enable_tx_cache)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
tx_context_cache.Put(
|
|
|
|
|
|
|
|
{tx_hash, static_cast<bool>(with_ring_signatures)},
|
|
|
|
|
|
|
|
tx_info_cache {
|
|
|
|
|
|
|
|
boost::get<uint64_t>(tx_context["tx_blk_height"]),
|
|
|
|
|
|
|
|
boost::get<uint64_t>(tx_context["blk_timestamp_uint"]),
|
|
|
|
|
|
|
|
tx_context});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["construction_time"] = fmt::format(
|
|
|
|
|
|
|
|
"{:0.4f}", static_cast<double>(duration.count())/1.0e6);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // else if (tx_context_cache.Contains(tx_hash))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tx_context["show_more_details_link"] = show_more_details_link;
|
|
|
|
tx_context["show_more_details_link"] = show_more_details_link;
|
|
|
|
|
|
|
|
|
|
|
|
if (boost::get<bool>(tx_context["has_error"]))
|
|
|
|
if (boost::get<bool>(tx_context["has_error"]))
|
|
|
@ -1737,7 +1371,6 @@ show_tx(string tx_hash_str, uint16_t with_ring_signatures = 0, bool refresh_page
|
|
|
|
mstch::map context {
|
|
|
|
mstch::map context {
|
|
|
|
{"testnet" , this->testnet},
|
|
|
|
{"testnet" , this->testnet},
|
|
|
|
{"stagenet" , this->stagenet},
|
|
|
|
{"stagenet" , this->stagenet},
|
|
|
|
{"show_cache_times" , show_cache_times},
|
|
|
|
|
|
|
|
{"txs" , mstch::array{}},
|
|
|
|
{"txs" , mstch::array{}},
|
|
|
|
{"refresh" , refresh_page},
|
|
|
|
{"refresh" , refresh_page},
|
|
|
|
{"tx_hash" , tx_hash_str}
|
|
|
|
{"tx_hash" , tx_hash_str}
|
|
|
@ -4878,11 +4511,6 @@ json_transaction(string tx_hash_str)
|
|
|
|
no_confirmations = txd.no_confirmations;
|
|
|
|
no_confirmations = txd.no_confirmations;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// get tx from tx fetched. can be use to double check
|
|
|
|
|
|
|
|
// if what we return in the json response agrees with
|
|
|
|
|
|
|
|
// what tx_hash was requested
|
|
|
|
|
|
|
|
string tx_hash_str_again = pod_to_hex(get_transaction_hash(tx));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get basic tx info
|
|
|
|
// get basic tx info
|
|
|
|
j_data = get_tx_json(tx, txd);
|
|
|
|
j_data = get_tx_json(tx, txd);
|
|
|
|
|
|
|
|
|
|
|
@ -6447,7 +6075,6 @@ construct_tx_context(transaction tx, uint16_t with_ring_signatures = 0)
|
|
|
|
{"error_msg" , string("")},
|
|
|
|
{"error_msg" , string("")},
|
|
|
|
{"have_raw_tx" , false},
|
|
|
|
{"have_raw_tx" , false},
|
|
|
|
{"show_more_details_link", true},
|
|
|
|
{"show_more_details_link", true},
|
|
|
|
{"from_cache" , false},
|
|
|
|
|
|
|
|
{"construction_time" , string {}},
|
|
|
|
{"construction_time" , string {}},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -6863,7 +6490,15 @@ get_tx_details(const transaction& tx,
|
|
|
|
tx_details txd;
|
|
|
|
tx_details txd;
|
|
|
|
|
|
|
|
|
|
|
|
// get tx hash
|
|
|
|
// get tx hash
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!tx.pruned)
|
|
|
|
|
|
|
|
{
|
|
|
|
txd.hash = get_transaction_hash(tx);
|
|
|
|
txd.hash = get_transaction_hash(tx);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
txd.hash = get_pruned_transaction_hash(tx, tx.prunable_hash);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// get tx public key from extra
|
|
|
|
// get tx public key from extra
|
|
|
|
// this check if there are two public keys
|
|
|
|
// this check if there are two public keys
|
|
|
|