mirror of
https://git.wownero.com/wownero/onion-wownero-blockchain-explorer.git
synced 2024-08-15 00:33:12 +00:00
Merge pull request #63 from moneroexamples/add_network_info_front_page
Add network info front page
This commit is contained in:
commit
cd140fd4ba
25 changed files with 1081 additions and 75 deletions
|
@ -112,6 +112,7 @@ set(LIBRARIES
|
|||
unbound
|
||||
curl
|
||||
crypto
|
||||
atomic
|
||||
ssl)
|
||||
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32)
|
||||
|
|
141
README.md
141
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 explorer commands used for http://139.162.32.245:8081/ and http://139.162.32.245:8082/, respectively.
|
||||
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
|
||||
```
|
||||
|
||||
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/<block_number|block_hash>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
89
main.cpp
89
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<string>("no-blocks-on-index");
|
||||
auto testnet_url = opts.get_option<string>("testnet-url");
|
||||
auto mainnet_url = opts.get_option<string>("mainnet-url");
|
||||
auto network_info_timeout_opt = opts.get_option<string>("network-info-timeout");
|
||||
auto mempool_info_timeout_opt = opts.get_option<string>("mempool-info-timeout");
|
||||
auto testnet_opt = opts.get_option<bool>("testnet");
|
||||
auto enable_key_image_checker_opt = opts.get_option<bool>("enable-key-image-checker");
|
||||
auto enable_output_key_checker_opt = opts.get_option<bool>("enable-output-key-checker");
|
||||
|
@ -60,6 +62,9 @@ int main(int ac, const char* av[]) {
|
|||
auto enable_tx_cache_opt = opts.get_option<bool>("enable-tx-cache");
|
||||
auto enable_block_cache_opt = opts.get_option<bool>("enable-block-cache");
|
||||
auto show_cache_times_opt = opts.get_option<bool>("show-cache-times");
|
||||
auto enable_emission_monitor_opt = opts.get_option<bool>("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<uint64_t>(*network_info_timeout_opt);
|
||||
mempool_info_timeout = boost::lexical_cast<uint64_t>(*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
|
||||
// <blockchain_path>/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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<bool>()->default_value(false)->implicit_value(true),
|
||||
|
@ -26,7 +26,7 @@ namespace xmreg
|
|||
("testnet,t", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"use testnet blockchain")
|
||||
("enable-pusher", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable pushing signed tx")
|
||||
"enable signed transaction pusher")
|
||||
("enable-mixin-details", value<bool>()->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<bool>()->default_value(false)->implicit_value(true),
|
||||
|
@ -34,33 +34,39 @@ namespace xmreg
|
|||
("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable outputs key file checker")
|
||||
("enable-mempool-cache", value<bool>()->default_value(true),
|
||||
"enable caching txs in the mempool")
|
||||
"enable caching of transactions from the mempool")
|
||||
("enable-json-api", value<bool>()->default_value(true),
|
||||
"enable JSON REST api")
|
||||
("enable-tx-cache", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable caching of tx details")
|
||||
"enable caching of transaction details")
|
||||
("show-cache-times", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"show times of getting data from cache vs no cache")
|
||||
("enable-block-cache", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable caching of block details")
|
||||
("enable-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable users to have the index page on autorefresh")
|
||||
("enable-emission-monitor", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable Monero total emission monitoring thread")
|
||||
("port,p", value<string>()->default_value("8081"),
|
||||
"default port")
|
||||
"default explorer port")
|
||||
("testnet-url", value<string>()->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<string>()->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<string>()->default_value("10"),
|
||||
"number of last blocks to be shown on index page")
|
||||
("network-info-timeout", value<string>()->default_value("1000"),
|
||||
"maximum time, in milliseconds, to wait for network info availability")
|
||||
("mempool-info-timeout", value<string>()->default_value("5000"),
|
||||
"maximum time, in milliseconds, to wait for mempool data for the front page")
|
||||
("bc-path,b", value<string>(),
|
||||
"path to lmdb blockchain")
|
||||
"path to lmdb folder of the blockchain, e.g., ~/.bitmonero/lmdb")
|
||||
("ssl-crt-file", value<string>(),
|
||||
"A path to crt file for ssl (https) functionality")
|
||||
"path to crt file for ssl (https) functionality")
|
||||
("ssl-key-file", value<string>(),
|
||||
"A path to key file for ssl (https) functionality")
|
||||
"path to key file for ssl (https) functionality")
|
||||
("deamon-url,d", value<string>()->default_value("http:://127.0.0.1:18081"),
|
||||
"monero address string");
|
||||
"Monero deamon url");
|
||||
|
||||
|
||||
store(command_line_parser(acc, avv)
|
||||
|
|
322
src/CurrentBlockchainStatus.cpp
Normal file
322
src/CurrentBlockchainStatus.cpp
Normal file
|
@ -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<transaction> txs;
|
||||
std::list<crypto::hash> 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<string> 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<uint64_t>(strs.at(0));
|
||||
emission_loaded.coinbase = boost::lexical_cast<uint64_t>(strs.at(1));
|
||||
emission_loaded.fee = boost::lexical_cast<uint64_t>(strs.at(2));
|
||||
read_check_sum = boost::lexical_cast<uint64_t>(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<uint64_t> CurrentBlockchainStatus::current_height {0};
|
||||
|
||||
atomic<CurrentBlockchainStatus::Emission> CurrentBlockchainStatus::total_emission_atomic;
|
||||
|
||||
boost::thread CurrentBlockchainStatus::m_thread;
|
||||
|
||||
atomic<bool> CurrentBlockchainStatus::is_running {false};
|
||||
|
||||
Blockchain* CurrentBlockchainStatus::core_storage {nullptr};
|
||||
xmreg::MicroCore* CurrentBlockchainStatus::mcore {nullptr};
|
||||
}
|
114
src/CurrentBlockchainStatus.h
Normal file
114
src/CurrentBlockchainStatus.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// Created by mwo on 16/05/17.
|
||||
//
|
||||
|
||||
#ifndef XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H
|
||||
#define XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H
|
||||
|
||||
#include "MicroCore.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
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<uint64_t> current_height;
|
||||
|
||||
|
||||
static atomic<Emission> total_emission_atomic;
|
||||
|
||||
|
||||
static boost::thread m_thread;
|
||||
|
||||
static atomic<bool> 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
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
294
src/page.h
294
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<string, mempool_tx_info> 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<network_info> 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<json> 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<string> 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<block> 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<string, string> 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<tx_info> 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
|
||||
|
||||
|
|
|
@ -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<COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request>
|
||||
req_t = AUTO_VAL_INIT(req_t);
|
||||
epee::json_rpc::response<COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response, std::string>
|
||||
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<std::mutex> 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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<td>hash</td>
|
||||
<td>outputs</td>
|
||||
<td>fee</td>
|
||||
<td>mixin</td>
|
||||
<td>ring size</td>
|
||||
<td>in/out</td>
|
||||
<td>size [kB]</td>
|
||||
<td>version</td>
|
||||
|
|
|
@ -36,8 +36,8 @@
|
|||
</h5>
|
||||
|
||||
<h3>
|
||||
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}})
|
||||
</h3>
|
||||
<div class="center">
|
||||
<ul class="center">
|
||||
|
@ -111,4 +111,4 @@
|
|||
</div>
|
||||
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="center">
|
||||
<h6 style="margin-top:10px">
|
||||
<a href="https://github.com/moneroexamples/onion-monero-blockchain-explorer">source code</a>
|
||||
| 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}}
|
||||
</h6>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<td>txs</td>
|
||||
<td>fees</td>
|
||||
<td>outputs</td>
|
||||
<td>mixins</td>
|
||||
<td>ring size</td>
|
||||
<td>size [kB]</td>
|
||||
</tr>
|
||||
{{#blocks}}
|
||||
|
|
|
@ -32,6 +32,27 @@
|
|||
{{/testnet}}
|
||||
|
||||
</h3>
|
||||
|
||||
|
||||
{{#network_info}}
|
||||
<h3 style="font-size: 12px; margin-top: 5px; margin-bottom: 3">
|
||||
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}}
|
||||
</h3>
|
||||
{{/network_info}}
|
||||
|
||||
{{#emission}}
|
||||
<h3 style="font-size: 12px; margin-top: 2px">
|
||||
Monero emission (fees) is {{amount}} ({{fee_amount}}) as of {{blk_no}} block
|
||||
</h3>
|
||||
{{/emission}}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{{mempool_info}}}
|
||||
|
@ -58,7 +79,7 @@
|
|||
<td>fees</td>
|
||||
<td>outputs</td>
|
||||
<td>in(nonrct)/out</td>
|
||||
<td>mixin</td>
|
||||
<td>ring size</td>
|
||||
<td>tx size [kB]</td>
|
||||
</tr>
|
||||
{{#txs}}
|
||||
|
@ -87,6 +108,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
{{#show_cache_times}}
|
||||
<div class="center">
|
||||
<h6 style="margin-top: 1px;color:#949490">
|
||||
|
@ -96,4 +118,4 @@
|
|||
and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses)
|
||||
</h6>
|
||||
</div>
|
||||
{{/show_cache_times}}
|
||||
{{/show_cache_times}}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<td>fee</td>
|
||||
<td>outputs</td>
|
||||
<td>in(nonrct)/out</td>
|
||||
<td>mixin</td>
|
||||
<td>ring size</td>
|
||||
<td>tx size [kB]</td>
|
||||
</tr>
|
||||
{{#mempooltxs}}
|
||||
|
|
17
src/templates/mempool_error.html
Normal file
17
src/templates/mempool_error.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<h2 style="margin-bottom: 0px">
|
||||
Memory pool
|
||||
</h2>
|
||||
<h4 style="font-size: 14px; margin-top: 0px"></h4>
|
||||
<div class="center info" style="text-align: center;width:80%;color:#949490">
|
||||
|
||||
<p>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: <a href="/mempool">memory pool</a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
</div>
|
|
@ -91,7 +91,7 @@
|
|||
<table class="center">
|
||||
<tr>
|
||||
<td style="text-align: center;">
|
||||
Mixin {{mixin_pub_key}} might use your outputs
|
||||
Ring member {{mixin_pub_key}} might use your outputs
|
||||
<br/>
|
||||
from tx of hash: <a href="/tx/{{mix_tx_hash}}">{{mix_tx_hash}}</a>
|
||||
<br/>(tx public key: {{mix_tx_pub_key}})
|
||||
|
@ -133,7 +133,7 @@
|
|||
|
||||
</div>
|
||||
<h3>
|
||||
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}}
|
||||
<br/>
|
||||
<span style="font-size: 16px"> Possible spending is:
|
||||
{{possible_spending}} (tx fee included)
|
||||
|
@ -150,4 +150,4 @@
|
|||
|
||||
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
|
|
|
@ -125,8 +125,8 @@
|
|||
|
||||
{{#has_inputs}}
|
||||
{{#enable_mixins_details}}
|
||||
<h3>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}})
|
||||
<h3>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}})
|
||||
</h3>
|
||||
<div class="center">
|
||||
<ul class="center">
|
||||
|
@ -180,12 +180,12 @@
|
|||
{{#enable_mixins_details}}
|
||||
<table style="width:100%; margin-bottom:20px">
|
||||
<tr>
|
||||
<td>Mixin stealth address</td>
|
||||
<td>ring members</td>
|
||||
{{#have_raw_tx}}
|
||||
<td>Is it real?</td>
|
||||
{{/have_raw_tx}}
|
||||
<td>blk</td>
|
||||
<td>mixin</td>
|
||||
<td>ring size</td>
|
||||
<td>in/out</td>
|
||||
<td>timestamp</td>
|
||||
<td>age [y:d:h:m:s]</td>
|
||||
|
@ -214,7 +214,7 @@
|
|||
{{^enable_mixins_details}}
|
||||
<table style="width:100%; margin-bottom:20px">
|
||||
<tr>
|
||||
<td>Mixin stealth address</td>
|
||||
<td>ring members</td>
|
||||
{{#have_raw_tx}}
|
||||
<td>Is it real?</td>
|
||||
{{/have_raw_tx}}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<td>tx hash</td>
|
||||
<td>outputs</td>
|
||||
<td>fee</td>
|
||||
<td>mixin</td>
|
||||
<td>ring size</td>
|
||||
<td>in/out</td>
|
||||
<td>size [kB]</td>
|
||||
</tr>
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue