diff --git a/CMakeLists.txt b/CMakeLists.txt index 8060e7c..c693c9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,7 @@ set(LIBRARIES unbound curl crypto + atomic ssl) if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32) diff --git a/README.md b/README.md index 748ad42..19c7ba7 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,10 @@ Clearnet versions: - [https://xmrchain.net/](https://xmrchain.net/) - https enabled, most popular and very stable. - [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled. - [http://explore.MoneroWorld.com](http://explore.moneroworld.com) - same as the second one. - - [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled. + - [https://explorer.xmr.my/](https://explorer.xmr.my/) - nice looking one, https enabled. - [https://explorer.monero-otc.com/](https://explorer.monero-otc.com/) - https enabled. - [http://monerochain.com/](http://monerochain.com/) - JSON API based, multiple nodes. + - [http://66.85.74.134:8081/](http://66.85.74.134:8081/) - fluffynet subnet explorer. Clearnet testnet Monero version: @@ -47,6 +48,11 @@ i2p users (main Monero network) - down for now: - [http://monerotools.i2p](http://monerotools.i2p) +Alternative block explorers: + +- [http://moneroblocks.info](http://moneroblocks.info/) +- [https://monerobase.com](https://monerobase.com/) +- [http://chainradar.com](http://chainradar.com/xmr/blocks) ## Onion Monero Blockchain Explorer features @@ -66,7 +72,8 @@ The key features of the Onion Monero Blockchain Explorer are: - the only explorer showing number of amount output indices, - the only explorer supporting Monero testnet network, - the only explorer providing tx checker and pusher for online pushing of transactions, - - the only explorer able to estimate possible spendings based on address and viewkey. + - the only explorer able to estimate possible spendings based on address and viewkey, + - the only explorer that can provide total amount of all miner fees. ## Compilation on Ubuntu 16.04 @@ -101,6 +108,7 @@ as follows: ```bash # go to home folder if still in ~/monero cd ~ + # download the source code git clone https://github.com/moneroexamples/onion-monero-blockchain-explorer.git @@ -113,6 +121,9 @@ mkdir build && cd build # create the makefile cmake .. +# altearnatively can use: cmake -DMONERO_DIR=/path/to/monero_folder .. +# if monero is not in ~/monero + # compile make ``` @@ -143,10 +154,10 @@ Go to your browser: http://127.0.0.1:8081 ``` ./xmrblocks -h -xmrblocks, start Onion Monero Blockchain Explorer: +xmrblocks, Onion Monero Blockchain Explorer: -h [ --help ] [=arg(=1)] (=0) produce help message -t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain - --enable-pusher [=arg(=1)] (=0) enable pushing signed tx + --enable-pusher [=arg(=1)] (=0) enable signed transaction pusher --enable-mixin-details [=arg(=1)] (=0) enable mixin details for key images, e.g., timescale, mixin of mixins, in tx @@ -155,45 +166,93 @@ xmrblocks, start Onion Monero Blockchain Explorer: enable key images file checker --enable-output-key-checker [=arg(=1)] (=0) enable outputs key file checker - --enable-mempool-cache arg (=1) enable caching txs in the mempool + --enable-mempool-cache arg (=1) enable caching of transactions from the + mempool --enable-json-api arg (=1) enable JSON REST api - --enable-tx-cache [=arg(=1)] (=0) enable caching of tx details + --enable-tx-cache [=arg(=1)] (=0) enable caching of transaction details --show-cache-times [=arg(=1)] (=0) show times of getting data from cache vs no cache --enable-block-cache [=arg(=1)] (=0) enable caching of block details --enable-autorefresh-option [=arg(=1)] (=0) enable users to have the index page on autorefresh - -p [ --port ] arg (=8081) default port - --testnet-url arg you can specifiy testnet url, if you - run it on mainet. link will show on - front page to testnet explorer - --mainnet-url arg you can specifiy mainnet url, if you - run it on testnet. link will show on - front page to mainnet explorer + --enable-emission-monitor [=arg(=1)] (=0) + enable Monero total emission monitoring + thread + -p [ --port ] arg (=8081) default explorer port + --testnet-url arg you can specify testnet url, if you run + it on mainnet. link will show on front + page to testnet explorer + --mainnet-url arg you can specify mainnet url, if you run + it on testnet. link will show on front + page to mainnet explorer --no-blocks-on-index arg (=10) number of last blocks to be shown on index page - -b [ --bc-path ] arg path to lmdb blockchain - --ssl-crt-file arg A path to crt file for ssl (https) + --network-info-timeout arg (=1000) maximum time, in milliseconds, to wait + for network info availability + --mempool-info-timeout arg (=5000) maximum time, in milliseconds, to wait + for mempool data for the front page + -b [ --bc-path ] arg path to lmdb folder of the blockchain, + e.g., ~/.bitmonero/lmdb + --ssl-crt-file arg path to crt file for ssl (https) functionality - --ssl-key-file arg A path to key file for ssl (https) + --ssl-key-file arg path to key file for ssl (https) functionality -d [ --deamon-url ] arg (=http:://127.0.0.1:18081) - monero address string + Monero deamon url ``` Example usage, defined as bash aliases. ```bash # for mainnet explorer -alias xmrblocksmainnet='~/onion-monero-blockchain-explorer/build/xmrblocks --port 8081 --no-blocks-on-index 49 --testnet-url "http://139.162.32.245:8082" --enable-block-cache=1 --enable-tx-cache=1 --enable-mempool-cache=1 --show-cache-times=1 --enable-pusher' +alias xmrblocksmainnet='~/onion-monero-blockchain-explorer/build/xmrblocks --port 8081 --no-blocks-on-index 24 --testnet-url "http://139.162.32.245:8082" --enable-pusher --enable-emission-monitor' # for testnet explorer -alias xmrblockstestnet='~/onion-monero-blockchain-explorer/build/xmrblocks -t --port 8082 --no-blocks-on-index 24 --mainnet-url "http://139.162.32.245:8081" --enable-block-cache=1 --enable-tx-cache=1 --enable-mempool-cache=1 --show-cache-times=1 --enable-pusher' +alias xmrblockstestnet='~/onion-monero-blockchain-explorer/build/xmrblocks -t --port 8082 --no-blocks-on-index 24 --mainnet-url "http://139.162.32.245:8081" --enable-pusher --enable-emission-monitor' +``` + +These are aliases similar to those used for http://139.162.32.245:8081/ and http://139.162.32.245:8082/, respectively. + +## Enable Monero emission + +Obtaining current Monero emission amount is not straight forward. Thus, by default it is +disabled. To enable it use `--enable-emission-monitor` flag, e.g., + + +```bash +xmrblocks --enable-emission-monitor ``` -These are explorer commands used for http://139.162.32.245:8081/ and http://139.162.32.245:8082/, respectively. +This flag will enable emission monitoring thread. When started, the thread + will initially scan the entire blockchain, and calculate the cumulative emission based on each block. +Since it is a separate thread, the explorer will work as usual during this time. +Every 10000 blocks, the thread will save current emission in a file, by default, + in `~/.bitmonero/lmdb/emission_amount.txt`. For testnet network, + it is `~/.bitmonero/testnet/lmdb/emission_amount.txt`. This file is used so that we don't + need to rescan entire blockchain whenever the explorer is restarted. When the + explorer restarts, the thread will first check if `~/.bitmonero/lmdb/emission_amount.txt` + is present, read its values, and continue from there if possible. Subsequently, only the initial + use of the tread is time consuming. Once the thread scans the entire blockchain, it updates + the emission amount using new blocks as they come. Since the explorer writes this file, there can + be only one instance of it running for mainnet and testnet. Thus, for example, you cant have + two explorers for mainnet + running at the same time, as they will be trying to write and read the same file at the same time, + leading to unexpected results. Off course having one instance for mainnet and one instance for testnet + is fine, as they write to different files. + + When the emission monitor is enabled, information about current emission of coinbase and fees is + displayed on the front page, e.g., : + +``` +Monero emission (fees) is 14485540.430 (52545.373) as of 1313448 block +``` +The values given, can be checked using Monero daemon's `print_coinbase_tx_sum` command. +For example, for the above example: `print_coinbase_tx_sum 0 1313449`. + +To disable the monitor, simply restart the explorer without `--enable-emission-monitor` flag. + ## Enable SSL (https) By default, the explorer does not use ssl. But it has such a functionality. @@ -564,27 +623,47 @@ curl -w "\n" -X GET "http://139.162.32.245:8081/api/networkinfo" "data": { "alt_blocks_count": 0, "block_size_limit": 600000, - "cumulative_difficulty": 2067724366624367, - "difficulty": 7530486740, - "grey_peerlist_size": 4987, - "hash_rate": 62754056, - "height": 1307537, + "cumulative_difficulty": 2091549555696348, + "difficulty": 7941560081, + "fee_per_kb": 303970000, + "grey_peerlist_size": 4991, + "hash_rate": 66179667, + "height": 1310423, "incoming_connections_count": 0, - "outgoing_connections_count": 8, - "start_time": 1494473774, + "outgoing_connections_count": 5, + "start_time": 1494822692, "status": "OK", "target": 120, - "target_height": 1307518, + "target_height": 0, "testnet": false, - "top_block_hash": "0726de5b86f431547fc64fc2c8e1c11d76843ada0561993ee540e4eee29d83c3", - "tx_count": 1210222, - "tx_pool_size": 5, + "top_block_hash": "76f9e85d62415312758bc09e0b9b48fd2b005231ad1eee435a8081e551203f82", + "tx_count": 1219048, + "tx_pool_size": 2, "white_peerlist_size": 1000 }, "status": "success" } ``` +#### api/emission + +```bash +curl -w "\n" -X GET "http://139.162.32.245:8081/api/emission" +``` + +```json +{ + "data": { + "blk_no": 1313969, + "coinbase": 14489473877253413000, + "fee": 52601974988641130 + }, + "status": "success" +} +``` + +Emission only works when the emission monitoring thread is enabled. + #### api/rawblock/ diff --git a/cmake/MyUtils.cmake b/cmake/MyUtils.cmake index 616dd88..d69fa1f 100644 --- a/cmake/MyUtils.cmake +++ b/cmake/MyUtils.cmake @@ -42,6 +42,16 @@ macro(create_git_version) OUTPUT_STRIP_TRAILING_WHITESPACE ) + # Get current branch name + execute_process( + COMMAND git rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_BRANCH_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + + configure_file( ${CMAKE_SOURCE_DIR}/src/version.h.in ${CMAKE_BINARY_DIR}/gen/version.h diff --git a/main.cpp b/main.cpp index 8c1827d..6119525 100644 --- a/main.cpp +++ b/main.cpp @@ -17,7 +17,8 @@ namespace myxmr { struct jsonresponse: crow::response { - jsonresponse(const nlohmann::json& _body) : crow::response {_body.dump()} + jsonresponse(const nlohmann::json& _body) + : crow::response {_body.dump()} { add_header("Access-Control-Allow-Origin", "*"); add_header("Access-Control-Allow-Headers", "Content-Type"); @@ -26,10 +27,9 @@ struct jsonresponse: crow::response }; } - - -int main(int ac, const char* av[]) { - +int +main(int ac, const char* av[]) +{ // get command line options xmreg::CmdLineOptions opts {ac, av}; @@ -49,6 +49,8 @@ int main(int ac, const char* av[]) { auto no_blocks_on_index_opt = opts.get_option("no-blocks-on-index"); auto testnet_url = opts.get_option("testnet-url"); auto mainnet_url = opts.get_option("mainnet-url"); + auto network_info_timeout_opt = opts.get_option("network-info-timeout"); + auto mempool_info_timeout_opt = opts.get_option("mempool-info-timeout"); auto testnet_opt = opts.get_option("testnet"); auto enable_key_image_checker_opt = opts.get_option("enable-key-image-checker"); auto enable_output_key_checker_opt = opts.get_option("enable-output-key-checker"); @@ -60,6 +62,9 @@ int main(int ac, const char* av[]) { auto enable_tx_cache_opt = opts.get_option("enable-tx-cache"); auto enable_block_cache_opt = opts.get_option("enable-block-cache"); auto show_cache_times_opt = opts.get_option("show-cache-times"); + auto enable_emission_monitor_opt = opts.get_option("enable-emission-monitor"); + + bool testnet {*testnet_opt}; bool enable_pusher {*enable_pusher_opt}; @@ -71,6 +76,7 @@ int main(int ac, const char* av[]) { bool enable_json_api {*enable_json_api_opt}; bool enable_tx_cache {*enable_tx_cache_opt}; bool enable_block_cache {*enable_block_cache_opt}; + bool enable_emission_monitor {*enable_emission_monitor_opt}; bool show_cache_times {*show_cache_times_opt}; @@ -128,6 +134,7 @@ int main(int ac, const char* av[]) { cout << blockchain_path << endl; + // create instance of our MicroCore // and make pointer to the Blockchain xmreg::MicroCore mcore; @@ -144,7 +151,58 @@ int main(int ac, const char* av[]) { string deamon_url {*deamon_url_opt}; if (testnet && deamon_url == "http:://127.0.0.1:18081") + { deamon_url = "http:://127.0.0.1:28081"; + } + + uint64_t network_info_timeout {1000}; + uint64_t mempool_info_timeout {5000}; + try + { + network_info_timeout = boost::lexical_cast(*network_info_timeout_opt); + mempool_info_timeout = boost::lexical_cast(*mempool_info_timeout_opt); + + } + catch (boost::bad_lexical_cast &e) + { + cout << "Cant cast " << (*network_info_timeout_opt) + << " or/and " << (*mempool_info_timeout_opt) <<" into numbers. Using default values." + << endl; + + } + + + + if (enable_emission_monitor == true) + { + // This starts new thread, which aim is + // to calculate, store and monitor + // current total Monero emission amount. + + // This thread stores the current emission + // which it has caluclated in + // /emission_amount.txt file, + // e.g., ~/.bitmonero/lmdb/emission_amount.txt. + // So instead of calcualting the emission + // from scrach whenever the explorer is started, + // the thread is initalized with the values + // found in emission_amount.txt file. + + xmreg::CurrentBlockchainStatus::blockchain_path + = blockchain_path; + xmreg::CurrentBlockchainStatus::testnet + = testnet; + xmreg::CurrentBlockchainStatus::deamon_url + = deamon_url; + xmreg::CurrentBlockchainStatus::set_blockchain_variables( + &mcore, core_storage); + + // launch the status monitoring thread so that it keeps track of blockchain + // info, e.g., current height. Information from this thread is used + // by tx searching threads that are launched for each user independently, + // when they log back or create new account. + xmreg::CurrentBlockchainStatus::start_monitor_blockchain_thread(); + } // create instance of page class which // contains logic for the website @@ -162,6 +220,8 @@ int main(int ac, const char* av[]) { enable_block_cache, show_cache_times, no_blocks_on_index, + network_info_timeout, + mempool_info_timeout, *testnet_url, *mainnet_url); @@ -434,6 +494,14 @@ int main(int ac, const char* av[]) { return r; }); + CROW_ROUTE(app, "/api/emission") + ([&](const crow::request &req) { + + myxmr::jsonresponse r{xmrblocks.json_emission()}; + + return r; + }); + CROW_ROUTE(app, "/api/outputs").methods("GET"_method) ([&](const crow::request &req) { @@ -490,5 +558,16 @@ int main(int ac, const char* av[]) { } + if (enable_emission_monitor == true) + { + // finish Emission monitoring thread in a cotrolled manner. + xmreg::CurrentBlockchainStatus::m_thread.interrupt(); + xmreg::CurrentBlockchainStatus::m_thread.join(); + + cout << "Emission monitoring thread joined." << endl; + } + + cout << "The explorer is terminating." << endl; + return EXIT_SUCCESS; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a15dc01..d4f476c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,7 @@ set(SOURCE_HEADERS MicroCore.h tools.h monero_headers.h - ) + CurrentBlockchainStatus.h) set(SOURCE_FILES MicroCore.cpp @@ -14,7 +14,7 @@ set(SOURCE_FILES CmdLineOptions.cpp page.h rpccalls.cpp rpccalls.h - version.h.in) + version.h.in CurrentBlockchainStatus.cpp) # make static library called libmyxrm # that we are going to link to diff --git a/src/CmdLineOptions.cpp b/src/CmdLineOptions.cpp index 2a2830a..db82f64 100644 --- a/src/CmdLineOptions.cpp +++ b/src/CmdLineOptions.cpp @@ -18,7 +18,7 @@ namespace xmreg p.add("txhash", -1); options_description desc( - "xmrblocks, start Onion Monero Blockchain Explorer"); + "xmrblocks, Onion Monero Blockchain Explorer"); desc.add_options() ("help,h", value()->default_value(false)->implicit_value(true), @@ -26,7 +26,7 @@ namespace xmreg ("testnet,t", value()->default_value(false)->implicit_value(true), "use testnet blockchain") ("enable-pusher", value()->default_value(false)->implicit_value(true), - "enable pushing signed tx") + "enable signed transaction pusher") ("enable-mixin-details", value()->default_value(false)->implicit_value(true), "enable mixin details for key images, e.g., timescale, mixin of mixins, in tx context") ("enable-key-image-checker", value()->default_value(false)->implicit_value(true), @@ -34,33 +34,39 @@ namespace xmreg ("enable-output-key-checker", value()->default_value(false)->implicit_value(true), "enable outputs key file checker") ("enable-mempool-cache", value()->default_value(true), - "enable caching txs in the mempool") + "enable caching of transactions from the mempool") ("enable-json-api", value()->default_value(true), "enable JSON REST api") ("enable-tx-cache", value()->default_value(false)->implicit_value(true), - "enable caching of tx details") + "enable caching of transaction details") ("show-cache-times", value()->default_value(false)->implicit_value(true), "show times of getting data from cache vs no cache") ("enable-block-cache", value()->default_value(false)->implicit_value(true), "enable caching of block details") ("enable-autorefresh-option", value()->default_value(false)->implicit_value(true), "enable users to have the index page on autorefresh") + ("enable-emission-monitor", value()->default_value(false)->implicit_value(true), + "enable Monero total emission monitoring thread") ("port,p", value()->default_value("8081"), - "default port") + "default explorer port") ("testnet-url", value()->default_value(""), - "you can specifiy testnet url, if you run it on mainet. link will show on front page to testnet explorer") + "you can specify testnet url, if you run it on mainnet. link will show on front page to testnet explorer") ("mainnet-url", value()->default_value(""), - "you can specifiy mainnet url, if you run it on testnet. link will show on front page to mainnet explorer") + "you can specify mainnet url, if you run it on testnet. link will show on front page to mainnet explorer") ("no-blocks-on-index", value()->default_value("10"), "number of last blocks to be shown on index page") + ("network-info-timeout", value()->default_value("1000"), + "maximum time, in milliseconds, to wait for network info availability") + ("mempool-info-timeout", value()->default_value("5000"), + "maximum time, in milliseconds, to wait for mempool data for the front page") ("bc-path,b", value(), - "path to lmdb blockchain") + "path to lmdb folder of the blockchain, e.g., ~/.bitmonero/lmdb") ("ssl-crt-file", value(), - "A path to crt file for ssl (https) functionality") + "path to crt file for ssl (https) functionality") ("ssl-key-file", value(), - "A path to key file for ssl (https) functionality") + "path to key file for ssl (https) functionality") ("deamon-url,d", value()->default_value("http:://127.0.0.1:18081"), - "monero address string"); + "Monero deamon url"); store(command_line_parser(acc, avv) diff --git a/src/CurrentBlockchainStatus.cpp b/src/CurrentBlockchainStatus.cpp new file mode 100644 index 0000000..8750700 --- /dev/null +++ b/src/CurrentBlockchainStatus.cpp @@ -0,0 +1,322 @@ +// +// Created by mwo on 16/05/17. +// + +#include "CurrentBlockchainStatus.h" + +namespace xmreg +{ + +using namespace std; + + + +void +CurrentBlockchainStatus::set_blockchain_variables(MicroCore* _mcore, + Blockchain* _core_storage) +{ + mcore = _mcore; + core_storage =_core_storage; +} + + +void +CurrentBlockchainStatus::start_monitor_blockchain_thread() +{ + total_emission_atomic = Emission {0, 0, 0}; + + string emmision_saved_file = get_output_file_path().string(); + + // read stored emission data if possible + if (boost::filesystem::exists(emmision_saved_file)) + { + if (!load_current_emission_amount()) + { + cerr << "Emission file cant be read, got corrupted or has incorrect format:\n " << emmision_saved_file + << "\nEmission monitoring thread is not started.\nDelete the file and" + << " restart the explorer or disable emission monitoring." + << endl; + + cerr << "Press ENTER to continue without emission monitoring or Ctr+C to exit" << endl; + + cin.get(); + + return; + } + } + + if (!is_running) + { + m_thread = boost::thread{[]() + { + try + { + while (true) + { + Emission current_emission = total_emission_atomic; + + current_height = core_storage->get_current_blockchain_height(); + + // scan 10000 blocks for emissiom or if we are at the top of + // the blockchain, only few top blocks + update_current_emission_amount(); + + cout << "current emission: " << string(current_emission) << endl; + + save_current_emission_amount(); + + if (current_emission.blk_no < current_height - blockchain_chunk_size) + { + // while we scan the blockchain from scrach, every 10000 + // blocks take 1 second break + boost::this_thread::sleep_for(boost::chrono::seconds(1)); + } + else + { + // when we reach top of the blockchain, update + // the emission amount every minute. + boost::this_thread::sleep_for(boost::chrono::seconds(60)); + } + + } // while (true) + } + catch (boost::thread_interrupted&) + { + cout << "Emission monitoring thread interrupted." << endl; + return; + } + + }}; // m_thread = boost::thread{[]() + + is_running = true; + + } // if (!is_running) +} + + +void +CurrentBlockchainStatus::update_current_emission_amount() +{ + + Emission current_emission = total_emission_atomic; + + uint64_t blk_no = current_emission.blk_no; + + uint64_t end_block = blk_no + blockchain_chunk_size; + + uint64_t current_blockchain_height = current_height; + + // blockchain_chunk_gap is used so that we + // never read and store few top blocks + // the emission in the top few blocks will be calcalted + // later + end_block = end_block > current_blockchain_height + ? current_blockchain_height - blockchain_chunk_gap + : end_block; + + Emission emission_calculated = calculate_emission_in_blocks(blk_no, end_block); + + current_emission.coinbase += emission_calculated.coinbase; + current_emission.fee += emission_calculated.fee; + current_emission.blk_no = emission_calculated.blk_no; + + total_emission_atomic = current_emission; +} + +CurrentBlockchainStatus::Emission +CurrentBlockchainStatus::calculate_emission_in_blocks( + uint64_t start_blk, uint64_t end_blk) +{ + Emission emission_calculated {0, 0, 0}; + + while (start_blk < end_blk) + { + block blk; + + mcore->get_block_by_height(start_blk, blk); + + uint64_t coinbase_amount = get_outs_money_amount(blk.miner_tx); + + std::list txs; + std::list missed_txs; + + uint64_t tx_fee_amount = 0; + + core_storage->get_transactions(blk.tx_hashes, txs, missed_txs); + + for(const auto& tx: txs) + { + tx_fee_amount += get_tx_fee(tx); + } + + (void) missed_txs; + + emission_calculated.coinbase += coinbase_amount - tx_fee_amount; + emission_calculated.fee += tx_fee_amount; + + ++start_blk; + } + + emission_calculated.blk_no = start_blk; + + return emission_calculated; +} + + +bool +CurrentBlockchainStatus::save_current_emission_amount() +{ + + string emmision_saved_file = get_output_file_path().string(); + + ofstream out(emmision_saved_file); + + if( !out ) + { + cerr << "Couldn't open file." << endl; + return false; + } + + Emission current_emission = total_emission_atomic; + + out << string(current_emission) << flush; + + return true; +} + + +bool +CurrentBlockchainStatus::load_current_emission_amount() +{ + string emmision_saved_file = get_output_file_path().string(); + + string last_saved_emmision = xmreg::read(emmision_saved_file); + + if (last_saved_emmision.empty()) + { + cerr << "Couldn't open file." << endl; + return false; + } + + last_saved_emmision.erase(last_saved_emmision.find_last_not_of(" \n\r\t")+1); + + vector strs; + boost::split(strs, last_saved_emmision, boost::is_any_of(",")); + + if (strs.empty()) + { + cerr << "Problem spliting string values form emission_amount." << endl; + return false; + } + + Emission emission_loaded {0, 0, 0}; + + uint64_t read_check_sum {0}; + + try + { + emission_loaded.blk_no = boost::lexical_cast(strs.at(0)); + emission_loaded.coinbase = boost::lexical_cast(strs.at(1)); + emission_loaded.fee = boost::lexical_cast(strs.at(2)); + read_check_sum = boost::lexical_cast(strs.at(3)); + } + catch (boost::bad_lexical_cast &e) + { + cerr << "Cant parse to number date from string: " << last_saved_emmision << endl; + return false; + } + + if (read_check_sum != emission_loaded.checksum()) + { + cerr << "read_check_sum != check_sum: " + << read_check_sum << " != " << emission_loaded.checksum() + << endl; + + return false; + } + + total_emission_atomic = emission_loaded; + + return true; + +} + +bf::path +CurrentBlockchainStatus::get_output_file_path() +{ + return blockchain_path / output_file; +} + + +CurrentBlockchainStatus::Emission +CurrentBlockchainStatus::get_emission() +{ + // get current emission + Emission current_emission = total_emission_atomic; + + // this emission will be few blocks behind current blockchain + // height. By default 3 blocks. So we need to calcualate here + // the emission from the top missing blocks, to have complete + // emission data. + + uint64_t current_blockchain_height = current_height; + + uint64_t start_blk = current_emission.blk_no; + + // this should be at current hight or above + // as we calculate missing blocks only for top blockchain + // height + uint64_t end_block = start_blk + blockchain_chunk_gap; + + if (end_block >= current_blockchain_height + && start_blk < current_blockchain_height) + { + // make sure we are not over the blockchain height + end_block = end_block > current_blockchain_height + ? current_blockchain_height : end_block; + + // calculated emission for missing blocks + Emission gap_emission_calculated + = calculate_emission_in_blocks(start_blk, end_block); + + //cout << "gap_emission_calculated: " << std::string(gap_emission_calculated) << endl; + + current_emission.coinbase += gap_emission_calculated.coinbase; + current_emission.fee += gap_emission_calculated.fee; + current_emission.blk_no = gap_emission_calculated.blk_no > 0 + ? gap_emission_calculated.blk_no + : current_emission.blk_no; + } + + return current_emission; +} + +bool +CurrentBlockchainStatus::is_thread_running() +{ + return is_running; +} + +bf::path CurrentBlockchainStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"}; + +bool CurrentBlockchainStatus::testnet {false}; + +string CurrentBlockchainStatus::output_file {"emission_amount.txt"}; + +string CurrentBlockchainStatus::deamon_url {"http:://127.0.0.1:18081"}; + +uint64_t CurrentBlockchainStatus::blockchain_chunk_size {10000}; + +uint64_t CurrentBlockchainStatus::blockchain_chunk_gap {3}; + +atomic CurrentBlockchainStatus::current_height {0}; + +atomic CurrentBlockchainStatus::total_emission_atomic; + +boost::thread CurrentBlockchainStatus::m_thread; + +atomic CurrentBlockchainStatus::is_running {false}; + +Blockchain* CurrentBlockchainStatus::core_storage {nullptr}; +xmreg::MicroCore* CurrentBlockchainStatus::mcore {nullptr}; +} diff --git a/src/CurrentBlockchainStatus.h b/src/CurrentBlockchainStatus.h new file mode 100644 index 0000000..d213894 --- /dev/null +++ b/src/CurrentBlockchainStatus.h @@ -0,0 +1,114 @@ +// +// Created by mwo on 16/05/17. +// + +#ifndef XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H +#define XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H + +#include "MicroCore.h" + +#include + +#include +#include +#include +#include +#include + +namespace xmreg +{ + +using namespace std; + +namespace bf = boost::filesystem; + +struct CurrentBlockchainStatus +{ + + struct Emission + { + uint64_t coinbase; + uint64_t fee; + uint64_t blk_no; + + inline uint64_t + checksum() const + { + return coinbase + fee + blk_no; + } + + operator + std::string() const + { + return to_string(blk_no) + "," + to_string(coinbase) + + "," + to_string(fee) + "," + to_string(checksum()); + } + }; + + static bf::path blockchain_path; + + static bool testnet; + + static string output_file; + + static string deamon_url; + + // how many blocks to read before thread goes to sleep + static uint64_t blockchain_chunk_size; + + // gap from what we store total_emission_atomic and + // current blockchain height. We dont want to store + // what is on, e.g., top block, as this can get messy + // if the block gets orphaned or blockchain reorganization + // occurs. So the top 3 blocks (default number) will always + // be calculated in flight and added to what we have so far. + static uint64_t blockchain_chunk_gap; + + // current blockchain height and + // hash of top block + static atomic current_height; + + + static atomic total_emission_atomic; + + + static boost::thread m_thread; + + static atomic is_running; + + // make object for accessing the blockchain here + static MicroCore* mcore; + static Blockchain* core_storage; + + static void + start_monitor_blockchain_thread(); + + static void + set_blockchain_variables(MicroCore* _mcore, + Blockchain* _core_storage); + + static void + update_current_emission_amount(); + + static Emission + calculate_emission_in_blocks(uint64_t start_blk, uint64_t end_blk); + + static bool + save_current_emission_amount(); + + static bool + load_current_emission_amount(); + + static Emission + get_emission(); + + static bf::path + get_output_file_path(); + + static bool + is_thread_running(); +}; + +} + +#endif //XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H diff --git a/src/MicroCore.cpp b/src/MicroCore.cpp index a75f24a..8205419 100644 --- a/src/MicroCore.cpp +++ b/src/MicroCore.cpp @@ -327,6 +327,7 @@ MicroCore::get_blk_timestamp(uint64_t blk_height) */ MicroCore::~MicroCore() { + //m_blockchain_storage.get_db().close(); delete &m_blockchain_storage.get_db(); } diff --git a/src/monero_headers.h b/src/monero_headers.h index c373cc2..d43573e 100644 --- a/src/monero_headers.h +++ b/src/monero_headers.h @@ -14,6 +14,8 @@ #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" +#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks + #include "release/version/version.h" #include "net/http_client.h" diff --git a/src/page.h b/src/page.h index 9ae021a..0ff364d 100644 --- a/src/page.h +++ b/src/page.h @@ -17,6 +17,8 @@ #include "tools.h" #include "rpccalls.h" +#include "CurrentBlockchainStatus.h" + #include "../ext/crow/http_request.h" #include "../ext/vpetrigocaches/cache.hpp" @@ -33,6 +35,7 @@ #define TMPL_INDEX TMPL_DIR "/index.html" #define TMPL_INDEX2 TMPL_DIR "/index2.html" #define TMPL_MEMPOOL TMPL_DIR "/mempool.html" +#define TMPL_MEMPOOL_ERROR TMPL_DIR "/mempool_error.html" #define TMPL_HEADER TMPL_DIR "/header.html" #define TMPL_FOOTER TMPL_DIR "/footer.html" #define TMPL_BLOCK TMPL_DIR "/block.html" @@ -149,7 +152,7 @@ namespace xmreg if (!input_key_imgs.empty()) { - mixin_str = std::to_string(mixin_no - 1); + mixin_str = std::to_string(mixin_no); fee_str = fmt::format("{:0.6f}", xmr_amount); fee_short_str = fmt::format("{:0.3f}", xmr_amount); } @@ -259,6 +262,8 @@ namespace xmreg uint64_t no_of_mempool_tx_of_frontpage; uint64_t no_blocks_on_index; + uint64_t network_info_timeout; + uint64_t mempool_info_timeout; string testnet_url; string mainnet_url; @@ -309,6 +314,21 @@ namespace xmreg // parse their json for each request fifo_cache_t mempool_tx_json_cache; + // to keep network_info in cache + // and to show previous info in case current querry for + // the current info timesout. + struct network_info + { + uint64_t difficulty; + uint64_t hash_rate; + uint64_t fee_per_kb; + uint64_t alt_blocks_no; + uint64_t tx_pool_size; + uint64_t info_timestamp; + }; + + atomic previous_network_info; + // 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. @@ -333,6 +353,8 @@ namespace xmreg bool _enable_block_cache, bool _show_cache_times, uint64_t _no_blocks_on_index, + uint64_t _network_info_timeout, + uint64_t _mempool_info_timeout, string _testnet_url, string _mainnet_url) : mcore {_mcore}, @@ -350,6 +372,8 @@ namespace xmreg enable_block_cache {_enable_block_cache}, show_cache_times {_show_cache_times}, no_blocks_on_index {_no_blocks_on_index}, + network_info_timeout {_network_info_timeout}, + mempool_info_timeout {_mempool_info_timeout}, testnet_url {_testnet_url}, mainnet_url {_mainnet_url}, mempool_tx_json_cache(1000), @@ -359,6 +383,9 @@ namespace xmreg no_of_mempool_tx_of_frontpage = 25; + // initialized stored network info atomic + previous_network_info = network_info {0, 0, 0, 0, 0, 0}; + // read template files for all the pages // into template_file map @@ -367,6 +394,7 @@ namespace xmreg template_file["footer"] = get_footer(); template_file["index2"] = get_full_page(xmreg::read(TMPL_INDEX2)); template_file["mempool"] = xmreg::read(TMPL_MEMPOOL); + template_file["mempool_error"] = xmreg::read(TMPL_MEMPOOL_ERROR); template_file["mempool_full"] = get_full_page(template_file["mempool"]); template_file["block"] = get_full_page(xmreg::read(TMPL_BLOCK)); template_file["tx"] = get_full_page(xmreg::read(TMPL_TX)); @@ -394,6 +422,41 @@ namespace xmreg string index2(uint64_t page_no = 0, bool refresh_page = false) { + + // we get network info, such as current hash rate + // but since this makes a rpc call to deamon, we make it as an async + // call. this way we dont have to wait with execution of the rest of the + // index2 method, until deamon gives as the required result. + std::future network_info_ftr = std::async(std::launch::async, [&] + { + json j_info; + + if (!get_monero_network_info(j_info)) + { + return json{}; + } + + uint64_t fee_estimated {0}; + + // get dynamic fee estimate from last 10 blocks + if (!get_dynamic_per_kb_fee_estimate(fee_estimated)) + { + return json{}; + } + + j_info["fee_per_kb"] = fee_estimated; + + return j_info; + }); + + + // get mempool for the front page also using async future + std::future mempool_ftr = std::async(std::launch::async, [&] + { + // get memory pool rendered template + return mempool(false, no_of_mempool_tx_of_frontpage); + }); + //get current server timestamp server_timestamp = std::time(nullptr); @@ -427,6 +490,16 @@ namespace xmreg {"show_cache_times" , show_cache_times} }; +// std::list atl_blks; +// +// if (core_storage->get_alternative_blocks(atl_blks)) +// { +// for (const block& alt_blk: atl_blks) +// { +// //cout << "alt_blk: " << get_block_height(alt_blk) << endl; +// } +// } + context.emplace("txs", mstch::array()); // will keep tx to show // get reference to txs mstch map to be field below @@ -694,8 +767,116 @@ namespace xmreg context["cache_hits"] = cache_hits; context["cache_misses"] = cache_misses; + // now time to check if we have our networkinfo from network_info future + // wait a bit (300 millisecond max) if not, just in case, but we dont wait more. + // if its not ready by now, forget about it. + + std::future_status ftr_status = network_info_ftr.wait_for( + std::chrono::milliseconds(network_info_timeout)); + + network_info current_network_info {0, 0, 0, 0, 0, 0}; + + bool is_network_info_current {false}; + + if (ftr_status == std::future_status::ready) + { + json j_network_info = network_info_ftr.get(); + + if (!j_network_info.empty()) + { + current_network_info.difficulty = j_network_info["difficulty"]; + current_network_info.hash_rate = j_network_info["hash_rate"]; + current_network_info.fee_per_kb = j_network_info["fee_per_kb"]; + current_network_info.tx_pool_size = j_network_info["tx_pool_size"]; + current_network_info.alt_blocks_no = j_network_info["alt_blocks_count"]; + current_network_info.info_timestamp = local_copy_server_timestamp; + + previous_network_info = current_network_info; + + is_network_info_current = true; + } + } + else + { + current_network_info = previous_network_info; + cerr << "network_info future not ready yet, use the previous_network_info." << endl; + } + + // perapre network info mstch::map for the front page + + string hash_rate; + + if (testnet) + { + hash_rate = std::to_string(current_network_info.hash_rate) + " H/s"; + } + else + { + hash_rate = fmt::format("{:0.3f} MH/s", current_network_info.hash_rate/1.0e6); + } + + pair network_info_age = get_age(local_copy_server_timestamp, + current_network_info.info_timestamp); + + // if network info is younger than 2 minute, assume its current. No sense + // showing that it is not current if its less then block time. + + if (local_copy_server_timestamp - current_network_info.info_timestamp < 120) + { + is_network_info_current = true; + } + + context["network_info"] = mstch::map { + {"difficulty" , current_network_info.difficulty}, + {"hash_rate" , hash_rate}, + {"fee_per_kb" , print_money(current_network_info.fee_per_kb)}, + {"alt_blocks_no" , current_network_info.alt_blocks_no}, + {"tx_pool_size" , current_network_info.tx_pool_size}, + {"is_current_info" , is_network_info_current}, + {"is_pool_size_zero" , (current_network_info.tx_pool_size == 0)}, + {"age" , network_info_age.first}, + {"age_format" , network_info_age.second}, + }; + + string mempool_html {"Cant get mempool_pool"}; + + // get mempool data for the front page, if ready. If not, then just skip. + std::future_status mempool_ftr_status = mempool_ftr.wait_for( + std::chrono::milliseconds(mempool_info_timeout)); + + if (mempool_ftr_status == std::future_status::ready) + { + mempool_html = mempool_ftr.get(); + } + else + { + cerr << "mempool future not ready yet, skipping." << endl; + mempool_html = mstch::render(template_file["mempool_error"], context); + } + + if (CurrentBlockchainStatus::is_thread_running()) + { + CurrentBlockchainStatus::Emission current_values + = CurrentBlockchainStatus::get_emission(); + + string emission_blk_no = std::to_string(current_values.blk_no - 1); + string emission_coinbase = xmr_amount_to_str(current_values.coinbase, "{:0.3f}"); + string emission_fee = xmr_amount_to_str(current_values.fee, "{:0.3f}"); + + context["emission"] = mstch::map { + {"blk_no" , emission_blk_no}, + {"amount" , emission_coinbase}, + {"fee_amount", emission_fee} + }; + } + else + { + cerr << "emission thread not running, skipping." << endl; + } + + // get memory pool rendered template - string mempool_html = mempool(false, no_of_mempool_tx_of_frontpage); + //string mempool_html = mempool(false, no_of_mempool_tx_of_frontpage); // append mempool_html to the index context map context["mempool_info"] = mempool_html; @@ -707,8 +888,8 @@ namespace xmreg } /** - * Render mempool data - */ + * Render mempool data + */ string mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25) { @@ -915,7 +1096,7 @@ namespace xmreg {"no_inputs" , no_inputs}, {"no_outputs" , no_outputs}, {"no_nonrct_inputs", num_nonrct_inputs}, - {"mixin" , mixin_no}, + {"mixin" , mixin_no+1}, {"txsize" , txsize} }); @@ -4677,6 +4858,7 @@ namespace xmreg json j_info; + // get basic network info if (!get_monero_network_info(j_info)) { j_response["status"] = "error"; @@ -4684,6 +4866,38 @@ namespace xmreg return j_response; } + uint64_t fee_estimated {0}; + + // get dynamic fee estimate from last 10 blocks + if (!get_dynamic_per_kb_fee_estimate(fee_estimated)) + { + j_response["status"] = "error"; + j_response["message"] = "Cant get dynamic fee esimate"; + return j_response; + } + + j_info["fee_per_kb"] = fee_estimated; + +// // get mempool size in kB. +// std::vector mempool_txs; +// +// if (!rpc.get_mempool(mempool_txs)) +// { +// j_response["status"] = "error"; +// j_response["message"] = "Cant get mempool transactions"; +// return j_response; +// } +// +// uint64_t tx_pool_size_kbytes {0}; +// +// for (const tx_info& tx_i: mempool_txs) +// { +// tx_pool_size_kbytes += tx_i.blob_size; +// } +// +// j_info["tx_pool_size"] = mempool_txs.size(); +// j_info["tx_pool_size_kbytes"] = tx_pool_size_kbytes; + j_data = j_info; j_response["status"] = "success"; @@ -4692,6 +4906,50 @@ namespace xmreg } + /* + * Lets use this json api convention for success and error + * https://labs.omniti.com/labs/jsend + */ + json + json_emission() + { + json j_response { + {"status", "fail"}, + {"data", json {}} + }; + + json& j_data = j_response["data"]; + + json j_info; + + // get basic network info + if (!CurrentBlockchainStatus::is_thread_running()) + { + j_data["title"] = "Emission monitoring thread not enabled."; + return j_response; + } + else + { + CurrentBlockchainStatus::Emission current_values + = CurrentBlockchainStatus::get_emission(); + + string emission_blk_no = std::to_string(current_values.blk_no - 1); + string emission_coinbase = xmr_amount_to_str(current_values.coinbase, "{:0.3f}"); + string emission_fee = xmr_amount_to_str(current_values.fee, "{:0.3f}"); + + j_data = json { + {"blk_no" , current_values.blk_no - 1}, + {"coinbase", current_values.coinbase}, + {"fee" , current_values.fee}, + }; + } + + j_response["status"] = "success"; + + return j_response; + } + + private: json @@ -4717,6 +4975,7 @@ namespace xmreg return j_tx; } + bool find_tx(const crypto::hash& tx_hash, transaction& tx, @@ -5464,7 +5723,7 @@ namespace xmreg + template_file["footer"]; } - bool + bool get_monero_network_info(json& j_info) { COMMAND_RPC_GET_INFO::response network_info; @@ -5498,6 +5757,25 @@ namespace xmreg return true; } + bool + get_dynamic_per_kb_fee_estimate(uint64_t& fee_estimated) + { + + string error_msg; + + if (!rpc.get_dynamic_per_kb_fee_estimate( + FEE_ESTIMATE_GRACE_BLOCKS, + fee_estimated, error_msg)) + { + cerr << "rpc.get_dynamic_per_kb_fee_estimate failed" << endl; + return false; + } + + (void) error_msg; + + return true; + } + string get_footer() { @@ -5506,7 +5784,8 @@ namespace xmreg static const mstch::map footer_context { {"last_git_commit_hash", string {GIT_COMMIT_HASH}}, {"last_git_commit_date", string {GIT_COMMIT_DATETIME}}, - {"monero_version_full" , string {MONERO_VERSION_FULL}}, + {"git_branch_name" , string {GIT_BRANCH_NAME}}, + {"monero_version_full" , string {MONERO_VERSION_FULL}} }; string footer_html = mstch::render(xmreg::read(TMPL_FOOTER), footer_context); @@ -5527,3 +5806,4 @@ namespace xmreg #endif //CROWXMR_PAGE_H + diff --git a/src/rpccalls.cpp b/src/rpccalls.cpp index 7fcce07..ddce8ef 100644 --- a/src/rpccalls.cpp +++ b/src/rpccalls.cpp @@ -207,4 +207,70 @@ rpccalls::get_network_info(COMMAND_RPC_GET_INFO::response& response) } +bool +rpccalls::get_dynamic_per_kb_fee_estimate( + uint64_t grace_blocks, + uint64_t& fee, + string& error_msg) +{ + epee::json_rpc::request + req_t = AUTO_VAL_INIT(req_t); + epee::json_rpc::response + resp_t = AUTO_VAL_INIT(resp_t); + + + req_t.jsonrpc = "2.0"; + req_t.id = epee::serialization::storage_entry(0); + req_t.method = "get_fee_estimate"; + req_t.params.grace_blocks = grace_blocks; + + bool r {false}; + + std::lock_guard guard(m_daemon_rpc_mutex); + + { + if (!connect_to_monero_deamon()) + { + cerr << "get_current_height: not connected to deamon" << endl; + return false; + } + + r = epee::net_utils::invoke_http_json("/json_rpc", + req_t, resp_t, + m_http_client); + } + + string err; + + + if (r) + { + if (resp_t.result.status == CORE_RPC_STATUS_BUSY) + { + err = "daemon is busy. Please try again later."; + } + else if (resp_t.result.status != CORE_RPC_STATUS_OK) + { + err = resp_t.result.status; + } + + if (!err.empty()) + { + cerr << "Error connecting to Monero deamon due to " + << err << endl; + return false; + } + } + else + { + cerr << "Error connecting to Monero deamon at " + << deamon_url << endl; + return false; + } + + fee = resp_t.result.fee; + + return true; + +} } diff --git a/src/rpccalls.h b/src/rpccalls.h index ed21ea4..f0aefbc 100644 --- a/src/rpccalls.h +++ b/src/rpccalls.h @@ -52,6 +52,12 @@ public: bool get_network_info(COMMAND_RPC_GET_INFO::response& info); + bool + get_dynamic_per_kb_fee_estimate( + uint64_t grace_blocks, + uint64_t& fee, + string& error_msg); + }; diff --git a/src/templates/block.html b/src/templates/block.html index 117a4f9..c299f91 100644 --- a/src/templates/block.html +++ b/src/templates/block.html @@ -60,7 +60,7 @@ hash outputs fee - mixin + ring size in/out size [kB] version diff --git a/src/templates/checkrawtx.html b/src/templates/checkrawtx.html index 37772b6..85c8ee3 100644 --- a/src/templates/checkrawtx.html +++ b/src/templates/checkrawtx.html @@ -36,8 +36,8 @@

- Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}}; - resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}}) + Inputs' ring size time scale (from {{min_mix_time}} till {{max_mix_time}}; + resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real ring member {{/have_raw_tx}})

    @@ -111,4 +111,4 @@

