From 9b30acc5e2038cb4e12f71f0c1cf7365463a0541 Mon Sep 17 00:00:00 2001 From: moneroexamples Date: Tue, 12 Apr 2016 11:31:26 +0800 Subject: [PATCH] more info on index site and css added --- CMakeLists.txt | 70 +++++++++++++++++++++++---- cmake/copy_files.cmake | 96 +++++++++++++++++++++++++++++++++++++ ext/infix_iterator.h | 57 ++++++++++++++++++++++ main.cpp | 13 +++-- src/page.h | 90 +++++++++++++++++++++++++++------- src/templates/css/style.css | 5 ++ src/templates/header.html | 3 +- src/templates/index.html | 15 +++++- src/tools.cpp | 46 +++++++++++++++++- src/tools.h | 31 +++++++++++- 10 files changed, 393 insertions(+), 33 deletions(-) create mode 100644 cmake/copy_files.cmake create mode 100644 ext/infix_iterator.h create mode 100644 src/templates/css/style.css diff --git a/CMakeLists.txt b/CMakeLists.txt index ac4b6bd..9f44fb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,14 @@ cmake_minimum_required(VERSION 3.5) +#list(INSERT + # CMAKE_MODULE_PATH 0 ${PROJECT_SOURCE_DIR}/cmake) +set(CMAKE_MODULE_PATH + ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + set(PROJECT_NAME crowxmr) + project(${PROJECT_NAME}) set(CMAKE_CXX_FLAGS @@ -82,24 +88,70 @@ add_subdirectory(src/) set(SOURCE_FILES main.cpp) -ADD_CUSTOM_TARGET(driver DEPENDS src/templates/index.html) +#ADD_CUSTOM_TARGET(driver DEPENDS src/templates/index.html) add_executable(${PROJECT_NAME} ${SOURCE_FILES}) -#add_custom_command(OUTPUT template_folder -# COMMAND ${CMAKE_COMMAND} -E -# copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/src/templates" "${CMAKE_CURRENT_BINARY_DIR}" -# DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/templates") +#add_custom_command(TARGET template_folder POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy_directory +# "${CMAKE_CURRENT_SOURCE_DIR}/src/templates" "${CMAKE_CURRENT_BINARY_DIR}") # -#add_custom_target(index_html +#add_custom_target(template_folder # ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/templates") # #ADD_DEPENDENCIES(${PROJECT_NAME} -# index_html) +# template_folder) -file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/src/templates" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") +#file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/src/templates" +# DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") +# +#include(copy_files) +#copy_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/ *.html ${CMAKE_CURRENT_BINARY_DIR}) +# + +macro(configure_files srcDir destDir) + message(STATUS "Configuring directory ${destDir}") + make_directory(${destDir}) + + file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*) + foreach(templateFile ${templateFiles}) + set(srcTemplatePath ${srcDir}/${templateFile}) + if(NOT IS_DIRECTORY ${srcTemplatePath}) + message(STATUS "Configuring file ${templateFile}") + configure_file( + ${srcTemplatePath} + ${destDir}/${templateFile} + @ONLY) + endif(NOT IS_DIRECTORY ${srcTemplatePath}) + endforeach(templateFile) +endmacro(configure_files) + +configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates ${CMAKE_CURRENT_BINARY_DIR}/templates) +configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/css ${CMAKE_CURRENT_BINARY_DIR}/templates/css) + +#macro(copy_files GLOBPAT DESTINATION) +# +# message(STATUS "Configuring directory ${DESTINATION}") +# +# file(GLOB COPY_FILES +# RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} +# ${GLOBPAT}) +# add_custom_target(copy ALL +# COMMENT "Copying files: ${GLOBPAT}") +# +# foreach(FILENAME ${COPY_FILES}) +# set(SRC "${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME}") +# set(DST "${DESTINATION}/${FILENAME}") +# +# add_custom_command( +# TARGET copy +# COMMAND ${CMAKE_COMMAND} -E copy ${SRC} ${DST} +# ) +# endforeach(FILENAME) +#endmacro(copy_files) +# +#copy_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(${PROJECT_NAME} myxrm diff --git a/cmake/copy_files.cmake b/cmake/copy_files.cmake new file mode 100644 index 0000000..a419dc6 --- /dev/null +++ b/cmake/copy_files.cmake @@ -0,0 +1,96 @@ +# +# Utility macros for copying files +# +# Last update: 9th January 2013 +# + +# +# Create a target for copying files. +# +if (NOT TARGET copy_files) + add_custom_target(copy_files ALL) +endif() + +# +# Create a variable to keep track of the number of copy files targets created. +# +if (NOT copy_target_count) + set(copy_target_count 0) +endif (NOT copy_target_count) +set(copy_target_count ${copy_target_count} CACHE INTERNAL "" FORCE) + +#------------------------------------------------------------------------------- +# Macro: COPY_FILES +#------------------------------------------------------------------------------- +# +# Description: +# Adds a command to the build target 'copy_files' which copies files matching +# the specifeid globbing expression from the specified source directory to +# the specified destination directory. +# +# Usage: +# COPY_FILES(SRC_DIR GLOB_PAT DST_DIR) +# +# Arguments: +# - SRC_DIR : The source directory containging files to be copied. +# - GLOB_PAT : globbing expression used to match files for copying. +# - DST_DIR : The destination directory where files are to be copied to. +# +# Example: +# copy_files(${CMAKE_CURRENT_SOURCE_DIR} *.dat ${CMAKE_CURRENT_BINARY_DIR}) +# Will copy all files in the current source directory with the extension +# '.dat' into the current binary directory. +# +macro(COPY_FILES SRC_DIR GLOB_PAT DST_DIR) + file(GLOB file_list + RELATIVE ${SRC_DIR} + ${SRC_DIR}/${GLOB_PAT}) + math(EXPR copy_target_count '${copy_target_count}+1') + set(copy_target_count ${copy_target_count} CACHE INTERNAL "" FORCE) + set(target "copy_files_${copy_target_count}") + add_custom_target(${target}) + foreach(filename ${file_list}) + set(src "${SRC_DIR}/${filename}") + set(dst "${DST_DIR}/${filename}") + add_custom_command(TARGET ${target} PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${src} ${dst} + COMMENT "copying: ${src} to ${dst} " VERBATIM + ) + endforeach(filename) + #add_dependencies(copy_files ${target}) + add_dependencies(copy_files crowxmr) +endmacro(COPY_FILES) + +#------------------------------------------------------------------------------- +# Macro: COPY_FILE +#------------------------------------------------------------------------------- +# +# Description: +# Adds a command to the build target 'copy_files' which copies the specified +# file to the specified destination. +# +# Usage: +# COPY_FILE(SRC DST) +# +# Arguments: +# - SRC : The source filename path (the file to be copied). +# - DST : The destiation filename path. +# +# Example: +# copy_file( +# ${CMAKE_CURRENT_SOURCE_DIR}/myfile.txt +# ${CMAKE_CURRENT_BINARY_DIR}/myfile.txt +# ) +# +macro(COPY_FILE SRC DST) + math(EXPR copy_target_count '${copy_target_count}+1') + set(copy_target_count ${copy_target_count} CACHE INTERNAL "" FORCE) + set(target "copy_files_${copy_target_count}") + add_custom_target(${target}) + add_custom_command(TARGET ${target} PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${SRC} ${DST} + COMMENT "copying: ${SRC} to ${DST}" VERBATIM + ) + add_dependencies(copy_files ${target}) +endmacro(COPY_FILE) + diff --git a/ext/infix_iterator.h b/ext/infix_iterator.h new file mode 100644 index 0000000..519b338 --- /dev/null +++ b/ext/infix_iterator.h @@ -0,0 +1,57 @@ +// +// Created by mwo on 24/05/15. +// +// source: http://codereview.stackexchange.com/questions/13176/infix-iterator-code + +// infix_iterator.h +#if !defined(INFIX_ITERATOR_H_) +#define INFIX_ITERATOR_H_ +#include +#include +#include + +template > +class infix_ostream_iterator : + public std::iterator +{ + std::basic_ostream *os; + std::basic_string delimiter; + std::basic_string real_delim; + +public: + + typedef charT char_type; + typedef traits traits_type; + typedef std::basic_ostream ostream_type; + + infix_ostream_iterator(ostream_type &s) + : os(&s) + {} + + infix_ostream_iterator(ostream_type &s, charT const *d) + : os(&s), + real_delim(d) + {} + + infix_ostream_iterator &operator=(T const &item) + { + *os << delimiter << item; + delimiter = real_delim; + return *this; + } + + infix_ostream_iterator &operator*() { + return *this; + } + + infix_ostream_iterator &operator++() { + return *this; + } + + infix_ostream_iterator &operator++(int) { + return *this; + } +}; + +#endif + diff --git a/main.cpp b/main.cpp index 3a3db9a..ffa6b45 100644 --- a/main.cpp +++ b/main.cpp @@ -43,9 +43,16 @@ int main() { crow::SimpleApp app; CROW_ROUTE(app, "/") - ([&]() { - return xmrblocks.index(); - }); + ([&]() { + return xmrblocks.index(); + }); + + + CROW_ROUTE(app, "/css/style.css") + ([&]() { + return xmreg::read("./templates/css/style.css"); + }); + app.port(8080).multithreaded().run(); diff --git a/src/page.h b/src/page.h index 34aca0b..a1fff20 100644 --- a/src/page.h +++ b/src/page.h @@ -19,12 +19,16 @@ #include "tools.h" -#define TMPL_DIR "./templates" -#define TMPL_INDEX TMPL_DIR "/index.html" -#define TMPL_HEADER TMPL_DIR "/header.html" -#define TMPL_FOOTER TMPL_DIR "/footer.html" +#include + +#include + +#define TMPL_DIR "./templates" +#define TMPL_INDEX TMPL_DIR "/index.html" +#define TMPL_HEADER TMPL_DIR "/header.html" +#define TMPL_FOOTER TMPL_DIR "/footer.html" + -#define READ_TMPL(tmpl_path) xmreg::read(tmpl_path) namespace xmreg { @@ -49,50 +53,102 @@ namespace xmreg { string index() { + //get current server timestamp + time_t server_timestamp = std::time(nullptr); + // get the current blockchain height. Just to check if it reads ok. uint64_t height = core_storage->get_current_blockchain_height() - 1; + // initalise page tempate map with basic info about blockchain mstch::map context { - {"height", fmt::format("{:d}", height)}, + {"height", fmt::format("{:d}", height)}, + {"server_timestamp", xmreg::timestamp_to_str(server_timestamp)}, {"blocks", mstch::array()} }; - size_t no_of_last_blocks {50}; + // number of last blocks to show + size_t no_of_last_blocks {100}; + // get reference to blocks template map to be field below mstch::array& blocks = boost::get(context["blocks"]); + // iterate over last no_of_last_blocks of blocks for (size_t i = height; i > height - no_of_last_blocks; --i) { + // get block at the given height i block blk; mcore->get_block_by_height(i, blk); + // get block's hash crypto::hash blk_hash = core_storage->get_block_id_by_height(i); + // get xmr in the block reward + array coinbase_tx = sum_money_in_tx(blk.miner_tx); + + // get transactions in the block + const vector& txs_in_blk = + core_storage->get_db().get_tx_list(blk.tx_hashes); + + // sum xmr in the inputs and ouputs of all transactions + array sum_xmr_in_out = sum_money_in_txs(txs_in_blk); + + // get mixin number in each transaction + vector mixin_numbers = get_mixin_no_in_txs(txs_in_blk); + + // find minimum and maxium mixin numbers + int mixin_min {-1}; + int mixin_max {-1}; + + if (!mixin_numbers.empty()) + { + mixin_min = static_cast( + *std::min_element(mixin_numbers.begin(), mixin_numbers.end())); + mixin_max = static_cast( + *max_element(mixin_numbers.begin(), mixin_numbers.end())); + } + + auto mixin_format = [=]() -> mstch::node + { + if (mixin_min < 0) + { + return string("N/A"); + } + return fmt::format("{:d} - {:d}", mixin_min, mixin_max); + }; + + // set output page template map blocks.push_back(mstch::map { - {"height" , to_string(i)}, - {"timestamp" , xmreg::timestamp_to_str(blk.timestamp)}, - {"hash" , fmt::format("{:s}", blk_hash)}, - {"notx" , fmt::format("{:d}", blk.tx_hashes.size())} + {"height" , to_string(i)}, + {"timestamp" , xmreg::timestamp_to_str(blk.timestamp)}, + {"hash" , fmt::format("{:s}", blk_hash)}, + {"block_reward", fmt::format("{:0.4f}", XMR_AMOUNT(coinbase_tx[1]))}, + {"notx" , fmt::format("{:d}", blk.tx_hashes.size())}, + {"xmr_inputs" , fmt::format("{:0.4f}", XMR_AMOUNT(sum_xmr_in_out[0]))}, + {"xmr_outputs" , fmt::format("{:0.4f}", XMR_AMOUNT(sum_xmr_in_out[1]))}, + {"mixin_range" , mstch::lambda {mixin_format}} }); } + // read index.html + std::string index_html = xmreg::read(TMPL_INDEX); - std::string view = READ_TMPL(TMPL_INDEX); + // add header and footer + string full_page = get_full_page(index_html); - string full_page = get_full_page(view); - - return mstch::render(view, context); + // render the page + return mstch::render(full_page, context); } private: + string get_full_page(string& middle) { - return READ_TMPL(TMPL_HEADER) + return xmreg::read(TMPL_HEADER) + middle - + READ_TMPL(TMPL_FOOTER); + + xmreg::read(TMPL_FOOTER); } }; diff --git a/src/templates/css/style.css b/src/templates/css/style.css new file mode 100644 index 0000000..d1ee124 --- /dev/null +++ b/src/templates/css/style.css @@ -0,0 +1,5 @@ +tr { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size : 14px; + height: 22px; +} \ No newline at end of file diff --git a/src/templates/header.html b/src/templates/header.html index 306a76f..a9a6300 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -2,7 +2,8 @@ - Monero blocks + Hidden Monero Explorer +
\ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html index 7f12675..1211ebc 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -1,15 +1,28 @@