- \ No newline at end of file + diff --git a/src/templates/css/style.css b/src/templates/css/style.css index f11dee2..ac4336b 100644 --- a/src/templates/css/style.css +++ b/src/templates/css/style.css @@ -16,7 +16,7 @@ h1, h2, h3, h4, h5, h6 { padding: 10px;*/ } -tr, li, #pages { +tr, li, #pages, .info { font-family: "Lucida Console", Monaco, monospace; font-size : 12px; height: 22px; diff --git a/src/templates/footer.html b/src/templates/footer.html index d4ab012..e5db414 100644 --- a/src/templates/footer.html +++ b/src/templates/footer.html @@ -1,7 +1,7 @@
source code - | explorer version: {{last_git_commit_date}}-{{last_git_commit_hash}} + | explorer version: {{git_branch_name}}-{{last_git_commit_date}}-{{last_git_commit_hash}} | monero version: {{monero_version_full}}
diff --git a/src/templates/index.html b/src/templates/index.html index 46b0272..41bcd93 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -32,7 +32,7 @@ txs fees outputs - mixins + ring size size [kB] {{#blocks}} diff --git a/src/templates/index2.html b/src/templates/index2.html index 8e70134..af8aaed 100644 --- a/src/templates/index2.html +++ b/src/templates/index2.html @@ -32,6 +32,27 @@ {{/testnet}} + + + {{#network_info}} +

+ Network difficulty: {{difficulty}} + | Hash rate: {{hash_rate}} + | Fee per kb: {{fee_per_kb}} + | Alternative blocks no: {{alt_blocks_no}} + {{^is_current_info}} + | Data from {{age}} {{age_format}} ago + {{/is_current_info}} +

+ {{/network_info}} + + {{#emission}} +

+ Monero emission (fees) is {{amount}} ({{fee_amount}}) as of {{blk_no}} block +

+ {{/emission}} + + {{{mempool_info}}} @@ -58,7 +79,7 @@ fees outputs in(nonrct)/out - mixin + ring size tx size [kB] {{#txs}} @@ -87,6 +108,7 @@ + {{#show_cache_times}}
@@ -96,4 +118,4 @@ and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses)
-{{/show_cache_times}} \ No newline at end of file +{{/show_cache_times}} diff --git a/src/templates/mempool.html b/src/templates/mempool.html index 1f00920..b7e4c18 100644 --- a/src/templates/mempool.html +++ b/src/templates/mempool.html @@ -11,7 +11,7 @@ fee outputs in(nonrct)/out - mixin + ring size tx size [kB] {{#mempooltxs}} diff --git a/src/templates/mempool_error.html b/src/templates/mempool_error.html new file mode 100644 index 0000000..a003cae --- /dev/null +++ b/src/templates/mempool_error.html @@ -0,0 +1,17 @@ +

+ Memory pool +

+

+
+ +

Mempool data preparation for the front page failed. + Its processing + {{#network_info}}{{^is_pool_size_zero}}({{tx_pool_size}} txs){{/is_pool_size_zero}}{{/network_info}} + took longer than expected and it timed out. + To view the mempool without time constrain, + go to dedicated mempool page: memory pool +

+ + + +
diff --git a/src/templates/my_outputs.html b/src/templates/my_outputs.html index f744cbf..69040a1 100644 --- a/src/templates/my_outputs.html +++ b/src/templates/my_outputs.html @@ -91,7 +91,7 @@
- Mixin {{mixin_pub_key}} might use your outputs + Ring member {{mixin_pub_key}} might use your outputs
from tx of hash: {{mix_tx_hash}}
(tx public key: {{mix_tx_pub_key}}) @@ -133,7 +133,7 @@

- Sum XMR from matched and marked by * mixin's outputs: {{sum_mixin_xmr}} + Sum XMR from matched and marked by * ring member's outputs: {{sum_mixin_xmr}}
Possible spending is: {{possible_spending}} (tx fee included) @@ -150,4 +150,4 @@ -
\ No newline at end of file +
diff --git a/src/templates/partials/tx_details.html b/src/templates/partials/tx_details.html index 91deb2d..8260d3c 100644 --- a/src/templates/partials/tx_details.html +++ b/src/templates/partials/tx_details.html @@ -125,8 +125,8 @@ {{#has_inputs}} {{#enable_mixins_details}} -

Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}}; - resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}}) +

Inputs' ring size time scale (from {{min_mix_time}} till {{max_mix_time}}; + resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real ring member {{/have_raw_tx}})

    @@ -180,12 +180,12 @@ {{#enable_mixins_details}} - + {{#have_raw_tx}} {{/have_raw_tx}} - + @@ -214,7 +214,7 @@ {{^enable_mixins_details}}
    Mixin stealth addressring membersIs it real?blkmixinring size in/out timestamp age [y:d:h:m:s]
    - + {{#have_raw_tx}} {{/have_raw_tx}} diff --git a/src/templates/partials/tx_table_header.html b/src/templates/partials/tx_table_header.html index 123d214..3de6ef2 100644 --- a/src/templates/partials/tx_table_header.html +++ b/src/templates/partials/tx_table_header.html @@ -3,7 +3,7 @@ - + diff --git a/src/version.h.in b/src/version.h.in index ef62e86..26b5927 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,7 @@ #define GIT_BRANCH "@GIT_BRANCH@" #define GIT_COMMIT_HASH "@GIT_COMMIT_HASH@" #define GIT_COMMIT_DATETIME "@GIT_COMMIT_DATETIME@" +#define GIT_BRANCH_NAME "@GIT_BRANCH_NAME@" #endif //XMRBLOCKS_VERSION_H_IN_H
    Mixin stealth addressring membersIs it real?tx hash outputs feemixinring size in/out size [kB]