Hidden Monero blockchain explorer

-

Current height: {{height}}

+

Current height: {{height}} | Server time {{server_timestamp}}

    + + + + + + + + + + {{#blocks}} + + + {{/blocks}}
    heighttimestampblock_hashblock_rewardno_of_txsxmr_inputsxmr_outputsmixin_range
    {{height}} {{timestamp}} {{hash}}{{block_reward}} {{notx}}{{xmr_outputs}}{{mixin_range}}
    diff --git a/src/tools.cpp b/src/tools.cpp index c226486..161dc70 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -307,6 +307,36 @@ namespace xmreg return sum_xmr; } + + + array + sum_money_in_tx(const transaction& tx) + { + array sum_xmr; + + sum_xmr[0] = sum_money_in_inputs(tx); + sum_xmr[1] = sum_money_in_outputs(tx); + + return sum_xmr; + }; + + + array + sum_money_in_txs(const vector& txs) + { + array sum_xmr {0,0}; + + for (const transaction& tx: txs) + { + sum_xmr[0] += sum_money_in_inputs(tx); + sum_xmr[1] += sum_money_in_outputs(tx); + } + + return sum_xmr; + }; + + + vector> get_ouputs(const transaction& tx) { @@ -363,6 +393,19 @@ namespace xmreg return mixin_no; } + vector + get_mixin_no_in_txs(const vector& txs) + { + vector mixin_no; + + for (const transaction& tx: txs) + { + mixin_no.push_back(get_mixin_no(tx)); + } + + return mixin_no; + } + vector get_key_images(const transaction& tx) @@ -461,10 +504,11 @@ namespace xmreg { if (!bf::exists(bf::path(filename))) { + cerr << "File does not exist: " << filename << endl; return string(); } - std::ifstream t("./templates/index.html"); + std::ifstream t(filename); return string(std::istreambuf_iterator(t), std::istreambuf_iterator()); } diff --git a/src/tools.h b/src/tools.h index f59a3fe..15b94cd 100644 --- a/src/tools.h +++ b/src/tools.h @@ -7,10 +7,14 @@ #define PATH_SEPARARTOR '/' +#define XMR_AMOUNT(value) \ + static_cast(value) / 1e12 + #include "monero_headers.h" #include "tx_details.h" #include "../ext/dateparser.h" +#include "../ext/infix_iterator.h" #include #include @@ -128,14 +132,23 @@ namespace xmreg uint64_t sum_money_in_inputs(const transaction& tx); + array + sum_money_in_tx(const transaction& tx); + + array + sum_money_in_txs(const vector& txs); + uint64_t get_mixin_no(const transaction& tx); + vector + get_mixin_no_in_txs(const vector& txs); + vector> get_ouputs(const transaction& tx); vector - get_key_images(const transaction& tx); + get_key_images(const transaction& tx); inline void @@ -162,6 +175,22 @@ namespace xmreg string read(string filename); + + + /** + * prints an iterable such as vector + */ + template + void print_iterable(const T & elems) { + + infix_ostream_iterator + oiter(std::cout, ","); + + std::cout << "["; + std::copy(elems.begin(), elems.end(),oiter); + std::cout << "]" << std::endl; + } + } #endif //XMREG01_TOOLS_H