├── .gitignore ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── cmake ├── FindASan.cmake ├── FindHIDAPI.cmake ├── FindMSan.cmake ├── FindMonero.cmake ├── FindSanitizers.cmake ├── FindTSan.cmake ├── FindUBSan.cmake ├── MyUtils.cmake ├── asan-wrapper └── sanitize-helpers.cmake ├── ext ├── CMakeLists.txt ├── crow_all.h ├── fmt │ ├── format.cc │ ├── format.h │ ├── ostream.cc │ └── ostream.h ├── json.hpp ├── minicsv.h └── mstch │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── cmake │ └── mstch-config.cmake │ ├── include │ └── mstch │ │ └── mstch.hpp │ └── src │ ├── CMakeLists.txt │ ├── mstch.cpp │ ├── render_context.cpp │ ├── render_context.hpp │ ├── state │ ├── in_section.cpp │ ├── in_section.hpp │ ├── outside_section.cpp │ ├── outside_section.hpp │ └── render_state.hpp │ ├── template_type.cpp │ ├── template_type.hpp │ ├── token.cpp │ ├── token.hpp │ ├── utils.cpp │ ├── utils.hpp │ └── visitor │ ├── get_token.hpp │ ├── has_token.hpp │ ├── is_node_empty.hpp │ ├── render_node.hpp │ └── render_section.hpp ├── main.cpp └── src ├── CMakeLists.txt ├── CmdLineOptions.cpp ├── CmdLineOptions.h ├── CurrentBlockchainStatus.cpp ├── CurrentBlockchainStatus.h ├── MempoolStatus.cpp ├── MempoolStatus.h ├── MicroCore.cpp ├── MicroCore.h ├── crypto ├── CMakeLists.txt └── rx-slow-hash.c ├── monero_headers.h ├── page.h ├── rpccalls.cpp ├── rpccalls.h ├── templates ├── address.html ├── altblocks.html ├── block.html ├── checkrawkeyimgs.html ├── checkrawoutputkeys.html ├── checkrawtx.html ├── css │ └── style.css ├── footer.html ├── header.html ├── index.html ├── index2.html ├── mempool.html ├── mempool_error.html ├── my_outputs.html ├── partials │ ├── tx_details.html │ ├── tx_table_header.html │ └── tx_table_row.html ├── pushrawtx.html ├── randomx.html ├── rawkeyimgs.html ├── rawoutputkeys.html ├── rawtx.html ├── search_results.html └── tx.html ├── tools.cpp ├── tools.h └── version.h.in /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .sass-cache 3 | *.*~ 4 | *.user 5 | .idea/ 6 | *.log 7 | *.orig 8 | tests/ 9 | build/ 10 | cmake-build-debug/ 11 | .ycm_extra_conf.py 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | 3 | set(PROJECT_NAME 4 | xmrblocks) 5 | 6 | 7 | project(${PROJECT_NAME}) 8 | 9 | set(CMAKE_CXX_STANDARD 17) 10 | 11 | if (WIN32) 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -O3") 13 | endif() 14 | 15 | 16 | if (NOT MONERO_DIR) 17 | set(MONERO_DIR ~/monero) 18 | endif() 19 | 20 | message(STATUS MONERO_DIR ": ${MONERO_DIR}") 21 | 22 | if (NOT MONERO_SOURCE_DIR) 23 | set(MONERO_SOURCE_DIR ${MONERO_DIR} 24 | CACHE PATH "Path to the root directory for Monero") 25 | endif() 26 | 27 | if (NOT MONERO_BUILD_DIR) 28 | # set location of monero build tree 29 | set(MONERO_BUILD_DIR ${MONERO_SOURCE_DIR}/build/release/ 30 | CACHE PATH "Path to the build directory for Monero") 31 | 32 | if (NOT EXISTS ${MONERO_BUILD_DIR}) 33 | # try different location 34 | message(STATUS "Trying different folder for monero libraries") 35 | set(MONERO_BUILD_DIR ${MONERO_SOURCE_DIR}/build/Linux/master/release/ 36 | CACHE PATH "Path to the build directory for Monero" FORCE) 37 | endif() 38 | 39 | endif() 40 | 41 | if (NOT EXISTS ${MONERO_BUILD_DIR}) 42 | message(FATAL_ERROR "Monero libraries not found in: ${MONERO_BUILD_DIR}") 43 | endif() 44 | 45 | set(MY_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" 46 | CACHE PATH "The path to the cmake directory of the current project") 47 | 48 | list(APPEND CMAKE_MODULE_PATH "${MY_CMAKE_DIR}") 49 | 50 | set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "${MONERO_BUILD_DIR}" 51 | CACHE PATH "Add Monero directory for library searching") 52 | include(MyUtils) 53 | 54 | find_package(Monero) 55 | 56 | # find boost 57 | find_package(Boost COMPONENTS 58 | system 59 | filesystem 60 | thread 61 | date_time 62 | chrono 63 | regex 64 | serialization 65 | program_options 66 | date_time 67 | REQUIRED) 68 | 69 | #info https://github.com/arsenm/sanitizers-cmake 70 | find_package(Sanitizers) 71 | 72 | if(APPLE) 73 | include_directories(/usr/local/opt/openssl@1.1/include) 74 | link_directories(/usr/local/opt/openssl@1.1/lib) 75 | link_directories(/usr/local/lib) 76 | endif() 77 | 78 | MESSAGE(STATUS "Looking for libunbound") # FindUnbound.cmake from monero repo 79 | 80 | FIND_PATH(UNBOUND_INCLUDE_DIR 81 | NAMES unbound.h 82 | PATH_SUFFIXES include/ include/unbound/ 83 | PATHS "${PROJECT_SOURCE_DIR}" 84 | ${UNBOUND_ROOT} 85 | $ENV{UNBOUND_ROOT} 86 | /usr/local/ 87 | /usr/ 88 | ) 89 | 90 | find_library (UNBOUND_LIBRARY unbound) 91 | if (WIN32 OR (${UNBOUND_LIBRARY} STREQUAL "UNBOUND_LIBRARY-NOTFOUND")) 92 | add_library(unbound STATIC IMPORTED) 93 | set_property(TARGET unbound PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/external/unbound/libunbound.a) 94 | endif() 95 | 96 | if("${Xmr_WALLET-CRYPTO_LIBRARIES}" STREQUAL "Xmr_WALLET-CRYPTO_LIBRARY-NOTFOUND") 97 | set(WALLET_CRYPTO "") 98 | else() 99 | set(WALLET_CRYPTO ${Xmr_WALLET-CRYPTO_LIBRARIES}) 100 | endif() 101 | 102 | # include boost headers 103 | include_directories(${Boost_INCLUDE_DIRS}) 104 | 105 | # include monero 106 | include_directories(${MONERO_SOURCE_DIR}/build) 107 | 108 | include_directories("ext/mstch/include") 109 | include_directories("ext/mstch/include/src") 110 | include_directories("ext/crow") 111 | 112 | # add ext/ subfolder 113 | add_subdirectory(ext/) 114 | 115 | # add src/ subfolder 116 | add_subdirectory(src/) 117 | 118 | 119 | set(SOURCE_FILES 120 | main.cpp) 121 | 122 | #ADD_CUSTOM_TARGET(driver DEPENDS src/templates/index.html) 123 | 124 | add_executable(${PROJECT_NAME} 125 | ${SOURCE_FILES}) 126 | 127 | add_sanitizers(${PROJECT_NAME}) 128 | 129 | create_git_version() 130 | 131 | configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates ${CMAKE_CURRENT_BINARY_DIR}/templates) 132 | configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/css ${CMAKE_CURRENT_BINARY_DIR}/templates/css) 133 | configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/partials ${CMAKE_CURRENT_BINARY_DIR}/templates/partials) 134 | configure_files(${CMAKE_CURRENT_SOURCE_DIR}/src/templates/js ${CMAKE_CURRENT_BINARY_DIR}/templates/js) 135 | 136 | set(LIBRARIES 137 | myxrm 138 | myext 139 | mstch 140 | wallet 141 | cryptonote_core 142 | cryptonote_basic 143 | cryptonote_protocol 144 | cryptonote_format_utils_basic 145 | blockchain_db 146 | device 147 | ${WALLET_CRYPTO} 148 | multisig 149 | daemonizer 150 | blocks 151 | lmdb 152 | ringct 153 | ringct_basic 154 | common 155 | mnemonics 156 | easylogging 157 | checkpoints 158 | cncrypto 159 | miniupnpc 160 | version 161 | epee 162 | hardforks 163 | randomx 164 | sodium 165 | ${Boost_LIBRARIES} 166 | pthread 167 | unbound 168 | crypto 169 | ssl) 170 | 171 | if(APPLE) 172 | set(LIBRARIES ${LIBRARIES} "-framework IOKit -framework Foundation") 173 | else() 174 | set(LIBRARIES ${LIBRARIES} atomic) 175 | endif() 176 | 177 | find_library(UNWIND_LIBRARY unwind) 178 | if (${UNWIND_LIBRARY} STREQUAL "UNWIND_LIBRARY-NOTFOUND") 179 | message (STATUS "unwind library not found") 180 | set (UNWIND_LIBRARY "") 181 | else () 182 | message (STATUS "Found unwind library: ${UNWIND_LIBRARY}") 183 | endif () 184 | 185 | if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32) 186 | set(LIBRARIES ${LIBRARIES} ${UNWIND_LIBRARY}) 187 | endif() 188 | 189 | if (WIN32) 190 | set(LIBRARIES ${LIBRARIES} 191 | wsock32 192 | ntdll 193 | ws2_32 194 | Iphlpapi 195 | ) 196 | else() 197 | set(LIBRARIES ${LIBRARIES} dl) 198 | endif() 199 | 200 | find_package(HIDAPI) 201 | set(LIBRARIES ${LIBRARIES} ${HIDAPI_LIBRARIES}) 202 | 203 | target_link_libraries(${PROJECT_NAME} ${LIBRARIES}) 204 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use ubuntu:latest as base for builder stage image 2 | FROM ubuntu:latest as builder 3 | 4 | # Set Monero branch/tag to be used for monerod compilation 5 | ARG MONERO_BRANCH=v0.18.4.0 6 | 7 | # Added DEBIAN_FRONTEND=noninteractive to workaround tzdata prompt on installation 8 | ENV DEBIAN_FRONTEND="noninteractive" 9 | 10 | # Install dependencies for monerod and xmrblocks compilation 11 | RUN apt-get update \ 12 | && apt-get upgrade -y \ 13 | && apt-get install -y --no-install-recommends \ 14 | git \ 15 | build-essential \ 16 | cmake \ 17 | miniupnpc \ 18 | graphviz \ 19 | doxygen \ 20 | pkg-config \ 21 | ca-certificates \ 22 | zip \ 23 | libboost-all-dev \ 24 | libunbound-dev \ 25 | libunwind8-dev \ 26 | libssl-dev \ 27 | libcurl4-openssl-dev \ 28 | libgtest-dev \ 29 | libreadline-dev \ 30 | libzmq3-dev \ 31 | libsodium-dev \ 32 | libhidapi-dev \ 33 | libhidapi-libusb0 \ 34 | && apt clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 35 | 36 | # Set compilation environment variables 37 | ENV CFLAGS='-fPIC' 38 | ENV CXXFLAGS='-fPIC' 39 | ENV USE_SINGLE_BUILDDIR=1 40 | ENV BOOST_DEBUG=1 41 | 42 | WORKDIR /root 43 | 44 | # Clone and compile monerod with all available threads 45 | ARG NPROC 46 | RUN git clone --recursive --branch ${MONERO_BRANCH} \ 47 | --depth 1 --shallow-submodules https://github.com/monero-project/monero.git \ 48 | && cd monero \ 49 | && test -z "$NPROC" && nproc > /nproc || echo -n "$NPROC" > /nproc && make -j"$(cat /nproc)" 50 | 51 | # Copy and cmake/make xmrblocks with all available threads 52 | COPY . /root/onion-monero-blockchain-explorer/ 53 | WORKDIR /root/onion-monero-blockchain-explorer/build 54 | RUN cmake .. && make -j"$(cat /nproc)" 55 | 56 | # Use ldd and awk to bundle up dynamic libraries for the final image 57 | RUN zip /lib.zip $(ldd xmrblocks | grep -E '/[^\ ]*' -o) 58 | 59 | # Use ubuntu:latest as base for final image 60 | FROM ubuntu:latest AS final 61 | 62 | # Added DEBIAN_FRONTEND=noninteractive to workaround tzdata prompt on installation 63 | ENV DEBIAN_FRONTEND="noninteractive" 64 | 65 | # Update Ubuntu packages and install unzip to handle bundled libs from builder stage 66 | RUN apt-get update \ 67 | && apt-get upgrade -y \ 68 | && apt-get install -y --no-install-recommends unzip \ 69 | && apt clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 70 | 71 | COPY --from=builder /lib.zip . 72 | RUN unzip -o lib.zip \ 73 | && rm -rf lib.zip \ 74 | && apt purge -y unzip 75 | 76 | # Add user and setup directories for monerod and xmrblocks 77 | RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu \ 78 | && useradd -ms /bin/bash monero \ 79 | && mkdir -p /home/monero/.bitmonero \ 80 | && chown -R monero:monero /home/monero/.bitmonero 81 | USER monero 82 | 83 | # Switch to home directory and install newly built xmrblocks binary 84 | WORKDIR /home/monero 85 | COPY --chown=monero:monero --from=builder /root/onion-monero-blockchain-explorer/build/xmrblocks . 86 | COPY --chown=monero:monero --from=builder /root/onion-monero-blockchain-explorer/build/templates ./templates/ 87 | 88 | # Expose volume used for lmdb access by xmrblocks 89 | VOLUME /home/monero/.bitmonero 90 | 91 | # Expose default explorer http port 92 | EXPOSE 8081 93 | 94 | ENTRYPOINT ["/bin/sh", "-c"] 95 | 96 | # Set sane defaults that are overridden if the user passes any commands 97 | CMD ["./xmrblocks --enable-json-api --enable-autorefresh-option --enable-pusher"] 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, moneroexamples 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors 16 | may be used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /cmake/FindASan.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 4 | # 2013 Matthew Arsenault 5 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) 26 | 27 | set(FLAG_CANDIDATES 28 | # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. 29 | "-g -fsanitize=address -fno-omit-frame-pointer" 30 | "-g -fsanitize=address" 31 | 32 | # Older deprecated flag for ASan 33 | "-g -faddress-sanitizer" 34 | ) 35 | 36 | 37 | if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) 38 | message(FATAL_ERROR "AddressSanitizer is not compatible with " 39 | "ThreadSanitizer or MemorySanitizer.") 40 | endif () 41 | 42 | 43 | include(sanitize-helpers) 44 | 45 | if (SANITIZE_ADDRESS) 46 | sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" 47 | "ASan") 48 | 49 | find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) 50 | mark_as_advanced(ASan_WRAPPER) 51 | endif () 52 | 53 | function (add_sanitize_address TARGET) 54 | if (NOT SANITIZE_ADDRESS) 55 | return() 56 | endif () 57 | 58 | sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") 59 | endfunction () 60 | -------------------------------------------------------------------------------- /cmake/FindHIDAPI.cmake: -------------------------------------------------------------------------------- 1 | # - try to find HIDAPI library 2 | # from http://www.signal11.us/oss/hidapi/ 3 | # 4 | # Cache Variables: (probably not for direct use in your scripts) 5 | # HIDAPI_INCLUDE_DIR 6 | # HIDAPI_LIBRARY 7 | # 8 | # Non-cache variables you might use in your CMakeLists.txt: 9 | # HIDAPI_FOUND 10 | # HIDAPI_INCLUDE_DIRS 11 | # HIDAPI_LIBRARIES 12 | # 13 | # Requires these CMake modules: 14 | # FindPackageHandleStandardArgs (known included with CMake >=2.6.2) 15 | # 16 | # Original Author: 17 | # 2009-2010 Ryan Pavlik 18 | # http://academic.cleardefinition.com 19 | # Iowa State University HCI Graduate Program/VRAC 20 | # 21 | # Copyright Iowa State University 2009-2010. 22 | # Distributed under the Boost Software License, Version 1.0. 23 | # (See accompanying file LICENSE_1_0.txt or copy at 24 | # http://www.boost.org/LICENSE_1_0.txt) 25 | 26 | find_library(HIDAPI_LIBRARY 27 | NAMES hidapi hidapi-libusb) 28 | 29 | find_path(HIDAPI_INCLUDE_DIR 30 | NAMES hidapi.h 31 | PATH_SUFFIXES 32 | hidapi) 33 | 34 | include(FindPackageHandleStandardArgs) 35 | find_package_handle_standard_args(HIDAPI 36 | DEFAULT_MSG 37 | HIDAPI_LIBRARY 38 | HIDAPI_INCLUDE_DIR) 39 | 40 | if(HIDAPI_FOUND) 41 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") 42 | if((STATIC AND UNIX AND NOT APPLE) OR (DEPENDS AND CMAKE_SYSTEM_NAME STREQUAL "Linux")) 43 | find_library(LIBUSB-1.0_LIBRARY usb-1.0) 44 | find_library(LIBUDEV_LIBRARY udev) 45 | if(LIBUSB-1.0_LIBRARY) 46 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUSB-1.0_LIBRARY}") 47 | if(LIBUDEV_LIBRARY) 48 | set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARIES};${LIBUDEV_LIBRARY}") 49 | else() 50 | message(WARNING "libudev library not found, binaries may fail to link.") 51 | endif() 52 | else() 53 | message(WARNING "libusb-1.0 library not found, binaries may fail to link.") 54 | endif() 55 | endif() 56 | 57 | set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") 58 | endif() 59 | 60 | mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) 61 | -------------------------------------------------------------------------------- /cmake/FindMSan.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 4 | # 2013 Matthew Arsenault 5 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) 26 | 27 | set(FLAG_CANDIDATES 28 | "-g -fsanitize=memory" 29 | ) 30 | 31 | 32 | include(sanitize-helpers) 33 | 34 | if (SANITIZE_MEMORY) 35 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 36 | message(WARNING "MemorySanitizer disabled for target ${TARGET} because " 37 | "MemorySanitizer is supported for Linux systems only.") 38 | set(SANITIZE_MEMORY Off CACHE BOOL 39 | "Enable MemorySanitizer for sanitized targets." FORCE) 40 | elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) 41 | message(WARNING "MemorySanitizer disabled for target ${TARGET} because " 42 | "MemorySanitizer is supported for 64bit systems only.") 43 | set(SANITIZE_MEMORY Off CACHE BOOL 44 | "Enable MemorySanitizer for sanitized targets." FORCE) 45 | else () 46 | sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" 47 | "MSan") 48 | endif () 49 | endif () 50 | 51 | function (add_sanitize_memory TARGET) 52 | if (NOT SANITIZE_MEMORY) 53 | return() 54 | endif () 55 | 56 | sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") 57 | endfunction () 58 | -------------------------------------------------------------------------------- /cmake/FindMonero.cmake: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # CMake helper for the majority of the cpp-ethereum modules. 3 | # 4 | # This module defines 5 | # Monero_XXX_LIBRARIES, the libraries needed to use ethereum. 6 | # Monero_FOUND, If false, do not try to use ethereum. 7 | # 8 | # File addetped from cpp-ethereum 9 | # 10 | # The documentation for cpp-ethereum is hosted at http://cpp-ethereum.org 11 | # 12 | # ------------------------------------------------------------------------------ 13 | # This file is part of cpp-ethereum. 14 | # 15 | # cpp-ethereum is free software: you can redistribute it and/or modify 16 | # it under the terms of the GNU General Public License as published by 17 | # the Free Software Foundation, either version 3 of the License, or 18 | # (at your option) any later version. 19 | # 20 | # cpp-ethereum is distributed in the hope that it will be useful, 21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | # GNU General Public License for more details. 24 | # 25 | # You should have received a copy of the GNU General Public License 26 | # along with cpp-ethereum. If not, see 27 | # 28 | # (c) 2014-2016 cpp-ethereum contributors. 29 | #------------------------------------------------------------------------------ 30 | 31 | set(LIBS common;blocks;cryptonote_basic;cryptonote_core;multisig; 32 | cryptonote_protocol;daemonizer;mnemonics;epee;lmdb;device;wallet-crypto; 33 | blockchain_db;ringct;wallet;cncrypto;easylogging;version; 34 | checkpoints;randomx;hardforks;miniupnpc) 35 | 36 | set(Xmr_INCLUDE_DIRS "${CPP_MONERO_DIR}") 37 | 38 | # if the project is a subset of main cpp-ethereum project 39 | # use same pattern for variables as Boost uses 40 | 41 | foreach (l ${LIBS}) 42 | 43 | string(TOUPPER ${l} L) 44 | 45 | find_library(Xmr_${L}_LIBRARY 46 | NAMES ${l} 47 | PATHS ${CMAKE_LIBRARY_PATH} 48 | PATH_SUFFIXES "/src/${l}" "/src/" "/external/db_drivers/lib${l}" "/lib" "/src/crypto" "/src/crypto/wallet" "/contrib/epee/src" "/external/easylogging++/" "/external/${l}" "external/miniupnp/miniupnpc" 49 | NO_DEFAULT_PATH 50 | ) 51 | 52 | set(Xmr_${L}_LIBRARIES ${Xmr_${L}_LIBRARY}) 53 | 54 | message(STATUS FindMonero " Xmr_${L}_LIBRARIES ${Xmr_${L}_LIBRARY}") 55 | 56 | if(NOT "${Xmr_${L}_LIBRARIES}" STREQUAL "${Xmr_${L}_LIBRARY-NOTFOUND}") 57 | add_library(${l} STATIC IMPORTED) 58 | set_property(TARGET ${l} PROPERTY IMPORTED_LOCATION ${Xmr_${L}_LIBRARIES}) 59 | endif() 60 | 61 | endforeach() 62 | 63 | if (EXISTS ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a) 64 | message(STATUS FindMonero " found libringct_basic.a") 65 | add_library(ringct_basic STATIC IMPORTED) 66 | set_property(TARGET ringct_basic 67 | PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/src/ringct/libringct_basic.a) 68 | endif() 69 | 70 | if (EXISTS ${MONERO_BUILD_DIR}/src/cryptonote_basic/libcryptonote_format_utils_basic.a) 71 | message(STATUS FindMonero " found libcryptonote_format_utils_basic.a") 72 | add_library(cryptonote_format_utils_basic STATIC IMPORTED) 73 | set_property(TARGET cryptonote_format_utils_basic 74 | PROPERTY IMPORTED_LOCATION ${MONERO_BUILD_DIR}/src/cryptonote_basic/libcryptonote_format_utils_basic.a) 75 | endif() 76 | 77 | 78 | message(STATUS ${MONERO_SOURCE_DIR}/build) 79 | 80 | # include monero headers 81 | include_directories( 82 | ${MONERO_SOURCE_DIR}/src 83 | ${MONERO_SOURCE_DIR}/src/crypto 84 | ${MONERO_SOURCE_DIR}/src/crypto/wallet 85 | ${MONERO_SOURCE_DIR}/external 86 | ${MONERO_SOURCE_DIR}/external/randomx/src 87 | ${MONERO_SOURCE_DIR}/build 88 | ${MONERO_SOURCE_DIR}/external/easylogging++ 89 | ${MONERO_SOURCE_DIR}/contrib/epee/include 90 | ${MONERO_SOURCE_DIR}/external/db_drivers/liblmdb 91 | ${MONERO_SOURCE_DIR}/generated_include/crypto/wallet) 92 | -------------------------------------------------------------------------------- /cmake/FindSanitizers.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 4 | # 2013 Matthew Arsenault 5 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # If any of the used compiler is a GNU compiler, add a second option to static 26 | # link against the sanitizers. 27 | option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) 28 | 29 | 30 | 31 | 32 | set(FIND_QUIETLY_FLAG "") 33 | if (DEFINED Sanitizers_FIND_QUIETLY) 34 | set(FIND_QUIETLY_FLAG "QUIET") 35 | endif () 36 | 37 | find_package(ASan ${FIND_QUIETLY_FLAG}) 38 | find_package(TSan ${FIND_QUIETLY_FLAG}) 39 | find_package(MSan ${FIND_QUIETLY_FLAG}) 40 | find_package(UBSan ${FIND_QUIETLY_FLAG}) 41 | 42 | 43 | 44 | 45 | function(sanitizer_add_blacklist_file FILE) 46 | if(NOT IS_ABSOLUTE ${FILE}) 47 | set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") 48 | endif() 49 | get_filename_component(FILE "${FILE}" REALPATH) 50 | 51 | sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" 52 | "SanitizerBlacklist" "SanBlist") 53 | endfunction() 54 | 55 | function(add_sanitizers ...) 56 | # If no sanitizer is enabled, return immediately. 57 | if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR 58 | SANITIZE_UNDEFINED)) 59 | return() 60 | endif () 61 | 62 | foreach (TARGET ${ARGV}) 63 | # Check if this target will be compiled by exactly one compiler. Other- 64 | # wise sanitizers can't be used and a warning should be printed once. 65 | sanitizer_target_compilers(${TARGET} TARGET_COMPILER) 66 | list(LENGTH TARGET_COMPILER NUM_COMPILERS) 67 | if (NUM_COMPILERS GREATER 1) 68 | message(WARNING "Can't use any sanitizers for target ${TARGET}, " 69 | "because it will be compiled by incompatible compilers. " 70 | "Target will be compiled without sanitizers.") 71 | return() 72 | 73 | # If the target is compiled by no known compiler, ignore it. 74 | elseif (NUM_COMPILERS EQUAL 0) 75 | message(WARNING "Can't use any sanitizers for target ${TARGET}, " 76 | "because it uses an unknown compiler. Target will be " 77 | "compiled without sanitizers.") 78 | return() 79 | endif () 80 | 81 | # Add sanitizers for target. 82 | add_sanitize_address(${TARGET}) 83 | add_sanitize_thread(${TARGET}) 84 | add_sanitize_memory(${TARGET}) 85 | add_sanitize_undefined(${TARGET}) 86 | endforeach () 87 | endfunction(add_sanitizers) 88 | -------------------------------------------------------------------------------- /cmake/FindTSan.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 4 | # 2013 Matthew Arsenault 5 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) 26 | 27 | set(FLAG_CANDIDATES 28 | "-g -fsanitize=thread" 29 | ) 30 | 31 | 32 | # ThreadSanitizer is not compatible with MemorySanitizer. 33 | if (SANITIZE_THREAD AND SANITIZE_MEMORY) 34 | message(FATAL_ERROR "ThreadSanitizer is not compatible with " 35 | "MemorySanitizer.") 36 | endif () 37 | 38 | 39 | include(sanitize-helpers) 40 | 41 | if (SANITIZE_THREAD) 42 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND 43 | NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 44 | message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " 45 | "ThreadSanitizer is supported for Linux systems and macOS only.") 46 | set(SANITIZE_THREAD Off CACHE BOOL 47 | "Enable ThreadSanitizer for sanitized targets." FORCE) 48 | elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) 49 | message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " 50 | "ThreadSanitizer is supported for 64bit systems only.") 51 | set(SANITIZE_THREAD Off CACHE BOOL 52 | "Enable ThreadSanitizer for sanitized targets." FORCE) 53 | else () 54 | sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" 55 | "TSan") 56 | endif () 57 | endif () 58 | 59 | function (add_sanitize_thread TARGET) 60 | if (NOT SANITIZE_THREAD) 61 | return() 62 | endif () 63 | 64 | sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") 65 | endfunction () 66 | -------------------------------------------------------------------------------- /cmake/FindUBSan.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 4 | # 2013 Matthew Arsenault 5 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | option(SANITIZE_UNDEFINED 26 | "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) 27 | 28 | set(FLAG_CANDIDATES 29 | "-g -fsanitize=undefined" 30 | ) 31 | 32 | 33 | include(sanitize-helpers) 34 | 35 | if (SANITIZE_UNDEFINED) 36 | sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" 37 | "UndefinedBehaviorSanitizer" "UBSan") 38 | endif () 39 | 40 | function (add_sanitize_undefined TARGET) 41 | if (NOT SANITIZE_UNDEFINED) 42 | return() 43 | endif () 44 | 45 | sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") 46 | endfunction () 47 | -------------------------------------------------------------------------------- /cmake/MyUtils.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(configure_files srcDir destDir) 3 | message(STATUS "Configuring directory ${destDir}") 4 | make_directory(${destDir}) 5 | 6 | file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*) 7 | foreach(templateFile ${templateFiles}) 8 | set(srcTemplatePath ${srcDir}/${templateFile}) 9 | if(NOT IS_DIRECTORY ${srcTemplatePath}) 10 | message(STATUS "Configuring file ${templateFile}") 11 | configure_file( 12 | ${srcTemplatePath} 13 | ${destDir}/${templateFile} 14 | @ONLY) 15 | endif(NOT IS_DIRECTORY ${srcTemplatePath}) 16 | endforeach(templateFile) 17 | endmacro(configure_files) 18 | 19 | macro(create_git_version) 20 | # Get the current working branch 21 | execute_process( 22 | COMMAND git rev-parse --abbrev-ref HEAD 23 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 24 | OUTPUT_VARIABLE GIT_BRANCH 25 | OUTPUT_STRIP_TRAILING_WHITESPACE 26 | ) 27 | 28 | # http://xit0.org/2013/04/cmake-use-git-branch-and-commit-details-in-project/ 29 | # Get the latest abbreviated commit hash of the working branch 30 | execute_process( 31 | COMMAND git log -1 --format=%h 32 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 33 | OUTPUT_VARIABLE GIT_COMMIT_HASH 34 | OUTPUT_STRIP_TRAILING_WHITESPACE 35 | ) 36 | 37 | # Get the date and time of last commit 38 | execute_process( 39 | COMMAND git log -1 --format=%cd --date=short 40 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 41 | OUTPUT_VARIABLE GIT_COMMIT_DATETIME 42 | OUTPUT_STRIP_TRAILING_WHITESPACE 43 | ) 44 | 45 | # Get current branch name 46 | execute_process( 47 | COMMAND git rev-parse --abbrev-ref HEAD 48 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 49 | OUTPUT_VARIABLE GIT_BRANCH_NAME 50 | OUTPUT_STRIP_TRAILING_WHITESPACE 51 | ) 52 | 53 | 54 | 55 | configure_file( 56 | ${CMAKE_SOURCE_DIR}/src/version.h.in 57 | ${CMAKE_BINARY_DIR}/gen/version.h 58 | ) 59 | 60 | include_directories(${CMAKE_BINARY_DIR}/gen) 61 | 62 | endmacro(create_git_version) -------------------------------------------------------------------------------- /cmake/asan-wrapper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # The MIT License (MIT) 4 | # 5 | # Copyright (c) 6 | # 2013 Matthew Arsenault 7 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 8 | # 9 | # Permission is hereby granted, free of charge, to any person obtaining a copy 10 | # of this software and associated documentation files (the "Software"), to deal 11 | # in the Software without restriction, including without limitation the rights 12 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | # copies of the Software, and to permit persons to whom the Software is 14 | # furnished to do so, subject to the following conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be included in all 17 | # copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | # SOFTWARE. 26 | 27 | # This script is a wrapper for AddressSanitizer. In some special cases you need 28 | # to preload AddressSanitizer to avoid error messages - e.g. if you're 29 | # preloading another library to your application. At the moment this script will 30 | # only do something, if we're running on a Linux platform. OSX might not be 31 | # affected. 32 | 33 | 34 | # Exit immediately, if platform is not Linux. 35 | if [ "$(uname)" != "Linux" ] 36 | then 37 | exec $@ 38 | fi 39 | 40 | 41 | # Get the used libasan of the application ($1). If a libasan was found, it will 42 | # be prepended to LD_PRELOAD. 43 | libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) 44 | if [ -n "$libasan" ] 45 | then 46 | if [ -n "$LD_PRELOAD" ] 47 | then 48 | export LD_PRELOAD="$libasan:$LD_PRELOAD" 49 | else 50 | export LD_PRELOAD="$libasan" 51 | fi 52 | fi 53 | 54 | # Execute the application. 55 | exec $@ 56 | -------------------------------------------------------------------------------- /cmake/sanitize-helpers.cmake: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 4 | # 2013 Matthew Arsenault 5 | # 2015-2016 RWTH Aachen University, Federal Republic of Germany 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # Helper function to get the language of a source file. 26 | function (sanitizer_lang_of_source FILE RETURN_VAR) 27 | get_filename_component(FILE_EXT "${FILE}" EXT) 28 | string(TOLOWER "${FILE_EXT}" FILE_EXT) 29 | string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) 30 | 31 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 32 | foreach (LANG ${ENABLED_LANGUAGES}) 33 | list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) 34 | if (NOT ${TEMP} EQUAL -1) 35 | set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) 36 | return() 37 | endif () 38 | endforeach() 39 | 40 | set(${RETURN_VAR} "" PARENT_SCOPE) 41 | endfunction () 42 | 43 | 44 | # Helper function to get compilers used by a target. 45 | function (sanitizer_target_compilers TARGET RETURN_VAR) 46 | # Check if all sources for target use the same compiler. If a target uses 47 | # e.g. C and Fortran mixed and uses different compilers (e.g. clang and 48 | # gfortran) this can trigger huge problems, because different compilers may 49 | # use different implementations for sanitizers. 50 | set(BUFFER "") 51 | get_target_property(TSOURCES ${TARGET} SOURCES) 52 | foreach (FILE ${TSOURCES}) 53 | # If expression was found, FILE is a generator-expression for an object 54 | # library. Object libraries will be ignored. 55 | string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) 56 | if ("${_file}" STREQUAL "") 57 | sanitizer_lang_of_source(${FILE} LANG) 58 | if (LANG) 59 | list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) 60 | endif () 61 | endif () 62 | endforeach () 63 | 64 | list(REMOVE_DUPLICATES BUFFER) 65 | set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) 66 | endfunction () 67 | 68 | 69 | # Helper function to check compiler flags for language compiler. 70 | function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) 71 | if (${LANG} STREQUAL "C") 72 | include(CheckCCompilerFlag) 73 | check_c_compiler_flag("${FLAG}" ${VARIABLE}) 74 | 75 | elseif (${LANG} STREQUAL "CXX") 76 | include(CheckCXXCompilerFlag) 77 | check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) 78 | 79 | elseif (${LANG} STREQUAL "Fortran") 80 | # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible 81 | # with older Cmake versions, we will check if this module is present 82 | # before we use it. Otherwise we will define Fortran coverage support as 83 | # not available. 84 | include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) 85 | if (INCLUDED) 86 | check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) 87 | elseif (NOT CMAKE_REQUIRED_QUIET) 88 | message(STATUS "Performing Test ${VARIABLE}") 89 | message(STATUS "Performing Test ${VARIABLE}" 90 | " - Failed (Check not supported)") 91 | endif () 92 | endif() 93 | endfunction () 94 | 95 | 96 | # Helper function to test compiler flags. 97 | function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) 98 | set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) 99 | 100 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) 101 | foreach (LANG ${ENABLED_LANGUAGES}) 102 | # Sanitizer flags are not dependend on language, but the used compiler. 103 | # So instead of searching flags foreach language, search flags foreach 104 | # compiler used. 105 | set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) 106 | if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) 107 | foreach (FLAG ${FLAG_CANDIDATES}) 108 | if(NOT CMAKE_REQUIRED_QUIET) 109 | message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") 110 | endif() 111 | 112 | set(CMAKE_REQUIRED_FLAGS "${FLAG}") 113 | unset(${PREFIX}_FLAG_DETECTED CACHE) 114 | sanitizer_check_compiler_flag("${FLAG}" ${LANG} 115 | ${PREFIX}_FLAG_DETECTED) 116 | 117 | if (${PREFIX}_FLAG_DETECTED) 118 | # If compiler is a GNU compiler, search for static flag, if 119 | # SANITIZE_LINK_STATIC is enabled. 120 | if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) 121 | string(TOLOWER ${PREFIX} PREFIX_lower) 122 | sanitizer_check_compiler_flag( 123 | "-static-lib${PREFIX_lower}" ${LANG} 124 | ${PREFIX}_STATIC_FLAG_DETECTED) 125 | 126 | if (${PREFIX}_STATIC_FLAG_DETECTED) 127 | set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") 128 | endif () 129 | endif () 130 | 131 | set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING 132 | "${NAME} flags for ${COMPILER} compiler.") 133 | mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) 134 | break() 135 | endif () 136 | endforeach () 137 | 138 | if (NOT ${PREFIX}_FLAG_DETECTED) 139 | set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING 140 | "${NAME} flags for ${COMPILER} compiler.") 141 | mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) 142 | 143 | message(WARNING "${NAME} is not available for ${COMPILER} " 144 | "compiler. Targets using this compiler will be " 145 | "compiled without ${NAME}.") 146 | endif () 147 | endif () 148 | endforeach () 149 | endfunction () 150 | 151 | 152 | # Helper to assign sanitizer flags for TARGET. 153 | function (sanitizer_add_flags TARGET NAME PREFIX) 154 | # Get list of compilers used by target and check, if sanitizer is available 155 | # for this target. Other compiler checks like check for conflicting 156 | # compilers will be done in add_sanitizers function. 157 | sanitizer_target_compilers(${TARGET} TARGET_COMPILER) 158 | list(LENGTH TARGET_COMPILER NUM_COMPILERS) 159 | if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") 160 | return() 161 | endif() 162 | 163 | # Set compile- and link-flags for target. 164 | set_property(TARGET ${TARGET} APPEND_STRING 165 | PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") 166 | set_property(TARGET ${TARGET} APPEND_STRING 167 | PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") 168 | set_property(TARGET ${TARGET} APPEND_STRING 169 | PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") 170 | endfunction () 171 | -------------------------------------------------------------------------------- /ext/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # first build mstch template library 2 | cmake_minimum_required(VERSION 3.0.2) 3 | add_subdirectory("mstch") 4 | 5 | 6 | # now build myext library from other files 7 | project(myext) 8 | 9 | set(SOURCE_HEADERS 10 | minicsv.h 11 | fmt/format.h) 12 | 13 | set(SOURCE_FILES 14 | fmt/format.cc 15 | fmt/ostream.cc) 16 | 17 | # make static library called libmyxrm 18 | # that we are going to link to 19 | # in the root CMakeLists.txt file 20 | add_library(myext 21 | STATIC 22 | ${SOURCE_FILES}) 23 | 24 | 25 | -------------------------------------------------------------------------------- /ext/fmt/ostream.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ - std::ostream support 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | #include "ostream.h" 11 | 12 | namespace fmt { 13 | 14 | namespace internal { 15 | FMT_FUNC void write(std::ostream &os, Writer &w) { 16 | const char *data = w.data(); 17 | typedef internal::MakeUnsigned::Type UnsignedStreamSize; 18 | UnsignedStreamSize size = w.size(); 19 | UnsignedStreamSize max_size = 20 | internal::to_unsigned((std::numeric_limits::max)()); 21 | do { 22 | UnsignedStreamSize n = size <= max_size ? size : max_size; 23 | os.write(data, static_cast(n)); 24 | data += n; 25 | size -= n; 26 | } while (size != 0); 27 | } 28 | } 29 | 30 | FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { 31 | MemoryWriter w; 32 | w.write(format_str, args); 33 | internal::write(os, w); 34 | } 35 | } // namespace fmt 36 | -------------------------------------------------------------------------------- /ext/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | /* 2 | Formatting library for C++ - std::ostream support 3 | 4 | Copyright (c) 2012 - 2016, Victor Zverovich 5 | All rights reserved. 6 | 7 | For the license information refer to format.h. 8 | */ 9 | 10 | #ifndef FMT_OSTREAM_H_ 11 | #define FMT_OSTREAM_H_ 12 | 13 | #include "format.h" 14 | #include 15 | 16 | namespace fmt { 17 | 18 | namespace internal { 19 | 20 | template 21 | class FormatBuf : public std::basic_streambuf { 22 | private: 23 | typedef typename std::basic_streambuf::int_type int_type; 24 | typedef typename std::basic_streambuf::traits_type traits_type; 25 | 26 | Buffer &buffer_; 27 | Char *start_; 28 | 29 | public: 30 | FormatBuf(Buffer &buffer) : buffer_(buffer), start_(&buffer[0]) { 31 | this->setp(start_, start_ + buffer_.capacity()); 32 | } 33 | 34 | FormatBuf(Buffer &buffer, Char *start) : buffer_(buffer) , start_(start) { 35 | this->setp(start_, start_ + buffer_.capacity()); 36 | } 37 | 38 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 39 | if (!traits_type::eq_int_type(ch, traits_type::eof())) { 40 | size_t buf_size = size(); 41 | buffer_.resize(buf_size); 42 | buffer_.reserve(buf_size * 2); 43 | 44 | start_ = &buffer_[0]; 45 | start_[buf_size] = traits_type::to_char_type(ch); 46 | this->setp(start_+ buf_size + 1, start_ + buf_size * 2); 47 | } 48 | return ch; 49 | } 50 | 51 | size_t size() const { 52 | return to_unsigned(this->pptr() - start_); 53 | } 54 | }; 55 | 56 | Yes &convert(std::ostream &); 57 | 58 | struct DummyStream : std::ostream { 59 | DummyStream(); // Suppress a bogus warning in MSVC. 60 | // Hide all operator<< overloads from std::ostream. 61 | void operator<<(Null<>); 62 | }; 63 | 64 | No &operator<<(std::ostream &, int); 65 | 66 | template 67 | struct ConvertToIntImpl { 68 | // Convert to int only if T doesn't have an overloaded operator<<. 69 | enum { 70 | value = sizeof(convert(get() << get())) == sizeof(No) 71 | }; 72 | }; 73 | 74 | // Write the content of w to os. 75 | void write(std::ostream &os, Writer &w); 76 | 77 | #if FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES 78 | template 79 | class is_streamable { 80 | template 81 | static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); 82 | 83 | template 84 | static auto test(...) -> std::false_type; 85 | 86 | public: 87 | static constexpr bool value = decltype(test(0))::value; 88 | }; 89 | #endif 90 | } // namespace internal 91 | 92 | // Formats a value. 93 | template 94 | void format_arg(BasicFormatter &f, 95 | const Char *&format_str, const T &value) { 96 | internal::MemoryBuffer buffer; 97 | 98 | internal::FormatBuf format_buf(buffer); 99 | std::basic_ostream output(&format_buf); 100 | output << value; 101 | 102 | BasicStringRef str(&buffer[0], format_buf.size()); 103 | typedef internal::MakeArg< BasicFormatter > MakeArg; 104 | format_str = f.format(format_str, MakeArg(str)); 105 | } 106 | 107 | /** 108 | \rst 109 | Prints formatted data to the stream *os*. 110 | 111 | **Example**:: 112 | 113 | print(cerr, "Don't {}!", "panic"); 114 | \endrst 115 | */ 116 | FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); 117 | FMT_VARIADIC(void, print, std::ostream &, CStringRef) 118 | 119 | #if __cplusplus >= 201103L 120 | template 121 | typename std::enable_if< 122 | !std::is_same< 123 | typename std::remove_cv::type>::type, 124 | char * 125 | >::value, 126 | BasicWriter& 127 | >::type 128 | operator<<(BasicWriter &writer, const T &value) { 129 | FMT_STATIC_ASSERT(internal::is_streamable::value, "T must be Streamable"); 130 | 131 | auto &buffer = writer.buffer(); 132 | Char *start = &buffer[0] + buffer.size(); 133 | 134 | internal::FormatBuf format_buf(buffer, start); 135 | std::basic_ostream output(&format_buf); 136 | output << value; 137 | 138 | buffer.resize(buffer.size() + format_buf.size()); 139 | return writer; 140 | } 141 | #endif 142 | } // namespace fmt 143 | 144 | #ifdef FMT_HEADER_ONLY 145 | # include "ostream.cc" 146 | #endif 147 | 148 | #endif // FMT_OSTREAM_H_ 149 | -------------------------------------------------------------------------------- /ext/mstch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | project(mstch) 3 | 4 | option(WITH_UNIT_TESTS "enable building unit test executable" OFF) 5 | option(WITH_BENCHMARK "enable building benchmark executable" OFF) 6 | 7 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 8 | set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) 9 | set(CMAKE_BUILD_TYPE Debug) 10 | 11 | set(mstch_VERSION 1.0.1) 12 | 13 | if(NOT MSVC) 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -O3") 15 | endif() 16 | 17 | add_subdirectory(src) 18 | -------------------------------------------------------------------------------- /ext/mstch/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Daniel Sipka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /ext/mstch/README.md: -------------------------------------------------------------------------------- 1 | # mstch - {{mustache}} templates in C++11 2 | 3 | ![mstch logo](http://i.imgur.com/MRyStO5.png) 4 | 5 | mstch is a complete implementation of [{{mustache}}](http://mustache.github.io/) 6 | templates using modern C++. It's compliant with [specifications](https://github.com/mustache/spec) 7 | v1.1.3, including the lambda module. 8 | 9 | [![Try it online](https://img.shields.io/badge/try%20it-online-blue.svg)](http://melpon.org/wandbox/permlink/EqyOe7IBRYPGVk5f) 10 | [![GitHub version](https://badge.fury.io/gh/no1msd%2Fmstch.svg)](http://badge.fury.io/gh/no1msd%2Fmstch) 11 | [![Build Status](https://travis-ci.org/no1msd/mstch.svg?branch=master)](https://travis-ci.org/no1msd/mstch) 12 | [![Build status](https://ci.appveyor.com/api/projects/status/d6mxp0uba5646x16?svg=true)](https://ci.appveyor.com/project/no1msd/mstch) 13 | 14 | ## Supported features 15 | 16 | mstch supports the complete feature set described in the `mustache(5)` [manpage](http://mustache.github.com/mustache.5.html): 17 | 18 | - JSON-like data structure using [Boost.Variant](http://www.boost.org/libs/variant) 19 | - variables, sections, inverted sections 20 | - partials 21 | - changing the delimiter 22 | - C++11 lambdas 23 | - C++ objects as view models 24 | 25 | ## Basic usage 26 | 27 | ```c++ 28 | #include 29 | #include 30 | 31 | int main() { 32 | std::string view{"{{#names}}Hi {{name}}!\n{{/names}}"}; 33 | mstch::map context{ 34 | {"names", mstch::array{ 35 | mstch::map{{"name", std::string{"Chris"}}}, 36 | mstch::map{{"name", std::string{"Mark"}}}, 37 | mstch::map{{"name", std::string{"Scott"}}}, 38 | }} 39 | }; 40 | 41 | std::cout << mstch::render(view, context) << std::endl; 42 | 43 | return 0; 44 | } 45 | 46 | ``` 47 | 48 | The output of this example will be: 49 | 50 | ```html 51 | Hi Chris! 52 | Hi Mark! 53 | Hi Scott! 54 | ``` 55 | 56 | ### Data structure 57 | 58 | The types in the example above, `mstch::array` and `mstch::map` are actually 59 | aliases for standard types: 60 | 61 | ```c++ 62 | using map = std::map; 63 | using array = std::vector; 64 | ``` 65 | 66 | `mstch::node` is a `boost::variant` that can hold a `std::string`, `int`, 67 | `double`, `bool`, `mstch::lambda` or a `std::shared_ptr` 68 | (see below), also a map or an array recursively. Essentially it works just like 69 | a JSON object. 70 | 71 | Note that when using a `std::string` as value you must explicitly specify the 72 | type, since a `const char*` literal like `"foobar"` would be implicitly 73 | converted to `bool`. Alternatively you can use [C++14 string_literals](http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s) 74 | if your compiler supports it. 75 | 76 | ## Advanced usage 77 | 78 | ### Partials 79 | 80 | Partials can be passed in a `std::map` as the third parameter of the 81 | `mstch::render` function: 82 | 83 | ```c++ 84 | std::string view{"{{#names}}{{> user}}{{/names}}"}; 85 | std::string user_view{"{{name}}\n"}; 86 | mstch::map context{ 87 | {"names", mstch::array{ 88 | mstch::map{{"name", std::string{"Chris"}}}, 89 | mstch::map{{"name", std::string{"Mark"}}}, 90 | mstch::map{{"name", std::string{"Scott"}}}, 91 | }} 92 | }; 93 | 94 | std::cout << mstch::render(view, context, {{"user", user_view}}) << std::endl; 95 | ``` 96 | 97 | Output: 98 | 99 | ```html 100 | Chris 101 | Mark 102 | Scott 103 | ``` 104 | 105 | ### Lambdas 106 | 107 | C++11 lambda expressions can be used to add logic to your templates. Like a 108 | `const char*` literal, lambdas can be implicitly converted to `bool`, so they 109 | must be wrapped in a `mstch::lambda` object when used in a `mstch::node`. The 110 | lambda expression passed to `mstch::lambda` must itself return a `mstch::node`. 111 | The returned node will be rendered to a string, then it will be parsed as a 112 | template. 113 | 114 | The lambda expression accepts either no parameters: 115 | 116 | ```c++ 117 | std::string view{"Hello {{lambda}}!"}; 118 | mstch::map context{ 119 | {"lambda", mstch::lambda{[]() -> mstch::node { 120 | return std::string{"World"}; 121 | }}} 122 | }; 123 | 124 | std::cout << mstch::render(view, context) << std::endl; 125 | ``` 126 | 127 | Output: 128 | 129 | ```html 130 | Hello World! 131 | ``` 132 | 133 | Or it accepts a `const std::string&` that gets the unrendered literal block: 134 | 135 | ```c++ 136 | std::string view{"{{#bold}}{{yay}} :){{/bold}}"}; 137 | mstch::map context{ 138 | {"yay", std::string{"Yay!"}}, 139 | {"bold", mstch::lambda{[](const std::string& text) -> mstch::node { 140 | return "" + text + ""; 141 | }}} 142 | }; 143 | 144 | std::cout << mstch::render(view, context) << std::endl; 145 | ``` 146 | 147 | Output: 148 | 149 | ```html 150 | Yay! :) 151 | ``` 152 | 153 | ### Objects 154 | 155 | Custom objects can also be used as context for rendering templates. The class 156 | must inherit from `mstch::object`, and register it's exported methods with 157 | `register_methods`. Exported methods must have the return type of `mstch::node`. 158 | Objects must be created as a `std::shared_ptr`. 159 | 160 | ```c++ 161 | class example: public mstch::object { 162 | public: 163 | example(): m_value(1) { 164 | register_methods(this, { 165 | {"count", &example::count}, 166 | {"names", &example::names} 167 | }); 168 | } 169 | 170 | mstch::node count() { 171 | return m_value++; 172 | } 173 | 174 | mstch::node names() { 175 | return mstch::array{ 176 | std::string{"Chris"}, std::string{"Mark"}, std::string{"Scott"}}; 177 | } 178 | 179 | private: 180 | int m_value; 181 | }; 182 | 183 | std::string view{"{{#names}}{{count}}: {{.}}\n{{/names}}"}; 184 | const auto context = std::make_shared(); 185 | 186 | std::cout << mstch::render(view, context) << std::endl; 187 | ``` 188 | 189 | Output: 190 | 191 | ```html 192 | 1: Chris 193 | 2: Mark 194 | 3: Scott 195 | ``` 196 | 197 | ### Custom escape function 198 | 199 | By default, mstch uses HTML escaping on the output, as per specification. This 200 | is not useful if your output is not HTML, so mstch provides a way to supply 201 | your own escape implementation. Just assign any callable object to the static 202 | `mstch::config::escape`, which is an initially empty 203 | `std::function`. 204 | 205 | For example you can turn off escaping entirely with a lambda: 206 | 207 | ```c++ 208 | mstch::config::escape = [](const std::string& str) -> std::string { 209 | return str; 210 | }; 211 | ``` 212 | 213 | ## Requirements 214 | 215 | - A C++ compiler with decent C++11 support. Currently tested with: 216 | - GCC 4.7, 4.8, 4.9, 5.1 217 | - clang 3.5, 3.6, 3.7 (both libstdc++ and libc++ are supported) 218 | - MSVC 2013, 2015 219 | - Boost 1.54+ for [Boost.Variant](http://www.boost.org/libs/variant) 220 | - CMake 3.0+ for building 221 | 222 | ## Using mstch in your project 223 | 224 | If you are using CMake, the easiest way to include mstch in your project is to 225 | copy the whole directory to your source tree, and use `add_subdirectory` in your 226 | CMakeLists.txt. This will set a variable named `mstch_INCLUDE_DIR` that contains 227 | its include path, and add a static library target named `mstch`. For example: 228 | 229 | ```cmake 230 | add_subdirectory(external/mstch) 231 | include_directories(${mstch_INCLUDE_DIR}) 232 | target_link_libraries(your_project mstch) 233 | ``` 234 | 235 | If you prefer to install the library globally, you can simply do the following 236 | from the root of the source tree: 237 | 238 | ```bash 239 | $ mkdir build 240 | $ cd build 241 | $ cmake .. 242 | $ make 243 | $ make install 244 | ``` 245 | 246 | The install command may require root privileges. This will also install CMake 247 | config files, so you can use use `find_package` in your CMakeLists.txt: 248 | 249 | ```cmake 250 | find_package(mstch) 251 | target_link_libraries(your_project mstch::mstch) 252 | ``` 253 | 254 | ## Running the unit tests 255 | 256 | Unit tests are using the [Catch](https://github.com/philsquared/Catch) framework 257 | and [rapidjson](http://rapidjson.org/) to parse the 258 | [Mustache specifications](https://github.com/mustache/spec), all of which are 259 | included in the repository as git submodules. Various 260 | [Boost](http://www.boost.org/) libraries are also required to build them. 261 | 262 | Don't forget to initialize submodules: 263 | 264 | ```bash 265 | $ git submodule init 266 | $ git submodule update 267 | ``` 268 | 269 | To build and run the unit tests: 270 | 271 | ```bash 272 | $ mkdir build 273 | $ cd build 274 | $ cmake -DWITH_UNIT_TESTS=ON .. 275 | $ make 276 | $ make test 277 | ``` 278 | 279 | ## License 280 | 281 | mstch is licensed under the [MIT license](https://github.com/no1msd/mstch/blob/master/LICENSE). 282 | -------------------------------------------------------------------------------- /ext/mstch/cmake/mstch-config.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/mstch-targets.cmake") -------------------------------------------------------------------------------- /ext/mstch/include/mstch/mstch.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace mstch { 12 | 13 | struct config { 14 | static std::function escape; 15 | }; 16 | 17 | namespace internal { 18 | 19 | template 20 | class object_t { 21 | public: 22 | const N& at(const std::string& name) const { 23 | cache[name] = (methods.at(name))(); 24 | return cache[name]; 25 | } 26 | 27 | bool has(const std::string name) const { 28 | return methods.count(name) != 0; 29 | } 30 | 31 | protected: 32 | template 33 | void register_methods(S* s, std::map methods) { 34 | for(auto& item: methods) 35 | this->methods.insert({item.first, std::bind(item.second, s)}); 36 | } 37 | 38 | private: 39 | std::map> methods; 40 | mutable std::map cache; 41 | }; 42 | 43 | template 44 | class is_fun { 45 | private: 46 | using not_fun = char; 47 | using fun_without_args = char[2]; 48 | using fun_with_args = char[3]; 49 | template struct really_has; 50 | template static fun_without_args& test( 51 | really_has*); 52 | template static fun_with_args& test( 53 | really_has*); 55 | template static not_fun& test(...); 56 | 57 | public: 58 | static bool const no_args = sizeof(test(0)) == sizeof(fun_without_args); 59 | static bool const has_args = sizeof(test(0)) == sizeof(fun_with_args); 60 | }; 61 | 62 | template 63 | using node_renderer = std::function; 64 | 65 | template 66 | class lambda_t { 67 | public: 68 | template 69 | lambda_t(F f, typename std::enable_if::no_args>::type* = 0): 70 | fun([f](node_renderer renderer, const std::string&) { 71 | return renderer(f()); 72 | }) 73 | { 74 | } 75 | 76 | template 77 | lambda_t(F f, typename std::enable_if::has_args>::type* = 0): 78 | fun([f](node_renderer renderer, const std::string& text) { 79 | return renderer(f(text)); 80 | }) 81 | { 82 | } 83 | 84 | std::string operator()(node_renderer renderer, 85 | const std::string& text = "") const 86 | { 87 | return fun(renderer, text); 88 | } 89 | 90 | private: 91 | std::function renderer, const std::string&)> fun; 92 | }; 93 | 94 | template 95 | struct map : public std::map 96 | { 97 | map() {} 98 | map(const map& rhs) : std::map(rhs) {} 99 | map(const std::initializer_list::value_type>& args) : std::map(args) {} 100 | map& operator=(const map& rhs) 101 | { 102 | std::map::clear(); 103 | for (auto& i : rhs) 104 | std::map::insert(i); 105 | return *this; 106 | } 107 | }; 108 | 109 | } 110 | 111 | using node = boost::make_recursive_variant< 112 | std::nullptr_t, std::string, int, double, bool, uint64_t, int64_t, uint32_t, 113 | internal::lambda_t, 114 | std::shared_ptr>, 115 | internal::map, 116 | std::vector>::type; 117 | using object = internal::object_t; 118 | using lambda = internal::lambda_t; 119 | using map = internal::map; 120 | using array = std::vector; 121 | 122 | std::string render( 123 | const std::string& tmplt, 124 | const node& root, 125 | const std::map& partials = 126 | std::map()); 127 | 128 | } 129 | -------------------------------------------------------------------------------- /ext/mstch/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Boost 1.54 REQUIRED) 2 | 3 | set(mstch_INCLUDE_DIR 4 | ${PROJECT_SOURCE_DIR}/include CACHE STRING "mstch include directory") 5 | 6 | # /home/mwo/crow-monero-test/ext/mstch 7 | message(${PROJECT_SOURCE_DIR}) 8 | # 9 | include_directories( 10 | ${Boost_INCLUDE_DIR}) 11 | 12 | set(SRC 13 | state/in_section.cpp 14 | state/outside_section.cpp 15 | state/render_state.hpp 16 | visitor/get_token.hpp 17 | visitor/has_token.hpp 18 | visitor/is_node_empty.hpp 19 | visitor/render_node.hpp 20 | visitor/render_section.hpp 21 | mstch.cpp 22 | render_context.cpp 23 | template_type.cpp 24 | token.cpp 25 | utils.cpp) 26 | 27 | add_library(mstch STATIC ${SRC}) 28 | # 29 | set_property(TARGET mstch PROPERTY VERSION ${mstch_VERSION}) 30 | # 31 | #install( 32 | # TARGETS mstch EXPORT mstchTargets 33 | # LIBRARY DESTINATION lib 34 | # ARCHIVE DESTINATION lib) 35 | # 36 | #install( 37 | # FILES "${PROJECT_SOURCE_DIR}/include/mstch/mstch.hpp" 38 | # DESTINATION include/mstch 39 | # COMPONENT Devel) 40 | # 41 | #include(CMakePackageConfigHelpers) 42 | #write_basic_package_version_file( 43 | # "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake" 44 | # VERSION ${mstch_VERSION} 45 | # COMPATIBILITY AnyNewerVersion) 46 | # 47 | #export( 48 | # EXPORT mstchTargets 49 | # FILE "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-targets.cmake" 50 | # NAMESPACE mstch::) 51 | # 52 | #configure_file( 53 | # "${PROJECT_SOURCE_DIR}/cmake/mstch-config.cmake" 54 | # "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config.cmake") 55 | # 56 | #install( 57 | # EXPORT mstchTargets 58 | # FILE mstch-targets.cmake 59 | # NAMESPACE mstch:: 60 | # DESTINATION lib/cmake/mstch) 61 | # 62 | #install(FILES 63 | # "${PROJECT_SOURCE_DIR}/cmake/mstch-config.cmake" 64 | # "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake" 65 | # DESTINATION lib/cmake/mstch 66 | # COMPONENT Devel) 67 | -------------------------------------------------------------------------------- /ext/mstch/src/mstch.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "mstch/mstch.hpp" 4 | #include "render_context.hpp" 5 | 6 | using namespace mstch; 7 | 8 | std::function mstch::config::escape; 9 | 10 | std::string mstch::render( 11 | const std::string& tmplt, 12 | const node& root, 13 | const std::map& partials) 14 | { 15 | std::map partial_templates; 16 | for (auto& partial: partials) 17 | partial_templates.insert({partial.first, {partial.second}}); 18 | 19 | return render_context(root, partial_templates).render(tmplt); 20 | } 21 | -------------------------------------------------------------------------------- /ext/mstch/src/render_context.cpp: -------------------------------------------------------------------------------- 1 | #include "render_context.hpp" 2 | #include "state/outside_section.hpp" 3 | #include "visitor/get_token.hpp" 4 | 5 | using namespace mstch; 6 | 7 | const mstch::node render_context::null_node; 8 | 9 | render_context::push::push(render_context& context, const mstch::node& node): 10 | m_context(context) 11 | { 12 | context.m_nodes.emplace_front(node); 13 | context.m_node_ptrs.emplace_front(&node); 14 | context.m_state.push(std::unique_ptr(new outside_section)); 15 | } 16 | 17 | render_context::push::~push() { 18 | m_context.m_nodes.pop_front(); 19 | m_context.m_node_ptrs.pop_front(); 20 | m_context.m_state.pop(); 21 | } 22 | 23 | std::string render_context::push::render(const template_type& templt) { 24 | return m_context.render(templt); 25 | } 26 | 27 | render_context::render_context( 28 | const mstch::node& node, 29 | const std::map& partials): 30 | m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node) 31 | { 32 | m_state.push(std::unique_ptr(new outside_section)); 33 | } 34 | 35 | const mstch::node& render_context::find_node( 36 | const std::string& token, 37 | std::list current_nodes) 38 | { 39 | if (token != "." && token.find('.') != std::string::npos) 40 | return find_node(token.substr(token.rfind('.') + 1), 41 | {&find_node(token.substr(0, token.rfind('.')), current_nodes)}); 42 | else 43 | for (auto& node: current_nodes) 44 | if (visit(has_token(token), *node)) 45 | return visit(get_token(token, *node), *node); 46 | return null_node; 47 | } 48 | 49 | const mstch::node& render_context::get_node(const std::string& token) { 50 | return find_node(token, m_node_ptrs); 51 | } 52 | 53 | std::string render_context::render( 54 | const template_type& templt, const std::string& prefix) 55 | { 56 | std::string output; 57 | bool prev_eol = true; 58 | for (auto& token: templt) { 59 | if (prev_eol && prefix.length() != 0) 60 | output += m_state.top()->render(*this, {prefix}); 61 | output += m_state.top()->render(*this, token); 62 | prev_eol = token.eol(); 63 | } 64 | return output; 65 | } 66 | 67 | std::string render_context::render_partial( 68 | const std::string& partial_name, const std::string& prefix) 69 | { 70 | return m_partials.count(partial_name) ? 71 | render(m_partials.at(partial_name), prefix) : ""; 72 | } 73 | -------------------------------------------------------------------------------- /ext/mstch/src/render_context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "mstch/mstch.hpp" 10 | #include "state/render_state.hpp" 11 | #include "template_type.hpp" 12 | 13 | namespace mstch { 14 | 15 | class render_context { 16 | public: 17 | class push { 18 | public: 19 | push(render_context& context, const mstch::node& node = {}); 20 | ~push(); 21 | std::string render(const template_type& templt); 22 | private: 23 | render_context& m_context; 24 | }; 25 | 26 | render_context( 27 | const mstch::node& node, 28 | const std::map& partials); 29 | const mstch::node& get_node(const std::string& token); 30 | std::string render( 31 | const template_type& templt, const std::string& prefix = ""); 32 | std::string render_partial( 33 | const std::string& partial_name, const std::string& prefix); 34 | template 35 | void set_state(Args&& ... args) { 36 | m_state.top() = std::unique_ptr( 37 | new T(std::forward(args)...)); 38 | } 39 | 40 | private: 41 | static const mstch::node null_node; 42 | const mstch::node& find_node( 43 | const std::string& token, 44 | std::list current_nodes); 45 | std::map m_partials; 46 | std::deque m_nodes; 47 | std::list m_node_ptrs; 48 | std::stack> m_state; 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ext/mstch/src/state/in_section.cpp: -------------------------------------------------------------------------------- 1 | #include "in_section.hpp" 2 | #include "outside_section.hpp" 3 | #include "visitor/is_node_empty.hpp" 4 | #include "visitor/render_section.hpp" 5 | 6 | using namespace mstch; 7 | 8 | in_section::in_section(type type, const token& start_token): 9 | m_type(type), m_start_token(start_token), m_skipped_openings(0) 10 | { 11 | } 12 | 13 | std::string in_section::render(render_context& ctx, const token& token) { 14 | if (token.token_type() == token::type::section_close) 15 | if (token.name() == m_start_token.name() && m_skipped_openings == 0) { 16 | auto& node = ctx.get_node(m_start_token.name()); 17 | std::string out; 18 | 19 | if (m_type == type::normal && !visit(is_node_empty(), node)) 20 | out = visit(render_section(ctx, m_section, m_start_token.delims()), node); 21 | else if (m_type == type::inverted && visit(is_node_empty(), node)) 22 | out = render_context::push(ctx).render(m_section); 23 | 24 | ctx.set_state(); 25 | return out; 26 | } else 27 | m_skipped_openings--; 28 | else if (token.token_type() == token::type::inverted_section_open || 29 | token.token_type() == token::type::section_open) 30 | m_skipped_openings++; 31 | 32 | m_section << token; 33 | return ""; 34 | } 35 | -------------------------------------------------------------------------------- /ext/mstch/src/state/in_section.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "render_state.hpp" 7 | #include "template_type.hpp" 8 | 9 | namespace mstch { 10 | 11 | class in_section: public render_state { 12 | public: 13 | enum class type { inverted, normal }; 14 | in_section(type type, const token& start_token); 15 | std::string render(render_context& context, const token& token) override; 16 | 17 | private: 18 | const type m_type; 19 | const token& m_start_token; 20 | template_type m_section; 21 | int m_skipped_openings; 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /ext/mstch/src/state/outside_section.cpp: -------------------------------------------------------------------------------- 1 | #include "outside_section.hpp" 2 | 3 | #include "visitor/render_node.hpp" 4 | #include "in_section.hpp" 5 | #include "render_context.hpp" 6 | 7 | using namespace mstch; 8 | 9 | std::string outside_section::render( 10 | render_context& ctx, const token& token) 11 | { 12 | using flag = render_node::flag; 13 | switch (token.token_type()) { 14 | case token::type::section_open: 15 | ctx.set_state(in_section::type::normal, token); 16 | break; 17 | case token::type::inverted_section_open: 18 | ctx.set_state(in_section::type::inverted, token); 19 | break; 20 | case token::type::variable: 21 | return visit(render_node(ctx, flag::escape_html), ctx.get_node(token.name())); 22 | case token::type::unescaped_variable: 23 | return visit(render_node(ctx, flag::none), ctx.get_node(token.name())); 24 | case token::type::text: 25 | return token.raw(); 26 | case token::type::partial: 27 | return ctx.render_partial(token.name(), token.partial_prefix()); 28 | default: 29 | break; 30 | } 31 | return ""; 32 | } 33 | -------------------------------------------------------------------------------- /ext/mstch/src/state/outside_section.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "render_state.hpp" 4 | 5 | namespace mstch { 6 | 7 | class outside_section: public render_state { 8 | public: 9 | std::string render(render_context& context, const token& token) override; 10 | }; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /ext/mstch/src/state/render_state.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "token.hpp" 6 | 7 | namespace mstch { 8 | 9 | class render_context; 10 | 11 | class render_state { 12 | public: 13 | virtual ~render_state() {} 14 | virtual std::string render(render_context& context, const token& token) = 0; 15 | }; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /ext/mstch/src/template_type.cpp: -------------------------------------------------------------------------------- 1 | #include "template_type.hpp" 2 | 3 | using namespace mstch; 4 | 5 | template_type::template_type(const std::string& str, const delim_type& delims): 6 | m_open(delims.first), m_close(delims.second) 7 | { 8 | tokenize(str); 9 | strip_whitespace(); 10 | } 11 | 12 | template_type::template_type(const std::string& str): 13 | m_open("{{"), m_close("}}") 14 | { 15 | tokenize(str); 16 | strip_whitespace(); 17 | } 18 | 19 | void template_type::process_text(citer begin, citer end) { 20 | if (begin == end) 21 | return; 22 | auto start = begin; 23 | for (auto it = begin; it != end; ++it) 24 | if (*it == '\n' || it == end - 1) { 25 | m_tokens.push_back({{start, it + 1}}); 26 | start = it + 1; 27 | } 28 | } 29 | 30 | void template_type::tokenize(const std::string& tmp) { 31 | citer beg = tmp.begin(); 32 | auto npos = std::string::npos; 33 | 34 | for (std::size_t cur_pos = 0; cur_pos < tmp.size();) { 35 | auto open_pos = tmp.find(m_open, cur_pos); 36 | auto close_pos = tmp.find( 37 | m_close, open_pos == npos ? open_pos : open_pos + 1); 38 | 39 | if (close_pos != npos && open_pos != npos) { 40 | if (*(beg + open_pos + m_open.size()) == '{' && 41 | *(beg + close_pos + m_close.size()) == '}') 42 | ++close_pos; 43 | 44 | process_text(beg + cur_pos, beg + open_pos); 45 | cur_pos = close_pos + m_close.size(); 46 | m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()}, 47 | m_open.size(), m_close.size()}); 48 | 49 | if (cur_pos == tmp.size()) { 50 | m_tokens.push_back({{""}}); 51 | m_tokens.back().eol(true); 52 | } 53 | 54 | if (*(beg + open_pos + m_open.size()) == '=' && 55 | *(beg + close_pos - 1) == '=') 56 | { 57 | auto tok_beg = beg + open_pos + m_open.size() + 1; 58 | auto tok_end = beg + close_pos - 1; 59 | auto front_skip = first_not_ws(tok_beg, tok_end); 60 | auto back_skip = first_not_ws(reverse(tok_end), reverse(tok_beg)); 61 | m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)}; 62 | m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1}; 63 | } 64 | } else { 65 | process_text(beg + cur_pos, tmp.end()); 66 | cur_pos = close_pos; 67 | } 68 | } 69 | } 70 | 71 | void template_type::strip_whitespace() { 72 | auto line_begin = m_tokens.begin(); 73 | bool has_tag = false, non_space = false; 74 | 75 | for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it) { 76 | auto type = (*it).token_type(); 77 | if (type != token::type::text && type != token::type::variable && 78 | type != token::type::unescaped_variable) 79 | has_tag = true; 80 | else if (!(*it).ws_only()) 81 | non_space = true; 82 | 83 | if ((*it).eol()) { 84 | if (has_tag && !non_space) { 85 | store_prefixes(line_begin); 86 | 87 | auto c = line_begin; 88 | for (bool end = false; !end; (*c).ws_only() ? c = m_tokens.erase(c) : ++c) 89 | if ((end = (*c).eol())) 90 | it = c - 1; 91 | } 92 | 93 | non_space = has_tag = false; 94 | line_begin = it + 1; 95 | } 96 | } 97 | } 98 | 99 | void template_type::store_prefixes(std::vector::iterator beg) { 100 | for (auto cur = beg; !(*cur).eol(); ++cur) 101 | if ((*cur).token_type() == token::type::partial && 102 | cur != beg && (*(cur - 1)).ws_only()) 103 | (*cur).partial_prefix((*(cur - 1)).raw()); 104 | } 105 | -------------------------------------------------------------------------------- /ext/mstch/src/template_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "token.hpp" 7 | #include "utils.hpp" 8 | 9 | namespace mstch { 10 | 11 | class template_type { 12 | public: 13 | template_type() = default; 14 | template_type(const std::string& str); 15 | template_type(const std::string& str, const delim_type& delims); 16 | std::vector::const_iterator begin() const { return m_tokens.begin(); } 17 | std::vector::const_iterator end() const { return m_tokens.end(); } 18 | void operator<<(const token& token) { m_tokens.push_back(token); } 19 | 20 | private: 21 | std::vector m_tokens; 22 | std::string m_open; 23 | std::string m_close; 24 | void strip_whitespace(); 25 | void process_text(citer beg, citer end); 26 | void tokenize(const std::string& tmp); 27 | void store_prefixes(std::vector::iterator beg); 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /ext/mstch/src/token.cpp: -------------------------------------------------------------------------------- 1 | #include "token.hpp" 2 | #include "utils.hpp" 3 | 4 | using namespace mstch; 5 | 6 | token::type token::token_info(char c) { 7 | switch (c) { 8 | case '>': return type::partial; 9 | case '^': return type::inverted_section_open; 10 | case '/': return type::section_close; 11 | case '&': return type::unescaped_variable; 12 | case '#': return type::section_open; 13 | case '!': return type::comment; 14 | default: return type::variable; 15 | } 16 | } 17 | 18 | token::token(const std::string& str, std::size_t left, std::size_t right): 19 | m_raw(str), m_eol(false), m_ws_only(false) 20 | { 21 | if (left != 0 && right != 0) { 22 | if (str[left] == '=' && str[str.size() - right - 1] == '=') { 23 | m_type = type::delimiter_change; 24 | } else if (str[left] == '{' && str[str.size() - right - 1] == '}') { 25 | m_type = type::unescaped_variable; 26 | m_name = {first_not_ws(str.begin() + left + 1, str.end() - right), 27 | first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1}; 28 | } else { 29 | auto c = first_not_ws(str.begin() + left, str.end() - right); 30 | m_type = token_info(*c); 31 | if (m_type != type::variable) 32 | c = first_not_ws(c + 1, str.end() - right); 33 | m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1}; 34 | m_delims = {{str.begin(), str.begin() + left}, 35 | {str.end() - right, str.end()}}; 36 | } 37 | } else { 38 | m_type = type::text; 39 | m_eol = (str.size() > 0 && str[str.size() - 1] == '\n'); 40 | m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ext/mstch/src/token.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mstch { 6 | 7 | using delim_type = std::pair; 8 | 9 | class token { 10 | public: 11 | enum class type { 12 | text, variable, section_open, section_close, inverted_section_open, 13 | unescaped_variable, comment, partial, delimiter_change 14 | }; 15 | token(const std::string& str, std::size_t left = 0, std::size_t right = 0); 16 | type token_type() const { return m_type; }; 17 | const std::string& raw() const { return m_raw; }; 18 | const std::string& name() const { return m_name; }; 19 | const std::string& partial_prefix() const { return m_partial_prefix; }; 20 | const delim_type& delims() const { return m_delims; }; 21 | void partial_prefix(const std::string& p_partial_prefix) { 22 | m_partial_prefix = p_partial_prefix; 23 | }; 24 | bool eol() const { return m_eol; } 25 | void eol(bool eol) { m_eol = eol; } 26 | bool ws_only() const { return m_ws_only; } 27 | 28 | private: 29 | type m_type; 30 | std::string m_name; 31 | std::string m_raw; 32 | std::string m_partial_prefix; 33 | delim_type m_delims; 34 | bool m_eol; 35 | bool m_ws_only; 36 | type token_info(char c); 37 | }; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /ext/mstch/src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.hpp" 2 | #include "mstch/mstch.hpp" 3 | 4 | mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end) { 5 | for (auto it = begin; it != end; ++it) 6 | if (*it != ' ') return it; 7 | return end; 8 | } 9 | 10 | mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end) { 11 | for (auto rit = begin; rit != end; ++rit) 12 | if (*rit != ' ') return --(rit.base()); 13 | return --(end.base()); 14 | } 15 | 16 | mstch::criter mstch::reverse(mstch::citer it) { 17 | return std::reverse_iterator(it); 18 | } 19 | 20 | std::string mstch::html_escape(const std::string& str) { 21 | if (mstch::config::escape) 22 | return mstch::config::escape(str); 23 | 24 | std::string out; 25 | citer start = str.begin(); 26 | 27 | auto add_escape = [&out, &start](const std::string& escaped, citer& it) { 28 | out += std::string{start, it} + escaped; 29 | start = it + 1; 30 | }; 31 | 32 | for (auto it = str.begin(); it != str.end(); ++it) 33 | switch (*it) { 34 | case '&': add_escape("&", it); break; 35 | case '\'': add_escape("'", it); break; 36 | case '"': add_escape(""", it); break; 37 | case '<': add_escape("<", it); break; 38 | case '>': add_escape(">", it); break; 39 | case '/': add_escape("/", it); break; 40 | default: break; 41 | } 42 | 43 | return out + std::string{start, str.end()}; 44 | } 45 | -------------------------------------------------------------------------------- /ext/mstch/src/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mstch { 7 | 8 | using citer = std::string::const_iterator; 9 | using criter = std::string::const_reverse_iterator; 10 | 11 | citer first_not_ws(citer begin, citer end); 12 | citer first_not_ws(criter begin, criter end); 13 | std::string html_escape(const std::string& str); 14 | criter reverse(citer it); 15 | 16 | template 17 | auto visit(Args&&... args) -> decltype(boost::apply_visitor( 18 | std::forward(args)...)) 19 | { 20 | return boost::apply_visitor(std::forward(args)...); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ext/mstch/src/visitor/get_token.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mstch/mstch.hpp" 6 | #include "has_token.hpp" 7 | 8 | namespace mstch { 9 | 10 | class get_token: public boost::static_visitor { 11 | public: 12 | get_token(const std::string& token, const mstch::node& node): 13 | m_token(token), m_node(node) 14 | { 15 | } 16 | 17 | template 18 | const mstch::node& operator()(const T&) const { 19 | return m_node; 20 | } 21 | 22 | const mstch::node& operator()(const map& map) const { 23 | return map.at(m_token); 24 | } 25 | 26 | const mstch::node& operator()(const std::shared_ptr& object) const { 27 | return object->at(m_token); 28 | } 29 | 30 | private: 31 | const std::string& m_token; 32 | const mstch::node& m_node; 33 | }; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ext/mstch/src/visitor/has_token.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mstch/mstch.hpp" 6 | 7 | namespace mstch { 8 | 9 | class has_token: public boost::static_visitor { 10 | public: 11 | has_token(const std::string& token): m_token(token) { 12 | } 13 | 14 | template 15 | bool operator()(const T&) const { 16 | return m_token == "."; 17 | } 18 | 19 | bool operator()(const map& map) const { 20 | return map.count(m_token) == 1; 21 | } 22 | 23 | bool operator()(const std::shared_ptr& object) const { 24 | return object->has(m_token); 25 | } 26 | 27 | private: 28 | const std::string& m_token; 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /ext/mstch/src/visitor/is_node_empty.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "mstch/mstch.hpp" 6 | 7 | namespace mstch { 8 | 9 | class is_node_empty: public boost::static_visitor { 10 | public: 11 | template 12 | bool operator()(const T&) const { 13 | return false; 14 | } 15 | 16 | bool operator()(const std::nullptr_t&) const { 17 | return true; 18 | } 19 | 20 | bool operator()(const int& value) const { 21 | return value == 0; 22 | } 23 | 24 | bool operator()(const double& value) const { 25 | return value == 0; 26 | } 27 | 28 | bool operator()(const bool& value) const { 29 | return !value; 30 | } 31 | 32 | bool operator()(const std::string& value) const { 33 | return value == ""; 34 | } 35 | 36 | bool operator()(const array& array) const { 37 | return array.size() == 0; 38 | } 39 | }; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /ext/mstch/src/visitor/render_node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "render_context.hpp" 7 | #include "mstch/mstch.hpp" 8 | #include "utils.hpp" 9 | 10 | namespace mstch { 11 | 12 | class render_node: public boost::static_visitor { 13 | public: 14 | enum class flag { none, escape_html }; 15 | render_node(render_context& ctx, flag p_flag = flag::none): 16 | m_ctx(ctx), m_flag(p_flag) 17 | { 18 | } 19 | 20 | template 21 | std::string operator()(const T&) const { 22 | return ""; 23 | } 24 | 25 | std::string operator()(const int& value) const { 26 | return std::to_string(value); 27 | } 28 | 29 | std::string operator()(const double& value) const { 30 | std::stringstream ss; 31 | ss << value; 32 | return ss.str(); 33 | } 34 | 35 | std::string operator()(const uint64_t& value) const { 36 | std::stringstream ss; 37 | ss << value; 38 | return ss.str(); 39 | } 40 | 41 | std::string operator()(const int64_t& value) const { 42 | std::stringstream ss; 43 | ss << value; 44 | return ss.str(); 45 | } 46 | 47 | std::string operator()(const uint32_t& value) const { 48 | std::stringstream ss; 49 | ss << value; 50 | return ss.str(); 51 | } 52 | 53 | std::string operator()(const bool& value) const { 54 | return value ? "true" : "false"; 55 | } 56 | 57 | std::string operator()(const lambda& value) const { 58 | template_type interpreted{value([this](const mstch::node& n) { 59 | return visit(render_node(m_ctx), n); 60 | })}; 61 | auto rendered = render_context::push(m_ctx).render(interpreted); 62 | return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered; 63 | } 64 | 65 | std::string operator()(const std::string& value) const { 66 | return (m_flag == flag::escape_html) ? html_escape(value) : value; 67 | } 68 | 69 | private: 70 | render_context& m_ctx; 71 | flag m_flag; 72 | }; 73 | 74 | } 75 | -------------------------------------------------------------------------------- /ext/mstch/src/visitor/render_section.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "render_context.hpp" 6 | #include "mstch/mstch.hpp" 7 | #include "utils.hpp" 8 | #include "render_node.hpp" 9 | 10 | namespace mstch { 11 | 12 | class render_section: public boost::static_visitor { 13 | public: 14 | enum class flag { none, keep_array }; 15 | render_section( 16 | render_context& ctx, 17 | const template_type& section, 18 | const delim_type& delims, 19 | flag p_flag = flag::none): 20 | m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag) 21 | { 22 | } 23 | 24 | template 25 | std::string operator()(const T& t) const { 26 | return render_context::push(m_ctx, t).render(m_section); 27 | } 28 | 29 | std::string operator()(const lambda& fun) const { 30 | std::string section_str; 31 | for (auto& token: m_section) 32 | section_str += token.raw(); 33 | template_type interpreted{fun([this](const mstch::node& n) { 34 | return visit(render_node(m_ctx), n); 35 | }, section_str), m_delims}; 36 | return render_context::push(m_ctx).render(interpreted); 37 | } 38 | 39 | std::string operator()(const array& array) const { 40 | std::string out; 41 | if (m_flag == flag::keep_array) 42 | return render_context::push(m_ctx, array).render(m_section); 43 | else 44 | for (auto& item: array) 45 | out += visit(render_section( 46 | m_ctx, m_section, m_delims, flag::keep_array), item); 47 | return out; 48 | } 49 | 50 | private: 51 | render_context& m_ctx; 52 | const template_type& m_section; 53 | const delim_type& m_delims; 54 | flag m_flag; 55 | }; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | 3 | project(myxrm) 4 | 5 | set(SOURCE_HEADERS 6 | MicroCore.h 7 | tools.h 8 | monero_headers.h 9 | CurrentBlockchainStatus.h) 10 | 11 | set(SOURCE_FILES 12 | MicroCore.cpp 13 | tools.cpp 14 | CmdLineOptions.cpp 15 | page.h 16 | rpccalls.cpp rpccalls.h 17 | version.h.in 18 | CurrentBlockchainStatus.cpp 19 | MempoolStatus.cpp 20 | MempoolStatus.h) 21 | 22 | add_subdirectory(crypto) 23 | 24 | 25 | # make static library called libmyxrm 26 | # that we are going to link to 27 | # in the root CMakeLists.txt file 28 | add_library(myxrm 29 | STATIC 30 | ${SOURCE_FILES}) 31 | 32 | target_link_libraries(myxrm mycrypto) 33 | 34 | -------------------------------------------------------------------------------- /src/CmdLineOptions.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 6/11/15. 3 | // 4 | 5 | #include "CmdLineOptions.h" 6 | 7 | 8 | namespace xmreg 9 | { 10 | /** 11 | * Take the acc and *avv[] from the main() and check and parse 12 | * all the options given 13 | */ 14 | CmdLineOptions::CmdLineOptions(int acc, const char *avv[]) { 15 | 16 | positional_options_description p; 17 | 18 | p.add("txhash", -1); 19 | 20 | options_description desc( 21 | "xmrblocks, Onion Monero Blockchain Explorer"); 22 | 23 | desc.add_options() 24 | ("help,h", value()->default_value(false)->implicit_value(true), 25 | "produce help message") 26 | ("testnet,t", value()->default_value(false)->implicit_value(true), 27 | "use testnet blockchain") 28 | ("stagenet,s", value()->default_value(false)->implicit_value(true), 29 | "use stagenet blockchain") 30 | ("enable-pusher", value()->default_value(false)->implicit_value(true), 31 | "enable signed transaction pusher") 32 | ("enable-randomx", value()->default_value(false)->implicit_value(true), 33 | "enable generation of randomx code") 34 | ("enable-mixin-details", value()->default_value(false)->implicit_value(true), 35 | "enable mixin details for key images, e.g., timescale, mixin of mixins, in tx context") 36 | ("enable-key-image-checker", value()->default_value(false)->implicit_value(true), 37 | "enable key images file checker") 38 | ("enable-output-key-checker", value()->default_value(false)->implicit_value(true), 39 | "enable outputs key file checker") 40 | ("enable-json-api", value()->default_value(false)->implicit_value(true), 41 | "enable JSON REST api") 42 | ("enable-as-hex", value()->default_value(false)->implicit_value(true), 43 | "enable links to provide hex represtations of a tx and a block") 44 | ("enable-autorefresh-option", value()->default_value(false)->implicit_value(true), 45 | "enable users to have the index page on autorefresh") 46 | ("enable-emission-monitor", value()->default_value(false)->implicit_value(true), 47 | "enable Monero total emission monitoring thread") 48 | ("port,p", value()->default_value("8081"), 49 | "default explorer port") 50 | ("bindaddr,x", value()->default_value("0.0.0.0"), 51 | "default bind address for the explorer") 52 | ("testnet-url", value()->default_value(""), 53 | "you can specify testnet url, if you run it on mainnet or stagenet. link will show on front page to testnet explorer") 54 | ("stagenet-url", value()->default_value(""), 55 | "you can specify stagenet url, if you run it on mainnet or testnet. link will show on front page to stagenet explorer") 56 | ("mainnet-url", value()->default_value(""), 57 | "you can specify mainnet url, if you run it on testnet or stagenet. link will show on front page to mainnet explorer") 58 | ("no-blocks-on-index", value()->default_value("10"), 59 | "number of last blocks to be shown on index page") 60 | ("mempool-info-timeout", value()->default_value("5000"), 61 | "maximum time, in milliseconds, to wait for mempool data for the front page") 62 | ("mempool-refresh-time", value()->default_value("5"), 63 | "time, in seconds, for each refresh of mempool state") 64 | ("concurrency,c", value()->default_value(0), 65 | "number of threads handling http queries. Default is 0 which means it is based you on the cpu") 66 | ("bc-path,b", value(), 67 | "path to lmdb folder of the blockchain, e.g., ~/.bitmonero/lmdb") 68 | ("ssl-crt-file", value(), 69 | "path to crt file for ssl (https) functionality") 70 | ("ssl-key-file", value(), 71 | "path to key file for ssl (https) functionality") 72 | ("daemon-login", value(), 73 | "Specify username[:password] for daemon RPC client") 74 | ("daemon-url,d", value()->default_value("127.0.0.1:18081"), 75 | "Monero daemon url") 76 | ("enable-mixin-guess", value()->default_value(false)->implicit_value(true), 77 | "enable guessing real outputs in key images based on viewkey"); 78 | 79 | 80 | store(command_line_parser(acc, avv) 81 | .options(desc) 82 | .positional(p) 83 | .run(), vm); 84 | 85 | notify(vm); 86 | 87 | if (vm.count("help")) 88 | { 89 | if (vm["help"].as()) 90 | cout << desc << "\n"; 91 | } 92 | } 93 | 94 | /** 95 | * Return the value of the argument passed to the program 96 | * in wrapped around boost::optional 97 | */ 98 | template 99 | boost::optional 100 | CmdLineOptions::get_option(const string & opt_name) const 101 | { 102 | 103 | if (!vm.count(opt_name)) 104 | { 105 | return boost::none; 106 | } 107 | 108 | return vm[opt_name].as(); 109 | } 110 | 111 | 112 | // explicit instantiations of get_option template function 113 | template boost::optional 114 | CmdLineOptions::get_option(const string & opt_name) const; 115 | 116 | template boost::optional 117 | CmdLineOptions::get_option(const string & opt_name) const; 118 | 119 | template boost::optional 120 | CmdLineOptions::get_option(const string & opt_name) const; 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/CmdLineOptions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 6/11/15. 3 | // 4 | 5 | #ifndef XMREG01_CMDLINEOPTIONS_H 6 | #define XMREG01_CMDLINEOPTIONS_H 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace xmreg 15 | { 16 | 17 | using namespace std; 18 | using namespace boost::program_options; 19 | 20 | /** 21 | * Manages program options of this example program. 22 | * 23 | * Basically a wrapper for boost::program_options 24 | */ 25 | class CmdLineOptions { 26 | variables_map vm; 27 | 28 | public: 29 | CmdLineOptions(int acc, const char *avv[]); 30 | 31 | template 32 | boost::optional get_option(const string & opt_name) const; 33 | }; 34 | } 35 | 36 | 37 | #endif //XMREG01_CMDLINEOPTIONS_H 38 | -------------------------------------------------------------------------------- /src/CurrentBlockchainStatus.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 16/05/17. 3 | // 4 | 5 | #include "CurrentBlockchainStatus.h" 6 | 7 | namespace xmreg 8 | { 9 | 10 | using namespace std; 11 | 12 | 13 | 14 | void 15 | CurrentBlockchainStatus::set_blockchain_variables(MicroCore* _mcore, 16 | Blockchain* _core_storage) 17 | { 18 | mcore = _mcore; 19 | core_storage =_core_storage; 20 | } 21 | 22 | 23 | void 24 | CurrentBlockchainStatus::start_monitor_blockchain_thread() 25 | { 26 | total_emission_atomic = Emission {0, 0, 0}; 27 | 28 | string emmision_saved_file = get_output_file_path().string(); 29 | 30 | // read stored emission data if possible 31 | if (boost::filesystem::exists(emmision_saved_file)) 32 | { 33 | if (!load_current_emission_amount()) 34 | { 35 | cerr << "Emission file cant be read, got corrupted or has incorrect format:\n " << emmision_saved_file 36 | << "\nEmission monitoring thread is not started.\nDelete the file and" 37 | << " restart the explorer or disable emission monitoring." 38 | << endl; 39 | 40 | cerr << "Press ENTER to continue without emission monitoring or Ctr+C to exit" << endl; 41 | 42 | cin.get(); 43 | 44 | return; 45 | } 46 | } 47 | 48 | if (!is_running) 49 | { 50 | m_thread = boost::thread{[]() 51 | { 52 | try 53 | { 54 | while (true) 55 | { 56 | Emission current_emission = total_emission_atomic; 57 | 58 | current_height = core_storage->get_current_blockchain_height(); 59 | 60 | // scan 10000 blocks for emissiom or if we are at the top of 61 | // the blockchain, only few top blocks 62 | update_current_emission_amount(); 63 | 64 | cout << "current emission: " << string(current_emission) << endl; 65 | 66 | save_current_emission_amount(); 67 | 68 | if (current_emission.blk_no < current_height - blockchain_chunk_size) 69 | { 70 | // while we scan the blockchain from scrach, every 10000 71 | // blocks take 1 second break 72 | boost::this_thread::sleep_for(boost::chrono::seconds(1)); 73 | } 74 | else 75 | { 76 | // when we reach top of the blockchain, update 77 | // the emission amount every minute. 78 | boost::this_thread::sleep_for(boost::chrono::seconds(60)); 79 | } 80 | 81 | } // while (true) 82 | } 83 | catch (boost::thread_interrupted&) 84 | { 85 | cout << "Emission monitoring thread interrupted." << endl; 86 | return; 87 | } 88 | 89 | }}; // m_thread = boost::thread{[]() 90 | 91 | is_running = true; 92 | 93 | } // if (!is_running) 94 | } 95 | 96 | 97 | void 98 | CurrentBlockchainStatus::update_current_emission_amount() 99 | { 100 | 101 | Emission current_emission = total_emission_atomic; 102 | 103 | uint64_t blk_no = current_emission.blk_no; 104 | 105 | uint64_t end_block = blk_no + blockchain_chunk_size; 106 | 107 | uint64_t current_blockchain_height = current_height; 108 | 109 | // blockchain_chunk_gap is used so that we 110 | // never read and store few top blocks 111 | // the emission in the top few blocks will be calcalted 112 | // later 113 | end_block = end_block > current_blockchain_height 114 | ? current_blockchain_height - blockchain_chunk_gap 115 | : end_block; 116 | 117 | Emission emission_calculated = calculate_emission_in_blocks(blk_no, end_block); 118 | 119 | current_emission.coinbase += emission_calculated.coinbase; 120 | current_emission.fee += emission_calculated.fee; 121 | current_emission.blk_no = emission_calculated.blk_no; 122 | 123 | total_emission_atomic = current_emission; 124 | } 125 | 126 | CurrentBlockchainStatus::Emission 127 | CurrentBlockchainStatus::calculate_emission_in_blocks( 128 | uint64_t start_blk, uint64_t end_blk) 129 | { 130 | Emission emission_calculated {0, 0, 0}; 131 | 132 | while (start_blk < end_blk) 133 | { 134 | block blk; 135 | 136 | mcore->get_block_by_height(start_blk, blk); 137 | 138 | uint64_t coinbase_amount = get_outs_money_amount(blk.miner_tx); 139 | 140 | vector txs; 141 | vector missed_txs; 142 | 143 | uint64_t tx_fee_amount = 0; 144 | 145 | core_storage->get_transactions(blk.tx_hashes, txs, missed_txs); 146 | 147 | for(const auto& tx: txs) 148 | { 149 | tx_fee_amount += get_tx_fee(tx); 150 | } 151 | 152 | (void) missed_txs; 153 | 154 | emission_calculated.coinbase += coinbase_amount - tx_fee_amount; 155 | emission_calculated.fee += tx_fee_amount; 156 | 157 | ++start_blk; 158 | } 159 | 160 | emission_calculated.blk_no = start_blk; 161 | 162 | return emission_calculated; 163 | } 164 | 165 | 166 | bool 167 | CurrentBlockchainStatus::save_current_emission_amount() 168 | { 169 | 170 | string emmision_saved_file = get_output_file_path().string(); 171 | 172 | ofstream out(emmision_saved_file); 173 | 174 | if( !out ) 175 | { 176 | cerr << "Couldn't open file." << endl; 177 | return false; 178 | } 179 | 180 | Emission current_emission = total_emission_atomic; 181 | 182 | out << string(current_emission) << flush; 183 | 184 | return true; 185 | } 186 | 187 | 188 | bool 189 | CurrentBlockchainStatus::load_current_emission_amount() 190 | { 191 | string emmision_saved_file = get_output_file_path().string(); 192 | 193 | string last_saved_emmision = xmreg::read(emmision_saved_file); 194 | 195 | if (last_saved_emmision.empty()) 196 | { 197 | cerr << "Couldn't open file." << endl; 198 | return false; 199 | } 200 | 201 | last_saved_emmision.erase(last_saved_emmision.find_last_not_of(" \n\r\t")+1); 202 | 203 | vector strs; 204 | boost::split(strs, last_saved_emmision, boost::is_any_of(",")); 205 | 206 | if (strs.empty()) 207 | { 208 | cerr << "Problem spliting string values form emission_amount." << endl; 209 | return false; 210 | } 211 | 212 | Emission emission_loaded {0, 0, 0}; 213 | 214 | uint64_t read_check_sum {0}; 215 | 216 | try 217 | { 218 | emission_loaded.blk_no = boost::lexical_cast(strs.at(0)); 219 | emission_loaded.coinbase = boost::lexical_cast(strs.at(1)); 220 | emission_loaded.fee = boost::lexical_cast(strs.at(2)); 221 | read_check_sum = boost::lexical_cast(strs.at(3)); 222 | } 223 | catch (boost::bad_lexical_cast &e) 224 | { 225 | cerr << "Cant parse to number date from string: " << last_saved_emmision << endl; 226 | return false; 227 | } 228 | 229 | if (read_check_sum != emission_loaded.checksum()) 230 | { 231 | cerr << "read_check_sum != check_sum: " 232 | << read_check_sum << " != " << emission_loaded.checksum() 233 | << endl; 234 | 235 | return false; 236 | } 237 | 238 | total_emission_atomic = emission_loaded; 239 | 240 | return true; 241 | 242 | } 243 | 244 | bf::path 245 | CurrentBlockchainStatus::get_output_file_path() 246 | { 247 | return blockchain_path / output_file; 248 | } 249 | 250 | 251 | CurrentBlockchainStatus::Emission 252 | CurrentBlockchainStatus::get_emission() 253 | { 254 | // get current emission 255 | Emission current_emission = total_emission_atomic; 256 | 257 | // this emission will be few blocks behind current blockchain 258 | // height. By default 3 blocks. So we need to calcualate here 259 | // the emission from the top missing blocks, to have complete 260 | // emission data. 261 | 262 | uint64_t current_blockchain_height = current_height; 263 | 264 | uint64_t start_blk = current_emission.blk_no; 265 | 266 | // this should be at current hight or above 267 | // as we calculate missing blocks only for top blockchain 268 | // height 269 | uint64_t end_block = start_blk + blockchain_chunk_gap; 270 | 271 | if (end_block >= current_blockchain_height 272 | && start_blk < current_blockchain_height) 273 | { 274 | // make sure we are not over the blockchain height 275 | end_block = end_block > current_blockchain_height 276 | ? current_blockchain_height : end_block; 277 | 278 | // calculated emission for missing blocks 279 | Emission gap_emission_calculated 280 | = calculate_emission_in_blocks(start_blk, end_block); 281 | 282 | //cout << "gap_emission_calculated: " << std::string(gap_emission_calculated) << endl; 283 | 284 | current_emission.coinbase += gap_emission_calculated.coinbase; 285 | current_emission.fee += gap_emission_calculated.fee; 286 | current_emission.blk_no = gap_emission_calculated.blk_no > 0 287 | ? gap_emission_calculated.blk_no 288 | : current_emission.blk_no; 289 | } 290 | 291 | return current_emission; 292 | } 293 | 294 | bool 295 | CurrentBlockchainStatus::is_thread_running() 296 | { 297 | return is_running; 298 | } 299 | 300 | bf::path CurrentBlockchainStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"}; 301 | 302 | cryptonote::network_type CurrentBlockchainStatus::nettype {cryptonote::network_type::MAINNET}; 303 | 304 | string CurrentBlockchainStatus::output_file {"emission_amount.txt"}; 305 | 306 | string CurrentBlockchainStatus::daemon_url {"http:://127.0.0.1:18081"}; 307 | 308 | uint64_t CurrentBlockchainStatus::blockchain_chunk_size {10000}; 309 | 310 | uint64_t CurrentBlockchainStatus::blockchain_chunk_gap {3}; 311 | 312 | atomic CurrentBlockchainStatus::current_height {0}; 313 | 314 | atomic CurrentBlockchainStatus::total_emission_atomic; 315 | 316 | boost::thread CurrentBlockchainStatus::m_thread; 317 | 318 | atomic CurrentBlockchainStatus::is_running {false}; 319 | 320 | Blockchain* CurrentBlockchainStatus::core_storage {nullptr}; 321 | xmreg::MicroCore* CurrentBlockchainStatus::mcore {nullptr}; 322 | } 323 | -------------------------------------------------------------------------------- /src/CurrentBlockchainStatus.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 16/05/17. 3 | // 4 | 5 | #ifndef XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H 6 | #define XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H 7 | 8 | #include "MicroCore.h" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace xmreg 19 | { 20 | 21 | using namespace std; 22 | 23 | namespace bf = boost::filesystem; 24 | 25 | struct CurrentBlockchainStatus 26 | { 27 | 28 | struct Emission 29 | { 30 | uint64_t coinbase; 31 | uint64_t fee; 32 | uint64_t blk_no; 33 | 34 | inline uint64_t 35 | checksum() const 36 | { 37 | return coinbase + fee + blk_no; 38 | } 39 | 40 | operator 41 | std::string() const 42 | { 43 | return to_string(blk_no) + "," + to_string(coinbase) 44 | + "," + to_string(fee) + "," + to_string(checksum()); 45 | } 46 | }; 47 | 48 | static bf::path blockchain_path; 49 | 50 | static cryptonote::network_type nettype; 51 | 52 | static string output_file; 53 | 54 | static string daemon_url; 55 | 56 | // how many blocks to read before thread goes to sleep 57 | static uint64_t blockchain_chunk_size; 58 | 59 | // gap from what we store total_emission_atomic and 60 | // current blockchain height. We dont want to store 61 | // what is on, e.g., top block, as this can get messy 62 | // if the block gets orphaned or blockchain reorganization 63 | // occurs. So the top 3 blocks (default number) will always 64 | // be calculated in flight and added to what we have so far. 65 | static uint64_t blockchain_chunk_gap; 66 | 67 | // current blockchain height and 68 | // hash of top block 69 | static atomic current_height; 70 | 71 | 72 | static atomic total_emission_atomic; 73 | 74 | 75 | static boost::thread m_thread; 76 | 77 | static atomic is_running; 78 | 79 | // make object for accessing the blockchain here 80 | static MicroCore* mcore; 81 | static Blockchain* core_storage; 82 | 83 | static void 84 | start_monitor_blockchain_thread(); 85 | 86 | static void 87 | set_blockchain_variables(MicroCore* _mcore, 88 | Blockchain* _core_storage); 89 | 90 | static void 91 | update_current_emission_amount(); 92 | 93 | static Emission 94 | calculate_emission_in_blocks(uint64_t start_blk, uint64_t end_blk); 95 | 96 | static bool 97 | save_current_emission_amount(); 98 | 99 | static bool 100 | load_current_emission_amount(); 101 | 102 | static Emission 103 | get_emission(); 104 | 105 | static bf::path 106 | get_output_file_path(); 107 | 108 | static bool 109 | is_thread_running(); 110 | }; 111 | 112 | } 113 | 114 | #endif //XMRBLOCKS_CURRENTBLOCKCHAINSTATUS_H 115 | -------------------------------------------------------------------------------- /src/MempoolStatus.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 28/05/17. 3 | // 4 | 5 | #ifndef XMRBLOCKS_MEMPOOLSTATUS_H 6 | #define XMRBLOCKS_MEMPOOLSTATUS_H 7 | 8 | 9 | #include "MicroCore.h" 10 | #include "rpccalls.h" 11 | 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace xmreg 21 | { 22 | 23 | struct MempoolStatus 24 | { 25 | 26 | using Guard = std::lock_guard; 27 | 28 | struct mempool_tx 29 | { 30 | crypto::hash tx_hash; 31 | transaction tx; 32 | 33 | uint64_t receive_time {0}; 34 | uint64_t sum_inputs {0}; 35 | uint64_t sum_outputs {0}; 36 | uint64_t no_inputs {0}; 37 | uint64_t no_outputs {0}; 38 | uint64_t num_nonrct_inputs {0}; 39 | uint64_t mixin_no {0}; 40 | 41 | string fee_str; 42 | string fee_micro_str; 43 | string payed_for_kB_str; 44 | string payed_for_kB_micro_str; 45 | string xmr_inputs_str; 46 | string xmr_outputs_str; 47 | string timestamp_str; 48 | string txsize; 49 | }; 50 | 51 | 52 | // to keep network_info in cache 53 | // and to show previous info in case current querry for 54 | // the current info timesout. 55 | struct network_info 56 | { 57 | uint64_t status {0}; 58 | uint64_t height {0}; 59 | uint64_t target_height {0}; 60 | uint64_t difficulty {0}; 61 | uint64_t difficulty_top64 {0}; 62 | uint64_t target {0}; 63 | uint64_t tx_count {0}; 64 | uint64_t tx_pool_size {0}; 65 | uint64_t alt_blocks_count {0}; 66 | uint64_t outgoing_connections_count {0}; 67 | uint64_t incoming_connections_count {0}; 68 | uint64_t white_peerlist_size {0}; 69 | uint64_t grey_peerlist_size {0}; 70 | cryptonote::network_type nettype {cryptonote::network_type::MAINNET}; 71 | crypto::hash top_block_hash; 72 | uint64_t cumulative_difficulty {0}; 73 | uint64_t cumulative_difficulty_top64 {0}; 74 | uint64_t block_size_limit {0}; 75 | uint64_t block_size_median {0}; 76 | uint64_t block_weight_limit {0}; 77 | char block_size_limit_str[10]; // needs to be trivially copyable 78 | char block_size_median_str[10]; // std::string is not trivially copyable 79 | uint64_t start_time {0}; 80 | uint64_t current_hf_version {0}; 81 | 82 | uint64_t hash_rate {0}; 83 | uint64_t hash_rate_top64 {0}; 84 | uint64_t fee_per_kb {0}; 85 | uint64_t info_timestamp {0}; 86 | 87 | bool current {false}; 88 | 89 | static uint64_t 90 | get_status_uint(const string& status) 91 | { 92 | if (status == CORE_RPC_STATUS_OK) 93 | return 1; 94 | 95 | if (status == CORE_RPC_STATUS_BUSY) 96 | return 2; 97 | 98 | // default 99 | return 0; 100 | } 101 | 102 | static string 103 | get_status_string(const uint64_t& status) 104 | { 105 | if (status == 1) 106 | return CORE_RPC_STATUS_OK; 107 | 108 | if (status == 2) 109 | return CORE_RPC_STATUS_BUSY; 110 | 111 | // default 112 | return 0; 113 | } 114 | }; 115 | 116 | static boost::thread m_thread; 117 | 118 | static mutex mempool_mutx; 119 | 120 | static atomic is_running; 121 | 122 | static uint64_t mempool_refresh_time; 123 | 124 | static atomic mempool_no; // no of txs 125 | static atomic mempool_size; // size in bytes. 126 | 127 | static bf::path blockchain_path; 128 | static string daemon_url; 129 | static cryptonote::network_type nettype; 130 | 131 | static rpccalls::login_opt login; 132 | 133 | // make object for accessing the blockchain here 134 | static MicroCore* mcore; 135 | static Blockchain* core_storage; 136 | 137 | // vector of mempool transactions that all threads 138 | // can refer to 139 | // 140 | static vector mempool_txs; 141 | 142 | static atomic current_network_info; 143 | 144 | static void 145 | set_blockchain_variables(MicroCore* _mcore, 146 | Blockchain* _core_storage); 147 | 148 | static void 149 | start_mempool_status_thread(); 150 | 151 | static bool 152 | read_mempool(); 153 | 154 | static bool 155 | read_network_info(); 156 | 157 | static vector 158 | get_mempool_txs(); 159 | 160 | // get first no_of_tx from the vector 161 | static vector 162 | get_mempool_txs(uint64_t no_of_tx); 163 | 164 | static bool 165 | is_thread_running(); 166 | }; 167 | 168 | } 169 | #endif //XMRBLOCKS_MEMPOOLSTATUS_H 170 | -------------------------------------------------------------------------------- /src/MicroCore.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #include "MicroCore.h" 6 | 7 | 8 | namespace xmreg 9 | { 10 | /** 11 | * The constructor is interesting, as 12 | * m_mempool and m_blockchain_storage depend 13 | * on each other. 14 | * 15 | * So basically m_mempool is initialized with 16 | * reference to Blockchain (i.e., Blockchain&) 17 | * and m_blockchain_storage is initialized with 18 | * reference to m_mempool (i.e., tx_memory_pool&) 19 | * 20 | * The same is done in cryptonode::core. 21 | */ 22 | MicroCore::MicroCore(): 23 | m_mempool(m_blockchain_storage), 24 | m_blockchain_storage(m_mempool) 25 | { 26 | m_device = &hw::get_device("default"); 27 | } 28 | 29 | 30 | /** 31 | * Initialized the MicroCore object. 32 | * 33 | * Create BlockchainLMDB on the heap. 34 | * Open database files located in blockchain_path. 35 | * Initialize m_blockchain_storage with the BlockchainLMDB object. 36 | */ 37 | bool 38 | MicroCore::init(const string& _blockchain_path, network_type nt) 39 | { 40 | int db_flags = 0; 41 | 42 | blockchain_path = _blockchain_path; 43 | 44 | nettype = nt; 45 | 46 | db_flags |= MDB_RDONLY; 47 | db_flags |= MDB_NOLOCK; 48 | 49 | BlockchainDB* db = nullptr; 50 | db = new BlockchainLMDB(); 51 | 52 | try 53 | { 54 | // try opening lmdb database files 55 | db->open(blockchain_path, db_flags); 56 | } 57 | catch (const std::exception& e) 58 | { 59 | cerr << "Error opening database: " << e.what(); 60 | return false; 61 | } 62 | 63 | // check if the blockchain database 64 | // is successful opened 65 | if(!db->is_open()) 66 | return false; 67 | 68 | // initialize Blockchain object to manage 69 | // the database. 70 | return m_blockchain_storage.init(db, nettype); 71 | } 72 | 73 | /** 74 | * Get m_blockchain_storage. 75 | * Initialize m_blockchain_storage with the BlockchainLMDB object. 76 | */ 77 | Blockchain& 78 | MicroCore::get_core() 79 | { 80 | return m_blockchain_storage; 81 | } 82 | 83 | tx_memory_pool& 84 | MicroCore::get_mempool() 85 | { 86 | return m_mempool; 87 | } 88 | 89 | /** 90 | * Get block by its height 91 | * 92 | * returns true if success 93 | */ 94 | bool 95 | MicroCore::get_block_by_height(const uint64_t& height, block& blk) 96 | { 97 | try 98 | { 99 | blk = m_blockchain_storage.get_db().get_block_from_height(height); 100 | } 101 | catch (const BLOCK_DNE& e) 102 | { 103 | cerr << "Block of height " << height 104 | << " not found in the blockchain!" 105 | << e.what() 106 | << endl; 107 | 108 | return false; 109 | } 110 | catch (const DB_ERROR& e) 111 | { 112 | cerr << "Blockchain access error when getting block " << height 113 | << e.what() 114 | << endl; 115 | 116 | return false; 117 | } 118 | catch (...) 119 | { 120 | cerr << "Something went terribly wrong when getting block " << height 121 | << endl; 122 | 123 | return false; 124 | } 125 | 126 | return true; 127 | } 128 | 129 | 130 | 131 | /** 132 | * Get transaction tx from the blockchain using it hash 133 | */ 134 | bool 135 | MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx) 136 | { 137 | if (m_blockchain_storage.have_tx(tx_hash)) 138 | { 139 | // get transaction with given hash 140 | try 141 | { 142 | tx = m_blockchain_storage.get_db().get_tx(tx_hash); 143 | } 144 | catch (TX_DNE const& e) 145 | { 146 | try 147 | { 148 | // coinbase txs are not considered pruned 149 | tx = m_blockchain_storage.get_db().get_pruned_tx(tx_hash); 150 | return true; 151 | } 152 | catch (TX_DNE const& e) 153 | { 154 | cerr << "MicroCore::get_tx: " << e.what() << endl; 155 | } 156 | 157 | return false; 158 | } 159 | } 160 | else 161 | { 162 | cerr << "MicroCore::get_tx tx does not exist in blockchain: " << tx_hash << endl; 163 | return false; 164 | } 165 | 166 | 167 | return true; 168 | } 169 | 170 | bool 171 | MicroCore::get_tx(const string& tx_hash_str, transaction& tx) 172 | { 173 | 174 | // parse tx hash string to hash object 175 | crypto::hash tx_hash; 176 | 177 | if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) 178 | { 179 | cerr << "Cant parse tx hash: " << tx_hash_str << endl; 180 | return false; 181 | } 182 | 183 | 184 | if (!get_tx(tx_hash, tx)) 185 | { 186 | return false; 187 | } 188 | 189 | 190 | return true; 191 | } 192 | 193 | 194 | 195 | 196 | /** 197 | * Find output with given public key in a given transaction 198 | */ 199 | bool 200 | MicroCore::find_output_in_tx(const transaction& tx, 201 | const public_key& output_pubkey, 202 | tx_out& out, 203 | size_t& output_index) 204 | { 205 | 206 | size_t idx {0}; 207 | 208 | 209 | // search in the ouputs for an output which 210 | // public key matches to what we want 211 | auto it = std::find_if(tx.vout.begin(), tx.vout.end(), 212 | [&](const tx_out& o) 213 | { 214 | public_key found_output_pubkey; 215 | cryptonote::get_output_public_key( 216 | o, found_output_pubkey); 217 | 218 | ++idx; 219 | 220 | return found_output_pubkey == output_pubkey; 221 | }); 222 | 223 | if (it != tx.vout.end()) 224 | { 225 | // we found the desired public key 226 | out = *it; 227 | output_index = idx > 0 ? idx - 1 : idx; 228 | 229 | //cout << idx << " " << output_index << endl; 230 | 231 | return true; 232 | } 233 | 234 | return false; 235 | } 236 | 237 | 238 | uint64_t 239 | MicroCore::get_blk_timestamp(uint64_t blk_height) 240 | { 241 | cryptonote::block blk; 242 | 243 | if (!get_block_by_height(blk_height, blk)) 244 | { 245 | cerr << "Cant get block by height: " << blk_height << endl; 246 | return 0; 247 | } 248 | 249 | return blk.timestamp; 250 | } 251 | 252 | bool 253 | init_blockchain(const string& path, 254 | MicroCore& mcore, 255 | Blockchain*& core_storage, 256 | network_type nt) 257 | { 258 | 259 | // initialize the core using the blockchain path 260 | if (!mcore.init(path, nt)) 261 | { 262 | cerr << "Error accessing blockchain." << endl; 263 | return false; 264 | } 265 | 266 | // get the high level Blockchain object to interact 267 | // with the blockchain lmdb database 268 | core_storage = &(mcore.get_core()); 269 | 270 | return true; 271 | } 272 | 273 | 274 | bool 275 | MicroCore::get_block_complete_entry(block const& b, block_complete_entry& bce) 276 | { 277 | bce.block = cryptonote::block_to_blob(b); 278 | 279 | for (const auto &tx_hash: b.tx_hashes) 280 | { 281 | transaction tx; 282 | 283 | if (!get_tx(tx_hash, tx)) 284 | return false; 285 | 286 | cryptonote::blobdata txblob = tx_to_blob(tx); 287 | 288 | bce.txs.push_back(txblob); 289 | } 290 | 291 | return true; 292 | } 293 | 294 | 295 | string 296 | MicroCore::get_blkchain_path() 297 | { 298 | return blockchain_path; 299 | } 300 | 301 | hw::device* const 302 | MicroCore::get_device() const 303 | { 304 | return m_device; 305 | } 306 | 307 | } 308 | -------------------------------------------------------------------------------- /src/MicroCore.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #ifndef XMREG01_MICROCORE_H 6 | #define XMREG01_MICROCORE_H 7 | 8 | #include 9 | 10 | #include "monero_headers.h" 11 | #include "tools.h" 12 | 13 | namespace xmreg 14 | { 15 | using namespace cryptonote; 16 | using namespace crypto; 17 | using namespace std; 18 | 19 | /** 20 | * Micro version of cryptonode::core class 21 | * Micro version of constructor, 22 | * init and destructor are implemted. 23 | * 24 | * Just enough to read the blockchain 25 | * database for use in the example. 26 | */ 27 | class MicroCore { 28 | 29 | string blockchain_path; 30 | 31 | tx_memory_pool m_mempool; 32 | Blockchain m_blockchain_storage; 33 | 34 | hw::device* m_device; 35 | 36 | network_type nettype; 37 | 38 | public: 39 | MicroCore(); 40 | 41 | bool 42 | init(const string& _blockchain_path, network_type nt); 43 | 44 | Blockchain& 45 | get_core(); 46 | 47 | tx_memory_pool& 48 | get_mempool(); 49 | 50 | bool 51 | get_block_by_height(const uint64_t& height, block& blk); 52 | 53 | bool 54 | get_tx(const crypto::hash& tx_hash, transaction& tx); 55 | 56 | bool 57 | get_tx(const string& tx_hash, transaction& tx); 58 | 59 | bool 60 | find_output_in_tx(const transaction& tx, 61 | const public_key& output_pubkey, 62 | tx_out& out, 63 | size_t& output_index); 64 | 65 | uint64_t 66 | get_blk_timestamp(uint64_t blk_height); 67 | 68 | bool 69 | get_block_complete_entry(block const& b, block_complete_entry& bce); 70 | 71 | string 72 | get_blkchain_path(); 73 | 74 | hw::device* const 75 | get_device() const; 76 | }; 77 | 78 | 79 | 80 | bool 81 | init_blockchain(const string& path, 82 | MicroCore& mcore, 83 | Blockchain*& core_storage, 84 | network_type nt); 85 | 86 | 87 | } 88 | 89 | 90 | 91 | #endif //XMREG01_MICROCORE_H 92 | -------------------------------------------------------------------------------- /src/crypto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.2) 2 | 3 | project(mycrypto) 4 | 5 | set(SOURCE_FILES 6 | rx-slow-hash.c) 7 | 8 | # make static library called libmyxrm 9 | # that we are going to link to 10 | # in the root CMakeLists.txt file 11 | add_library(mycrypto 12 | STATIC 13 | ${SOURCE_FILES}) 14 | 15 | -------------------------------------------------------------------------------- /src/monero_headers.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #ifndef XMREG01_MONERO_HEADERS_H_H 6 | #define XMREG01_MONERO_HEADERS_H_H 7 | 8 | #define DB_LMDB 2 9 | #define BLOCKCHAIN_DB DB_LMDB 10 | 11 | 12 | #define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003" 13 | #define SIGNED_TX_PREFIX "Monero signed tx set\003" 14 | #define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" 15 | #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" 16 | 17 | #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks 18 | 19 | #include "version.h" 20 | 21 | #include "net/http_client.h" 22 | #include "storages/http_abstract_invoke.h" 23 | 24 | #include "cryptonote_core/tx_pool.h" 25 | #include "cryptonote_core/blockchain.h" 26 | #include "blockchain_db/lmdb/db_lmdb.h" 27 | #include "device/device_default.hpp" 28 | 29 | #include "wallet/wallet2.h" 30 | 31 | #include "serialization/binary_utils.h" 32 | 33 | #include "ringct/rctTypes.h" 34 | #include "ringct/rctOps.h" 35 | #include "ringct/rctSigs.h" 36 | 37 | #include "easylogging++.h" 38 | 39 | #include "common/base58.h" 40 | 41 | #include "string_coding.h" 42 | 43 | 44 | #endif //XMREG01_MONERO_HEADERS_H_H 45 | 46 | -------------------------------------------------------------------------------- /src/rpccalls.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 13/04/16. 3 | // 4 | 5 | #include "rpccalls.h" 6 | 7 | namespace xmreg 8 | { 9 | 10 | 11 | rpccalls::rpccalls( 12 | string _daemon_url, 13 | login_opt login, 14 | uint64_t _timeout) 15 | : daemon_url {_daemon_url}, 16 | timeout_time {_timeout} 17 | { 18 | epee::net_utils::parse_url(daemon_url, url); 19 | 20 | port = std::to_string(url.port); 21 | 22 | timeout_time_ms = std::chrono::milliseconds {timeout_time}; 23 | 24 | m_http_client.set_server( 25 | daemon_url, 26 | login, 27 | epee::net_utils::ssl_support_t::e_ssl_support_disabled); 28 | } 29 | 30 | bool 31 | rpccalls::connect_to_monero_daemon() 32 | { 33 | //std::lock_guard guard(m_daemon_rpc_mutex); 34 | 35 | if(m_http_client.is_connected()) 36 | { 37 | return true; 38 | } 39 | 40 | return m_http_client.connect(timeout_time_ms); 41 | } 42 | 43 | 44 | bool 45 | rpccalls::get_base_fee_estimate(uint64_t grace_blocks, 46 | uint64_t& fee_estimate) { 47 | 48 | cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req; 49 | cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response res; 50 | 51 | req.grace_blocks = grace_blocks; 52 | 53 | if (!connect_to_monero_daemon()) 54 | { 55 | cerr << "get_base_fee_estimate: not connected to daemon" << endl; 56 | return false; 57 | } 58 | 59 | bool r = epee::net_utils::invoke_http_json( 60 | "/get_fee_estimate", 61 | req, res, m_http_client, timeout_time_ms); 62 | 63 | fee_estimate = res.fee; 64 | 65 | return r; 66 | 67 | 68 | } 69 | 70 | uint64_t 71 | rpccalls::get_current_height() 72 | { 73 | COMMAND_RPC_GET_HEIGHT::request req; 74 | COMMAND_RPC_GET_HEIGHT::response res; 75 | 76 | std::lock_guard guard(m_daemon_rpc_mutex); 77 | 78 | if (!connect_to_monero_daemon()) 79 | { 80 | cerr << "get_current_height: not connected to daemon" << endl; 81 | return false; 82 | } 83 | 84 | bool r = epee::net_utils::invoke_http_json( 85 | "/getheight", 86 | req, res, m_http_client, timeout_time_ms); 87 | 88 | if (!r) 89 | { 90 | cerr << "Error connecting to Monero daemon at " 91 | << daemon_url << endl; 92 | return 0; 93 | } 94 | 95 | return res.height; 96 | } 97 | 98 | bool 99 | rpccalls::get_mempool(vector& mempool_txs) 100 | { 101 | 102 | COMMAND_RPC_GET_TRANSACTION_POOL::request req; 103 | COMMAND_RPC_GET_TRANSACTION_POOL::response res; 104 | 105 | bool r; 106 | 107 | { 108 | std::lock_guard guard(m_daemon_rpc_mutex); 109 | 110 | if (!connect_to_monero_daemon()) 111 | { 112 | cerr << "get_mempool: not connected to daemon" << endl; 113 | return false; 114 | } 115 | 116 | r = epee::net_utils::invoke_http_json( 117 | "/get_transaction_pool", 118 | req, res, m_http_client, timeout_time_ms); 119 | } 120 | 121 | if (!r || res.status != CORE_RPC_STATUS_OK) 122 | { 123 | cerr << "Error connecting to Monero daemon at " 124 | << daemon_url << endl; 125 | return false; 126 | } 127 | 128 | mempool_txs = res.transactions; 129 | 130 | // mempool txs are not sorted base on their arival time, 131 | // so we sort it here. 132 | 133 | std::sort(mempool_txs.begin(), mempool_txs.end(), 134 | [](tx_info& t1, tx_info& t2) 135 | { 136 | return t1.receive_time > t2.receive_time; 137 | }); 138 | 139 | return true; 140 | } 141 | 142 | 143 | bool 144 | rpccalls::commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg) 145 | { 146 | COMMAND_RPC_SEND_RAW_TX::request req; 147 | COMMAND_RPC_SEND_RAW_TX::response res; 148 | 149 | req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer( 150 | tx_to_blob(ptx.tx) 151 | ); 152 | 153 | req.do_not_relay = false; 154 | 155 | std::lock_guard guard(m_daemon_rpc_mutex); 156 | 157 | if (!connect_to_monero_daemon()) 158 | { 159 | cerr << "commit_tx: not connected to daemon" << endl; 160 | return false; 161 | } 162 | 163 | bool r = epee::net_utils::invoke_http_json( 164 | "/sendrawtransaction", 165 | req, res, m_http_client, timeout_time_ms); 166 | 167 | if (!r || res.status == "Failed") 168 | { 169 | error_msg = res.reason; 170 | 171 | cerr << "Error sending tx: " << res.reason << endl; 172 | return false; 173 | } 174 | 175 | return true; 176 | } 177 | 178 | bool 179 | rpccalls::get_network_info(COMMAND_RPC_GET_INFO::response& response) 180 | { 181 | 182 | epee::json_rpc::request 183 | req_t = AUTO_VAL_INIT(req_t); 184 | epee::json_rpc::response 185 | resp_t = AUTO_VAL_INIT(resp_t); 186 | 187 | bool r {false}; 188 | 189 | req_t.jsonrpc = "2.0"; 190 | req_t.id = epee::serialization::storage_entry(0); 191 | req_t.method = "get_info"; 192 | 193 | { 194 | std::lock_guard guard(m_daemon_rpc_mutex); 195 | 196 | if (!connect_to_monero_daemon()) 197 | { 198 | cerr << "get_network_info: not connected to daemon" << endl; 199 | return false; 200 | } 201 | 202 | r = epee::net_utils::invoke_http_json("/json_rpc", 203 | req_t, resp_t, 204 | m_http_client); 205 | } 206 | 207 | string err; 208 | 209 | if (r) 210 | { 211 | if (resp_t.result.status == CORE_RPC_STATUS_BUSY) 212 | { 213 | err = "Daemon is busy. Please try again later."; 214 | } 215 | else if (resp_t.result.status != CORE_RPC_STATUS_OK) 216 | { 217 | err = resp_t.result.status; 218 | } 219 | 220 | if (!err.empty()) 221 | { 222 | cerr << "Error connecting to Monero daemon due to " 223 | << err << endl; 224 | return false; 225 | } 226 | } 227 | else 228 | { 229 | cerr << "Error connecting to Monero daemon at " 230 | << daemon_url << endl; 231 | return false; 232 | } 233 | 234 | response = resp_t.result; 235 | 236 | return true; 237 | } 238 | 239 | 240 | bool 241 | rpccalls::get_hardfork_info(COMMAND_RPC_HARD_FORK_INFO::response& response) 242 | { 243 | epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); 244 | epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); 245 | 246 | 247 | bool r {false}; 248 | 249 | req_t.jsonrpc = "2.0"; 250 | req_t.id = epee::serialization::storage_entry(0); 251 | req_t.method = "hard_fork_info"; 252 | 253 | { 254 | std::lock_guard guard(m_daemon_rpc_mutex); 255 | 256 | if (!connect_to_monero_daemon()) 257 | { 258 | cerr << "get_hardfork_info: not connected to daemon" << endl; 259 | return false; 260 | } 261 | 262 | r = epee::net_utils::invoke_http_json("/json_rpc", 263 | req_t, resp_t, 264 | m_http_client); 265 | } 266 | 267 | 268 | string err; 269 | 270 | if (r) 271 | { 272 | if (resp_t.result.status == CORE_RPC_STATUS_BUSY) 273 | { 274 | err = "daemon is busy. Please try again later."; 275 | } 276 | else if (resp_t.result.status != CORE_RPC_STATUS_OK) 277 | { 278 | err = resp_t.result.status; 279 | } 280 | 281 | if (!err.empty()) 282 | { 283 | cerr << "Error connecting to Monero daemon due to " 284 | << err << endl; 285 | return false; 286 | } 287 | } 288 | else 289 | { 290 | cerr << "Error connecting to Monero daemon at " 291 | << daemon_url << endl; 292 | return false; 293 | } 294 | 295 | response = resp_t.result; 296 | 297 | return true; 298 | } 299 | 300 | 301 | 302 | bool 303 | rpccalls::get_dynamic_per_kb_fee_estimate( 304 | uint64_t grace_blocks, 305 | uint64_t& fee, 306 | string& error_msg) 307 | { 308 | epee::json_rpc::request 309 | req_t = AUTO_VAL_INIT(req_t); 310 | epee::json_rpc::response 311 | resp_t = AUTO_VAL_INIT(resp_t); 312 | 313 | 314 | req_t.jsonrpc = "2.0"; 315 | req_t.id = epee::serialization::storage_entry(0); 316 | req_t.method = "get_fee_estimate"; 317 | req_t.params.grace_blocks = grace_blocks; 318 | 319 | bool r {false}; 320 | 321 | { 322 | std::lock_guard guard(m_daemon_rpc_mutex); 323 | 324 | if (!connect_to_monero_daemon()) 325 | { 326 | cerr << "get_dynamic_per_kb_fee_estimate: not connected to daemon" << endl; 327 | return false; 328 | } 329 | 330 | r = epee::net_utils::invoke_http_json("/json_rpc", 331 | req_t, resp_t, 332 | m_http_client); 333 | } 334 | 335 | string err; 336 | 337 | 338 | if (r) 339 | { 340 | if (resp_t.result.status == CORE_RPC_STATUS_BUSY) 341 | { 342 | err = "daemon is busy. Please try again later."; 343 | } 344 | else if (resp_t.result.status != CORE_RPC_STATUS_OK) 345 | { 346 | err = resp_t.result.status; 347 | } 348 | 349 | if (!err.empty()) 350 | { 351 | cerr << "Error connecting to Monero daemon due to " 352 | << err << endl; 353 | return false; 354 | } 355 | } 356 | else 357 | { 358 | cerr << "Error connecting to Monero daemon at " 359 | << daemon_url << endl; 360 | return false; 361 | } 362 | 363 | fee = resp_t.result.fee; 364 | 365 | return true; 366 | } 367 | 368 | 369 | 370 | bool 371 | rpccalls::get_block(string const& blk_hash, block& blk, string& error_msg) 372 | { 373 | epee::json_rpc::request req_t; 374 | epee::json_rpc::response resp_t; 375 | 376 | 377 | req_t.jsonrpc = "2.0"; 378 | req_t.id = epee::serialization::storage_entry(0); 379 | req_t.method = "getblock"; 380 | req_t.params.hash = blk_hash; 381 | 382 | bool r {false}; 383 | 384 | { 385 | std::lock_guard guard(m_daemon_rpc_mutex); 386 | 387 | if (!connect_to_monero_daemon()) 388 | { 389 | cerr << "get_block: not connected to daemon" << endl; 390 | return false; 391 | } 392 | 393 | r = epee::net_utils::invoke_http_json("/json_rpc", 394 | req_t, resp_t, 395 | m_http_client); 396 | } 397 | 398 | string err; 399 | 400 | 401 | if (r) 402 | { 403 | if (resp_t.result.status == CORE_RPC_STATUS_BUSY) 404 | { 405 | err = "daemon is busy. Please try again later."; 406 | } 407 | else if (resp_t.result.status != CORE_RPC_STATUS_OK) 408 | { 409 | err = resp_t.result.status; 410 | } 411 | 412 | if (!err.empty()) 413 | { 414 | cerr << "Error connecting to Monero daemon due to " 415 | << err << endl; 416 | return false; 417 | } 418 | } 419 | else 420 | { 421 | cerr << "get_block: error connecting to Monero daemon at " 422 | << daemon_url << endl; 423 | return false; 424 | } 425 | 426 | std::string block_bin_blob; 427 | 428 | if(!epee::string_tools::parse_hexstr_to_binbuff(resp_t.result.blob, block_bin_blob)) 429 | return false; 430 | 431 | return parse_and_validate_block_from_blob(block_bin_blob, blk); 432 | } 433 | 434 | 435 | 436 | } 437 | -------------------------------------------------------------------------------- /src/rpccalls.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 13/04/16. 3 | // 4 | 5 | 6 | #ifndef CROWXMR_RPCCALLS_H 7 | #define CROWXMR_RPCCALLS_H 8 | 9 | #include "monero_headers.h" 10 | 11 | #include "wipeable_string.h" 12 | 13 | #include 14 | #include 15 | 16 | 17 | 18 | 19 | namespace 20 | { 21 | 22 | // can be used to check if given class/struct exist 23 | // from: https://stackoverflow.com/a/10722840/248823 24 | template 25 | struct has_destructor 26 | { 27 | // has destructor 28 | template 29 | static std::true_type test(decltype(std::declval().~A()) *) 30 | { 31 | return std::true_type(); 32 | } 33 | 34 | // no constructor 35 | template 36 | static std::false_type test(...) 37 | { 38 | return std::false_type(); 39 | } 40 | 41 | /* This will be either `std::true_type` or `std::false_type` */ 42 | typedef decltype(test(0)) type; 43 | 44 | static const bool value = type::value; 45 | }; 46 | 47 | } 48 | 49 | 50 | namespace cryptonote 51 | { 52 | // declare struct in monero's cryptonote namespace. 53 | // monero should provide definition for this, 54 | // but we need to have it declared as we are going to 55 | // check if its definition exist or not. depending on this 56 | // we decide what gets to be defined as 57 | // get_alt_blocks(vector& alt_blocks_hashes); 58 | struct COMMAND_RPC_GET_ALT_BLOCKS_HASHES; 59 | } 60 | 61 | namespace xmreg 62 | { 63 | 64 | using namespace cryptonote; 65 | using namespace crypto; 66 | using namespace std; 67 | 68 | 69 | 70 | class rpccalls 71 | { 72 | string daemon_url ; 73 | uint64_t timeout_time; 74 | 75 | std::chrono::milliseconds timeout_time_ms; 76 | 77 | epee::net_utils::http::url_content url; 78 | 79 | epee::net_utils::http::http_simple_client m_http_client; 80 | 81 | std::mutex m_daemon_rpc_mutex; 82 | 83 | string port; 84 | 85 | public: 86 | 87 | using login_opt = boost::optional; 88 | 89 | rpccalls(string _daemon_url = "http:://127.0.0.1:18081", 90 | login_opt _login = login_opt {}, 91 | uint64_t _timeout = 200000); 92 | 93 | bool 94 | connect_to_monero_daemon(); 95 | 96 | uint64_t 97 | get_current_height(); 98 | 99 | bool 100 | get_mempool(vector& mempool_txs); 101 | 102 | bool 103 | commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg); 104 | 105 | bool 106 | get_network_info(COMMAND_RPC_GET_INFO::response& info); 107 | 108 | bool 109 | get_base_fee_estimate(uint64_t grace_blocks, uint64_t& fee_estimate); 110 | 111 | bool 112 | get_hardfork_info( COMMAND_RPC_HARD_FORK_INFO::response& res); 113 | 114 | bool 115 | get_dynamic_per_kb_fee_estimate( 116 | uint64_t grace_blocks, 117 | uint64_t& fee, 118 | string& error_msg); 119 | 120 | 121 | /** 122 | * This must be in the header for now, as it will be tempalte function 123 | * 124 | * @param alt_blocks_hashes 125 | * @return bool 126 | */ 127 | template 128 | typename enable_if::value, bool>::type 129 | get_alt_blocks(vector& alt_blocks_hashes) 130 | { 131 | // definition of COMMAND_RPC_GET_ALT_BLOCKS_HASHES exist 132 | // so perform rpc call to get this information 133 | 134 | bool r {false}; 135 | 136 | typename T::request req; 137 | typename T::response resp; 138 | 139 | { 140 | std::lock_guard guard(m_daemon_rpc_mutex); 141 | 142 | if (!connect_to_monero_daemon()) 143 | { 144 | cerr << "get_alt_blocks: not connected to daemon" << endl; 145 | return false; 146 | } 147 | 148 | r = epee::net_utils::invoke_http_json("/get_alt_blocks_hashes", 149 | req, resp, 150 | m_http_client); 151 | } 152 | 153 | string err; 154 | 155 | if (r) 156 | { 157 | if (resp.status == CORE_RPC_STATUS_BUSY) 158 | { 159 | err = "daemon is busy. Please try again later."; 160 | } 161 | else if (resp.status != CORE_RPC_STATUS_OK) 162 | { 163 | err = "daemon rpc failed. Please try again later."; 164 | } 165 | 166 | if (!err.empty()) 167 | { 168 | cerr << "Error connecting to Monero daemon due to " 169 | << err << endl; 170 | return false; 171 | } 172 | } 173 | else 174 | { 175 | cerr << "Error connecting to Monero daemon at " 176 | << daemon_url << endl; 177 | return false; 178 | } 179 | 180 | alt_blocks_hashes = resp.blks_hashes; 181 | 182 | return true; 183 | } 184 | 185 | template 186 | typename enable_if::value, bool>::type 187 | get_alt_blocks(vector& alt_blocks_hashes) 188 | { 189 | cerr << "COMMAND_RPC_GET_ALT_BLOCKS_HASHES does not exist!" << endl; 190 | // definition of COMMAND_RPC_GET_ALT_BLOCKS_HASHES does NOT exist 191 | // so dont do anything 192 | return false; 193 | } 194 | 195 | bool 196 | get_block(string const& blk_hash, block& blk, string& error_msg); 197 | 198 | }; 199 | 200 | 201 | } 202 | 203 | 204 | 205 | #endif //CROWXMR_RPCCALLS_H 206 | -------------------------------------------------------------------------------- /src/templates/address.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Address:
{{xmr_address}}

5 |
6 | 7 | {{#testnet}} 8 |

Network type: Testnet

9 | {{/testnet}} 10 | {{#stagenet}} 11 |

Network type: Stagenet

12 | {{/stagenet}} 13 | {{^testnet}}{{^stagenet}} 14 |

Network type: Mainnet

15 | {{/stagenet}}{{/testnet}} 16 | 17 |
18 |

Associated public keys

19 | 20 |

View key: {{public_viewkey}}

21 |

Spend key: {{public_spendkey}}

22 |
23 | 24 | {{#is_integrated_addr}} 25 |

Payment id: {{encrypted_payment_id}}

26 | {{/is_integrated_addr}} 27 | 28 | 29 |

30 | Transactions:
Sorry, its not possible to find txs associated with 31 | normal addresses in Monero 32 |

33 | 34 |
35 | 36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /src/templates/altblocks.html: -------------------------------------------------------------------------------- 1 |

2 | Alternative blocks 3 |

4 |

(no of alt blocks: {{no_alt_blocks}})

5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{#blocks}} 15 | 16 | 17 | 18 | 19 | 20 | 21 | {{/blocks}} 22 |
heightagehashtxs no
{{height}}{{age}}{{hash}}{{no_of_txs}}
23 | 24 | 25 |
26 | -------------------------------------------------------------------------------- /src/templates/block.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |

Block hash (height): {{blk_hash}} ({{blk_height}})

5 | 6 | 7 | {{#have_prev_hash}} 8 |
Previous block: {{prev_hash}}
9 | {{/have_prev_hash}} 10 | 11 | {{#have_next_hash}} 12 |
Next block: {{next_hash}}
13 | {{/have_next_hash}} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | {{#is_randomx}} 37 | 38 | {{/is_randomx}} 39 | {{^is_randomx}} 40 | 41 | {{/is_randomx}} 42 | 43 | 44 |
Timestamp [UTC] (epoch):{{blk_timestamp}} ({{blk_timestamp_epoch}})Age {{age_format}}:{{blk_age}}Δ [h:m:s]:{{delta_time}}
Major.minor version:{{major_ver}}.{{minor_ver}}Block reward:{{blk_reward}}Block size [kB]:{{blk_size}}
nonce:{{blk_nonce}}Total fees:{{sum_fees}}No of txs:{{no_txs}}
PoW hash:{{blk_pow_hash}}Difficulty:{{blk_difficulty}}RandomX source code
45 | 46 |

Miner reward transaction

47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {{#coinbase_txs}} 55 | 56 | 58 | 59 | 60 | 61 | {{/coinbase_txs}} 62 | 63 |
hashoutputssize [kB]version
{{hash}} 57 | {{sum_outputs}}{{tx_size}}{{version}}
64 | 65 | 66 | 67 |

Transactions ({{no_txs}})

68 | {{#have_txs}} 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {{#blk_txs}} 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | {{/blk_txs}} 88 |
hashoutputsfee [µɱ]in/outsize [kB]version
{{hash}}{{sum_outputs}}{{fee_micro}}{{no_inputs}}/{{no_outputs}}{{tx_size}}{{version}}
89 | {{/have_txs}} 90 | 91 | 92 | {{#enable_as_hex}} 93 |
94 | Block as hex 95 | | Complete block as hex 96 |
97 | {{/enable_as_hex}} 98 | 99 |
100 | -------------------------------------------------------------------------------- /src/templates/checkrawkeyimgs.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |

Signed Key Images Checker

7 |
8 | 9 | 10 |

Data file prefix: {{data_prefix}}

11 | 12 | {{#has_error}} 13 |

Attempt failed

14 |

{{error_msg}}

15 | {{/has_error}} 16 | {{^has_error}} 17 |

Key images for address: {{address}}

18 |

Viewkey: {{viewkey}}

19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | {{#key_imgs}} 30 | 31 | 32 | 33 | 34 | 42 | 43 | {{/key_imgs}} 44 |
Key no.Key imageIs spent?
{{key_no}}{{key_image}} 35 | {{#is_spent}} 36 | {{is_spent}} 37 | {{/is_spent}} 38 | {{^is_spent}} 39 | {{is_spent}} 40 | {{/is_spent}} 41 |
45 |
46 |
47 | 48 |
49 | 50 | {{/has_error}} 51 | 52 |
-------------------------------------------------------------------------------- /src/templates/checkrawoutputkeys.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

Signed Outputs Public Keys Checker

6 |
7 | 8 |

Data file prefix: {{data_prefix}}

9 | 10 | {{#has_error}} 11 |

Attempt failed

12 |

{{error_msg}}

13 | {{/has_error}} 14 | {{^has_error}} 15 |

Output keys for address: {{address}}

16 |

Viewkey: {{viewkey}}

17 | {{#has_total_xmr}} 18 |

Total xmr: {{total_xmr}}

19 | {{/has_total_xmr}} 20 | 21 |
22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | {{#are_key_images_known}} 31 | 32 | {{/are_key_images_known}} 33 | 34 | 35 | {{#output_keys}} 36 | 37 | 38 | 39 | 40 | 41 | 42 | {{#are_key_images_known}} 43 | 50 | {{/are_key_images_known}} 51 | 52 | 53 | 54 | {{/output_keys}} 55 |
Output no.Public keyTimestampRingCTIs spent?Amount
{{output_no}}{{output_pub_key}}{{timestamp}}{{is_ringct}}{{^is_spent}} 44 | {{is_spent}} 45 | {{/is_spent}} 46 | {{#is_spent}} 47 | {{is_spent}} 48 | {{/is_spent}} 49 | {{amount}}
56 |
57 |
58 | 59 |
60 | 61 | {{/has_error}} 62 | 63 |
-------------------------------------------------------------------------------- /src/templates/checkrawtx.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

Transaction checking

6 |
7 | 8 |
9 | 10 | {{#has_error}} 11 |

Checking tx failed

12 |

{{error_msg}}

13 | {{/has_error}} 14 | {{^has_error}} 15 |

Data file prefix: {{data_prefix}}

16 | {{/has_error}} 17 | 18 | {{#unsigned_tx_given}} 19 |

Details of unsigned raw tx data given

20 | 21 | {{#txs}} 22 |
23 |

Basic information

24 |
25 | {{#dest_infos}} 26 | Send {{dest_amount}} to {{dest_address}}
27 | {{/dest_infos}} 28 | 29 | {{#has_payment_id}} 30 | Payment id: {{payment_id}}
31 | {{/has_payment_id}} 32 | 33 | {{#has_payment_id8}} 34 | Payment id (encrypted): {{payment_id8}}
35 | {{/has_payment_id8}} 36 |
37 | 38 |

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

42 |
43 |
    44 | {{#timescales}} 45 |
  • |{{timescale}}|
  • 46 | {{/timescales}} 47 |
48 |
49 | 50 | 51 |

Outputs selected for this tx (total: {{sum_outputs_amounts}})

52 | {{#dest_sources}} 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {{#outputs}} 62 | 63 | 64 | 65 | {{#is_real}} 66 | 67 | {{/is_real}} 68 | {{^is_real}} 69 | 70 | {{/is_real}} 71 | 72 | 73 | 74 | 75 | {{/outputs}} 76 |
Output IndexStealth addressIs this real outputAge {{age_format}}Amount
{{out_index}}{{out_pub_key}}{{is_real}}{{is_real}}{{output_age}}{{output_amount}}
77 | {{/dest_sources}} 78 |

Change to be returned to the sender: {{change_amount}}

79 |
80 | {{/txs}} 81 | {{/unsigned_tx_given}} 82 | 83 | {{^unsigned_tx_given}} 84 |

Details of signed raw tx data given

85 | {{#txs}} 86 |
87 | {{#dest_infos}} 88 | {{^is_this_change}} 89 | Send {{dest_amount}} to {{dest_address}}
90 | {{/is_this_change}} 91 | {{#is_this_change}} 92 | Total change {{dest_amount}} back to {{dest_address}}
93 | {{/is_this_change}} 94 | {{/dest_infos}} 95 |
96 | {{>tx_details}} 97 | {{#tx_json}} 98 |
99 |

JSON representation of tx

100 |
101 | 102 | {{tx_json}} 103 | 104 |
105 | {{/tx_json}} 106 | {{/txs}} 107 | 108 | {{/unsigned_tx_given}} 109 | 110 | 111 |
112 | 113 |
114 |
115 | -------------------------------------------------------------------------------- /src/templates/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | color: white; 5 | background-color: black; 6 | } 7 | 8 | h1, h2, h3, h4, h5, h6 { 9 | text-align: center; 10 | } 11 | 12 | .center { 13 | margin: auto; 14 | width: 96%; 15 | /*border: 1px solid #73AD21; 16 | padding: 10px;*/ 17 | } 18 | 19 | tr, li, #pages, .info { 20 | font-family: "Lucida Console", Monaco, monospace; 21 | font-size : 12px; 22 | height: 22px; 23 | } 24 | 25 | #pages 26 | { 27 | margin-top: 15px; 28 | } 29 | 30 | td { 31 | text-align: center; 32 | word-break: break-all; 33 | } 34 | 35 | a:link { 36 | text-decoration: none; 37 | color: white; 38 | } 39 | 40 | a:visited { 41 | text-decoration: none; 42 | color: white; 43 | } 44 | 45 | a:hover { 46 | text-decoration: underline; 47 | color: white; 48 | } 49 | 50 | a:active { 51 | text-decoration: none; 52 | color: white; 53 | } 54 | 55 | form { 56 | display: inline-block; 57 | text-align: center; 58 | } 59 | 60 | .style-1 input[type="text"] { 61 | padding: 2px; 62 | border: solid 1px #dcdcdc; 63 | transition: box-shadow 0.3s, border 0.3s; 64 | } 65 | .style-1 input[type="text"]:focus, 66 | .style-1 input[type="text"].focus { 67 | border: solid 1px #707070; 68 | box-shadow: 0 0 5px 1px #969696; 69 | } 70 | 71 | 72 | .tabs { 73 | position: relative; 74 | min-height: 240px; 75 | clear: both; 76 | margin: 25px 0; 77 | } 78 | 79 | .tab { 80 | float: left; 81 | } 82 | 83 | .tab label { 84 | background: black; 85 | padding: 10px; 86 | border: 1px solid #ccc; 87 | margin-left: -1px; 88 | position: relative; 89 | left: 1px; 90 | } 91 | 92 | .tab [type=radio] { 93 | display: none; 94 | } 95 | 96 | .content { 97 | position: absolute; 98 | top: 28px; 99 | left: 0; 100 | background: black; 101 | right: 0; 102 | bottom: 0; 103 | padding: 20px; 104 | border: 1px solid #ccc; 105 | display: none; 106 | } 107 | 108 | [type=radio]:checked ~ label { 109 | background: #505050 ; 110 | border-bottom: 1px solid white; 111 | z-index: 2; 112 | } 113 | 114 | [type=radio]:checked ~ label ~ .content { 115 | z-index: 1; 116 | } 117 | 118 | #tab-1:checked ~ .content.tab-1, 119 | #tab-2:checked ~ .content.tab-2 { 120 | display: block; 121 | } 122 | 123 | input#toggle-1[type=checkbox] { 124 | position: absolute; 125 | /*top: -9999px;*/ 126 | left: -9999px; 127 | 128 | } 129 | label#show-decoded-inputs { 130 | /*-webkit-appearance: push-button;*/ 131 | /*-moz-appearance: button;*/ 132 | display: inline-block; 133 | /*margin: 60px 0 10px 0;*/ 134 | cursor: pointer; 135 | background-color: black;; 136 | color: white; 137 | width: 100%; 138 | text-align: center; 139 | } 140 | 141 | div#decoded-inputs{ 142 | display: none; 143 | } 144 | 145 | /* Toggled State */ 146 | input#toggle-1[type=checkbox]:checked ~ div#decoded-inputs { 147 | display: block; 148 | } 149 | -------------------------------------------------------------------------------- /src/templates/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | source code 4 | | explorer version (api): {{git_branch_name}}-{{last_git_commit_date}}-{{last_git_commit_hash}} ({{api}}) 5 | | monero version: {{monero_version_full}} 6 |
7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /src/templates/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#refresh}} 8 | 9 | {{/refresh}} 10 | Onion Monero Blockchain Explorer 11 | 12 | 15 | 16 | 17 | 18 |
19 | 20 |
21 |

Onion Monero Blockchain Explorer

22 |

(no javascript - no cookies - no web analytics trackers - no images - open sourced)

23 |
24 | 25 | 26 |
27 |
28 | 30 | 31 |
32 |
33 | 34 |
35 | -------------------------------------------------------------------------------- /src/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | 5 | Server time: {{server_timestamp}} | 6 | 7 | {{#refresh}} 8 | Autorefresh is ON (10 s) 9 | {{/refresh}} 10 | {{^refresh}} 11 | Autorefresh is OFF 12 | {{/refresh}} 13 |

14 |
15 | 16 | {{{mempool_info}}} 17 | 18 | {{#is_page_zero}} 19 |

100 recent blocks

20 | {{/is_page_zero}} 21 | {{^is_page_zero}} 22 |

older blocks

23 | {{/is_page_zero}} 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {{#blocks}} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {{/blocks}} 50 |
heightage {{age_format}} (Δm)block hashtxsfeesoutputsring sizesize [kB]
{{height}}{{age}} ({{time_delta}}){{hash}}{{notx}}{{fees}}{{xmr_outputs}}{{mixin_range}}{{blksize}}
51 | 52 |
53 | {{^is_page_zero}} 54 | previous page | 55 | first page | 56 | {{/is_page_zero}} 57 | current page: {{page_no}}/{{total_page_no}} 58 | | next page 59 |
60 | 61 |
62 | -------------------------------------------------------------------------------- /src/templates/index2.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | 5 | Server time: {{server_timestamp}} | Transaction pool 6 | {{#enable_pusher}} 7 | | Transaction pusher 8 | {{/enable_pusher}} 9 | {{#enable_key_image_checker}} 10 | | Key images checker 11 | {{/enable_key_image_checker}} 12 | {{#enable_output_key_checker}} 13 | | Output keys checker 14 | {{/enable_output_key_checker}} 15 | {{#enable_autorefresh_option}} 16 | | 17 | {{#refresh}} 18 | Autorefresh is ON (10 s) 19 | {{/refresh}} 20 | {{^refresh}} 21 | Autorefresh is OFF 22 | {{/refresh}} 23 | {{/enable_autorefresh_option}} 24 | {{#testnet_url}} 25 | | Go to testnet explorer 26 | {{/testnet_url}} 27 | {{#stagenet_url}} 28 | | Go to stagenet explorer 29 | {{/stagenet_url}} 30 | {{#mainnet_url}} 31 | | Go to mainnet explorer 32 | {{/mainnet_url}} 33 | {{#testnet}} 34 | | This is testnet blockchain 35 | {{/testnet}} 36 | {{#stagenet}} 37 | | This is stagenet blockchain 38 | {{/stagenet}} 39 | 40 |

41 | 42 | 43 | {{#network_info}} 44 |

45 | Network difficulty: {{difficulty}} 46 | | Hard fork: v{{current_hf_version}} 47 | | Hash rate: {{hash_rate}} 48 | | Fee per byte: {{fee_per_kb}} 49 | | Median block size limit: {{block_size_limit}} kB 50 | {{^is_current_info}} 51 | | Data from {{age}} {{age_format}} ago 52 | {{/is_current_info}} 53 |

54 | {{/network_info}} 55 | 56 | {{#emission}} 57 |

58 | Monero emission (fees) is {{amount}} ({{fee_amount}}) as of {{blk_no}} block 59 |

60 | {{/emission}} 61 | 62 | 63 |
64 | 65 | {{{mempool_info}}} 66 | 67 | {{#is_page_zero}} 68 |

Transactions in the last {{no_of_last_blocks}} blocks

69 | {{/is_page_zero}} 70 | {{^is_page_zero}} 71 |

Transactions in older blocks

72 | {{/is_page_zero}} 73 | 74 |

(Median size of 100 blocks: {{blk_size_median}} kB)

75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | {{#txs}} 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | {{/txs}} 103 |
heightage {{age_format}}size [kB]no txstransaction hashfee [µɱ]outputsin/outtx size [kB]
{{height}}{{age}}{{blk_size}}{{no_txs}}{{hash}}{{fee_micro}}{{sum_outputs_short}}{{no_inputs}}/{{no_outputs}}{{tx_size_short}}
104 | 105 |
106 | {{^is_page_zero}} 107 | previous page | 108 | first page | 109 | {{/is_page_zero}} 110 | current page: {{page_no}}/{{total_page_no}} 111 | | next page 112 |
113 | 114 |
115 | 116 | -------------------------------------------------------------------------------- /src/templates/mempool.html: -------------------------------------------------------------------------------- 1 |

2 | Transaction pool 3 |

4 |

(no of txs: {{mempool_size}}, size: {{mempool_size_kB}} kB, updated every {{ mempool_refresh_time }} seconds)

5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {{#mempooltxs}} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {{/mempooltxs}} 26 |
age [h:m:s]transaction hashfee/per_kB [µɱ]in/outtx size [kB]
{{age}}{{hash}}{{fee}}/{{payed_for_kB}}{{no_inputs}}/{{no_outputs}}{{txsize}}
27 | 28 | {{^mempool_fits_on_front_page}} 29 | {{#partial_mempool_shown}} 30 | 33 | {{/partial_mempool_shown}} 34 | 35 | {{/mempool_fits_on_front_page}} 36 | 37 | 38 |
39 | -------------------------------------------------------------------------------- /src/templates/mempool_error.html: -------------------------------------------------------------------------------- 1 |

2 | Transaction pool 3 |

4 |

5 |
6 | 7 |

Txpool data preparation for the front page failed. 8 | Its processing 9 | {{#network_info}}{{^is_pool_size_zero}}({{tx_pool_size}} txs){{/is_pool_size_zero}}{{/network_info}} 10 | took longer than expected and it timed out. 11 | To view the txpool without time constrain, 12 | go to dedicated txpool page: memory pool 13 |

14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /src/templates/my_outputs.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |

Tx hash: {{tx_hash}}

5 |
Tx public key: {{tx_pub_key}}
6 | 7 | 8 | {{#has_payment_id}} 9 |
Payment id: {{payment_id}}
10 | {{/has_payment_id}} 11 | 12 | {{#has_payment_id8}} 13 | {{^decrypted_payment_id8}} 14 |
Payment id (encrypted): {{payment_id8}}
15 | {{/decrypted_payment_id8}} 16 | {{#decrypted_payment_id8}} 17 |
Payment id (decrypted): {{decrypted_payment_id8}} 18 | (value incorrect if you are not the recipient of the tx)
19 | {{/decrypted_payment_id8}} 20 | {{/has_payment_id8}} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
Block: {{blk_height}}Timestamp [UTC]: {{blk_timestamp}}Age [y:d:h:m:s]: {{delta_time}}Fee: {{tx_fee}}Tx size: {{tx_size}} kB
33 | 34 | {{^tx_prove}} 35 |

Checking which outputs belong to the given address and viewkey

36 |
address: {{xmr_address}}
37 |
viewkey: {{viewkey}}
38 | {{/tx_prove}} 39 | {{#tx_prove}} 40 |

Prove that you send this tx to the given address

41 |
address: {{xmr_address}}
42 |
Tx private key: {{viewkey}}
43 | {{/tx_prove}} 44 | 45 |

Outputs ({{outputs_no}})

46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | {{#outputs}} 54 | 55 | 56 | 57 | {{#mine_output}} 58 | 63 | {{/mine_output}} 64 | {{^mine_output}} 65 | 66 | {{/mine_output}} 67 | 68 | {{/outputs}} 69 |
output public keyamountoutput match?
{{output_idx}}: {{out_pub_key}}{{amount}} 59 | 60 | {{mine_output}} 61 | 62 | {{mine_output}}
70 | 71 |

72 | Sum XMR from matched outputs (i.e., incoming XMR): 73 | {{#found_our_outputs}} 74 | {{sum_xmr}} 75 | {{/found_our_outputs}} 76 | {{^found_our_outputs}} 77 | 0.000000000000 78 | {{/found_our_outputs}} 79 |

80 |

81 | link to this page 82 |

83 | 84 |
85 | 86 | {{#show_inputs}} 87 |
88 | 89 | 90 | 91 |
92 |

Inputs ({{inputs_no}})

93 |
94 | {{#inputs}} 95 |

Key image: {{key_image}}, amount {{key_image_amount}}

96 | {{#mixins}} 97 | {{#has_mixin_outputs}} 98 | {{#mixin_outputs}} 99 |
100 | 101 | 102 | 108 | 109 | {{#has_found_outputs}} 110 | 135 | {{/has_found_outputs}} 136 |
103 | Ring member {{mixin_pub_key}} might use your outputs 104 |
105 | from tx of hash: {{mix_tx_hash}} 106 |
(tx public key: {{mix_tx_pub_key}}) 107 |
111 |
112 | 113 | 114 | 115 | 116 | 117 | 118 | {{#found_outputs}} 119 | 120 | 121 | 122 | 130 | 131 | {{/found_outputs}} 132 |
output public keyamountoutput match?
{{my_public_key}}{{amount}} 123 | {{#mine_output}} 124 | {{mine_output}}{{#out_in_match}}*{{/out_in_match}} 125 | {{/mine_output}} 126 | {{^mine_output}} 127 | {{mine_output}} 128 | {{/mine_output}} 129 |
133 |
134 |
137 |
138 | {{/mixin_outputs}} 139 | {{/has_mixin_outputs}} 140 | {{/mixins}} 141 | {{/inputs}} 142 | 143 |
144 |

145 | Sum XMR from matched and marked by * ring member's outputs: {{sum_mixin_xmr}} 146 |
147 | Possible spending is: 148 | {{possible_spending}} (tx fee included) 149 | 150 |
151 | Note: without private spendkey, 152 | it is impossible to know whether this is your real spending.
153 | So do not take this number seriously. 154 | It is probably totally wrong anyway.
155 |
156 | 157 | Number of possible our mixins is {{no_all_possible_mixins}} 158 | for {{all_possible_mixins_amount}} xmr 159 | (amount as uint64). 160 | 161 |

162 |
163 | 164 | {{/show_inputs}} 165 | 166 | 167 |
168 |
169 | -------------------------------------------------------------------------------- /src/templates/partials/tx_details.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |

Tx hash: {{tx_hash}}

5 | {{#enable_mixins_details}} 6 |
Tx prefix hash: {{tx_prefix_hash}}
7 | {{/enable_mixins_details}} 8 |
Tx public key: {{tx_pub_key}}
9 | 10 | 11 | {{#has_payment_id}} 12 |
Payment id: {{payment_id}}
13 | {{/has_payment_id}} 14 | 15 | {{#has_payment_id8}} 16 |
Payment id (encrypted): {{payment_id8}}
17 | {{/has_payment_id8}} 18 | 19 | 20 | {{#have_prev_hash}} 21 |
Previous tx: {{prev_hash}}
22 | {{/have_prev_hash}} 23 | 24 | {{#have_next_hash}} 25 |
Next tx: {{next_hash}}
26 | {{/have_next_hash}} 27 | 28 | 29 | 30 | 31 | {{^have_raw_tx}} 32 | 33 | 34 | 35 | 36 | 37 | {{/have_raw_tx}} 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
Timestamp: {{blk_timestamp_uint}}Timestamp [UTC]: {{blk_timestamp}}Age [y:d:h:m:s]: {{delta_time}}
Block: {{blk_height}}Fee (per_kB): {{tx_fee}} ({{payed_for_kB}})Tx size: {{tx_size}} kB
Tx version: {{tx_version}}No of confirmations: {{confirmations}}RingCT/type: {{#is_ringct}}yes/{{rct_type}}{{/is_ringct}}{{^is_ringct}}no{{/is_ringct}}
Extra: {{extra}}
56 | 57 | 58 |

{{outputs_no}} output(s) for total of {{outputs_xmr_sum}} xmr

59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {{#outputs}} 68 | 69 | 70 | 71 | 72 | 73 | 74 | {{/outputs}} 75 |
stealth addressamountamount idxtag
{{output_idx}}: {{out_pub_key}}{{amount}}{{amount_idx}} of {{num_outputs}}{{output_tag}}
76 |
77 | 78 | {{^have_raw_tx}} 79 |
80 |
81 | 82 |
83 | 84 | 85 |
86 |

Check which outputs belong to given Monero address/subaddress and viewkey

87 |
88 | For RingCT transactions, outputs' amounts are also decoded 89 |
90 | Note: address/subaddress and viewkey are sent to the server, as the calculations are done on the server side 91 |
92 |
93 |
94 |
95 |
96 | 97 | 98 | 99 |
100 |
101 |
102 | 103 |
104 | 105 | 106 | 107 |
108 |

Prove to someone that you have sent them Monero in this transaction

109 |
110 | Tx private key can be obtained using get_tx_key 111 | command in monero-wallet-cli command line tool 112 |
113 | Note: address/subaddress and tx private key are sent to the server, as the calculations are done on the server side 114 |
115 |
116 |
117 |
118 | 119 | 120 |
121 | 122 |
123 |
124 |
125 |
126 |
127 | {{/have_raw_tx}} 128 | 129 | 130 | {{#has_inputs}} 131 | {{#enable_mixins_details}} 132 |

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

135 |
136 |
    137 | {{#timescales}} 138 |
  • |{{timescale}}|
  • 139 | {{/timescales}} 140 |
141 |
142 | {{/enable_mixins_details}} 143 | 144 | {{^inputs_xmr_sum_not_zero}} 145 |

{{inputs_no}} input(s) for total of {{inputs_xmr_sum}} xmr

146 | {{/inputs_xmr_sum_not_zero}} 147 | {{#inputs_xmr_sum_not_zero}} 148 | {{^have_any_unknown_amount}} 149 |

{{inputs_no}} inputs(s) for total of {{inputs_xmr_sum}} xmr

150 | {{/have_any_unknown_amount}} 151 | {{#have_any_unknown_amount}} 152 |

{{inputs_no}} inputs(s) for total of at least {{inputs_xmr_sum}} xmr

153 | {{/have_any_unknown_amount}} 154 | {{/inputs_xmr_sum_not_zero}} 155 | 156 | {{#show_part_of_inputs}} 157 |
158 | Only {{max_no_of_inputs_to_show}} inputs are shown. To see all, 159 | click "more details" 160 |
161 | {{/show_part_of_inputs}} 162 | 163 |
164 | 165 | {{#inputs}} 166 | 167 | 180 | 181 | 182 | 183 | 244 | 245 | 246 | 266 | 267 | {{/inputs}} 268 |
168 | key image {{input_idx}}: {{in_key_img}} 169 | {{#have_raw_tx}} 170 | Already spent: 171 | {{#already_spent}} 172 | True 173 | {{/already_spent}} 174 | {{^already_spent}} 175 | False 176 | {{/already_spent}} 177 | 178 | {{/have_raw_tx}} 179 | amount: {{amount}}
184 | {{#enable_mixins_details}} 185 | 186 | 187 | 188 | {{#have_raw_tx}} 189 | 190 | {{/have_raw_tx}} 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | {{#mixins}} 199 | 200 | 201 | {{#have_raw_tx}} 202 | {{#mix_is_it_real}} 203 | 204 | {{/mix_is_it_real}} 205 | {{^mix_is_it_real}} 206 | 207 | {{/mix_is_it_real}} 208 | {{/have_raw_tx}} 209 | 210 | 211 | 212 | 213 | 214 | 215 | {{/mixins}} 216 |
ring membersIs it real?blkring sizein/outtimestampage [y:d:h:m:s]
- {{mix_idx}}: {{mix_pub_key}}{{mix_is_it_real}}{{mix_is_it_real}}{{mix_blk}}{{mix_mixin_no}}{{mix_inputs_no}}/{{mix_outputs_no}}{{mix_timestamp}}{{mix_age}}
217 | {{/enable_mixins_details}} 218 | {{^enable_mixins_details}} 219 | 220 | 221 | 222 | {{#have_raw_tx}} 223 | 224 | {{/have_raw_tx}} 225 | 226 | 227 | {{#mixins}} 228 | 229 | 230 | {{#have_raw_tx}} 231 | {{#mix_is_it_real}} 232 | 233 | {{/mix_is_it_real}} 234 | {{^mix_is_it_real}} 235 | 236 | {{/mix_is_it_real}} 237 | {{/have_raw_tx}} 238 | 239 | 240 | {{/mixins}} 241 |
ring membersIs it real?blk
- {{mix_idx}}: {{mix_pub_key}}{{mix_is_it_real}}{{mix_is_it_real}}{{mix_blk}}
242 | {{/enable_mixins_details}} 243 |
269 | 270 | 271 |
272 | 273 | 274 | {{/has_inputs}} 275 | 276 | {{^have_raw_tx}} 277 | {{^with_ring_signatures}} 278 | {{#show_more_details_link}} 279 |
280 | More details 281 | {{#enable_as_hex}} 282 | | Tx as hex 283 | | Ring member outputs/mixins as hex 284 | | Full ring member txs as hex 285 | {{/enable_as_hex}} 286 |
287 | {{/show_more_details_link}} 288 | {{/with_ring_signatures}} 289 | {{#with_ring_signatures}} 290 | 291 | 292 |
293 |
294 | 295 | {{tx_json}} 296 | 297 |
298 |
299 |

300 |
Less details
301 | {{/with_ring_signatures}} 302 | {{/have_raw_tx}} 303 | 304 |
305 | -------------------------------------------------------------------------------- /src/templates/partials/tx_table_header.html: -------------------------------------------------------------------------------- 1 | 2 | timestamp 3 | tx hash 4 | outputs 5 | fee 6 | ring size 7 | in/out 8 | size [kB] 9 | 10 | -------------------------------------------------------------------------------- /src/templates/partials/tx_table_row.html: -------------------------------------------------------------------------------- 1 | 2 | {{timestamp}} 3 | {{hash}} 4 | {{sum_outputs}} 5 | {{tx_fee}} 6 | {{mixin}} 7 | {{no_inputs}}/{{no_outputs}} 8 | {{tx_size}} 9 | 10 | -------------------------------------------------------------------------------- /src/templates/pushrawtx.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

Transaction Pusher

6 |
7 | 8 |
9 | 10 | {{#txs}} 11 |

Attempting to push tx: {{tx_hash}}

12 | {{/txs}} 13 | 14 | {{#has_error}} 15 |

Attempt failed

16 |

{{error_msg}}

17 | {{/has_error}} 18 | {{^has_error}} 19 |

Success

20 |

21 | Your tx should be already in the txpool waiting to be included 22 | in an upcoming block. 23 |

24 | 25 | {{#txs}} 26 |

Go to tx: {{tx_hash}}

27 | {{/txs}} 28 | 29 | {{/has_error}} 30 | 31 |
32 |
33 |
-------------------------------------------------------------------------------- /src/templates/randomx.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Block hash (height): {{blk_hash}} ({{blk_height}})

4 |

5 | Eight RandomX and corresponding assembly x86 programs used 6 | to calculate the PoW hash of the block.
7 | The RandomX programs are executed on RandomX virtual machine. 8 |

9 |
10 | Values of the integer and floating point registers 11 | are also provided. 12 |
13 | 14 | {{#rx_codes}} 15 |

Program #{{rx_code_idx}}

16 | 17 | {{#first_program}} 18 | 19 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 45 | 48 | 49 | 50 | 68 | 69 |
20 | Integer registers R:

21 | r0: 0x0000000000000000, r1: 0x0000000000000000, r2: 0x0000000000000000, r3: 0x0000000000000000
22 | r4: 0x0000000000000000, r5: 0x0000000000000000, r6: 0x0000000000000000, r7: 0x0000000000000000

23 | 24 | Floating point registers F {lo, hi}:

25 | Initial values not accessible

26 | 27 | Floating point registers E {lo, hi}:

28 | Initial values not accessible

29 | 30 | Floating point registers A {lo, hi}:

31 | a0: {{a0}}, a1: {{a1}}
32 | a2: {{a2}}, a3: {{a3}}

33 | {{/first_program}} 34 |
RandomX codeASM x86
43 |
{{rx_code}}
44 |
46 |
{{rx_code_asm}}
47 |
51 | Integer registers R:

52 | r0: {{r0}}, r1: {{r1}}, r2: {{r2}}, r3: {{r3}}
53 | r4: {{r4}}, r5: {{r5}}, r6: {{r6}}, r7: {{r7}}

54 | 55 | Floating point registers F {lo, hi}:

56 | f0: {{f0}}, f1: {{f1}}
57 | f2: {{f2}}, f3: {{f3}}

58 | 59 | Floating point registers E {lo, hi}:

60 | e0: {{e0}}, e1: {{e1}}
61 | e2: {{e2}}, e3: {{e3}}

62 | 63 | Floating point registers A {lo, hi}:

64 | a0: {{a0}}, a1: {{a1}}
65 | a2: {{a2}}, a3: {{a3}}

66 |
67 |
70 | {{/rx_codes}} 71 |
72 | -------------------------------------------------------------------------------- /src/templates/rawkeyimgs.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

Signed Key Images Checker

6 |
7 | 8 | 9 |
10 |
11 | Paste base64 encoded, signed key images data here
12 | (In Linux, can get base64 signed raw tx data: base64 your_key_images_file | xclip -selection clipboard)
13 | (In Windows, can get base64 signed raw tx data: certutil.exe -encode -f your_key_images_file encoded.txt & type "encoded.txt" | clip)
14 |

15 | Viewkey (key image file data is encoded using your viewkey. Thus is needed for decryption) 16 |
17 | Note: data and viewkey are sent to the server, as the calculations are done on the server side 18 |
19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 |
-------------------------------------------------------------------------------- /src/templates/rawoutputkeys.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Signed Outputs Public Keys Checker

5 |
6 | 7 | 8 |
9 |
10 | Paste base64 encoded, signed output keys data here
11 | (In Linux, can get base64 signed raw tx data: base64 your_output_keys_filename | xclip -selection clipboard)
12 | (In Windows, can get base64 signed raw tx data: certutil.exe -encode -f your_output_keys_filename encoded.txt & type "encoded.txt" | clip)
13 |

14 | Viewkey (output keys file data is encoded using your viewkey. Thus is needed for decryption) 15 |
16 | Note: data and viewkey are sent to the server, as the calculations are done on the server side 17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 | 25 |
-------------------------------------------------------------------------------- /src/templates/rawtx.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

Transaction Pusher

6 |
7 | 8 | 9 |
10 |
11 | Paste here either a hex string of raw transaction
12 | (the tx_blob response in the wallet RPC, or the raw_monero_tx 13 | file saved by the wallet CLI with --do-not-relay option specified),
14 | or base64 encoded, unsigned or signed transaction data
15 |
16 | (In Linux, can get the raw tx data: cat raw_monero_tx | xclip -selection clipboard)
17 | (In Windows, can get the raw tx data: certutil.exe -encode -f raw_monero_tx encoded.txt & type "encoded.txt" | clip)
18 | 19 | 20 |
21 | Note: data is sent to the server, as the calculations are done on the server side 22 |
23 | Note: "check" does not work for the hex string of raw transaction 24 |
25 | 26 |                     27 |                     28 | 29 |
30 |
31 | 32 |
33 |
34 | -------------------------------------------------------------------------------- /src/templates/search_results.html: -------------------------------------------------------------------------------- 1 | 2 |

Search results for: {{search_text}}

3 | 4 | {{#no_results}} 5 |

Nothing in the blockchain has been found that matches the search term :-(

6 |
Note: there might be some delay when newest txs become searchable
7 | {{/no_results}} 8 | 9 | {{#to_many_results}} 10 |

More than 500 results found. Showing no more than this

11 | {{/to_many_results}} 12 | 13 |
14 | 15 | {{#has_key_images}} 16 | 17 |

The search term matches key image found in the following transaction

18 | 19 | 20 | {{>tx_table_head}} 21 | {{#key_images}} 22 | {{>tx_table_row}} 23 | {{/key_images}} 24 |
25 | 26 | {{/has_key_images}} 27 | 28 |
29 | 30 |
31 | 32 | {{#has_tx_public_keys}} 33 | 34 |

The search term matches tx public key of the following transaction

35 | 36 | 37 | {{>tx_table_head}} 38 | {{#tx_public_keys}} 39 | {{>tx_table_row}} 40 | {{/tx_public_keys}} 41 |
42 | 43 | {{/has_tx_public_keys}} 44 | 45 |
46 | 47 |
48 | 49 | {{#has_payments_id}} 50 | 51 |

The search term matches payment id found in the following transaction(s)

52 | 53 | 54 | {{>tx_table_head}} 55 | {{#payments_id}} 56 | {{>tx_table_row}} 57 | {{/payments_id}} 58 |
59 | 60 | {{/has_payments_id}} 61 | 62 |
63 | 64 | {{#has_encrypted_payments_id}} 65 | 66 |

The search term matches encrypted payment id found in the following transaction(s)

67 | 68 | 69 | {{>tx_table_head}} 70 | {{#encrypted_payments_id}} 71 | {{>tx_table_row}} 72 | {{/encrypted_payments_id}} 73 |
74 | 75 | {{/has_encrypted_payments_id}} 76 | 77 | 78 | 79 |
80 | 81 | {{#has_output_public_keys}} 82 | 83 |

The search term matches output public key found in the following transaction(s)

84 | 85 | 86 | {{>tx_table_head}} 87 | {{#output_public_keys}} 88 | {{>tx_table_row}} 89 | {{/output_public_keys}} 90 |
91 | 92 | {{/has_output_public_keys}} 93 | 94 |
95 | 96 |
97 | 98 | {{#has_output_public_keys_based_on_global_idx}} 99 | 100 |

The search term matches global index of an output in the following transaction(s)

101 | 102 | 103 | {{>tx_table_head}} 104 | {{#output_public_keys_based_on_global_idx}} 105 | {{>tx_table_row}} 106 | {{/output_public_keys_based_on_global_idx}} 107 |
108 | 109 | {{/has_output_public_keys_based_on_global_idx}} 110 | 111 |
112 | 113 |
114 | 115 | {{#has_output_public_keys_based_on_amount_idx}} 116 | 117 |

The search term matches amount index and an amount of an output 118 | in the following transaction(s)

119 | 120 | 121 | {{>tx_table_head}} 122 | {{#output_public_keys_based_on_amount_idx}} 123 | {{>tx_table_row}} 124 | {{/output_public_keys_based_on_amount_idx}} 125 |
126 | 127 | {{/has_output_public_keys_based_on_amount_idx}} 128 | 129 |
130 | 131 | 132 |
133 | 134 | {{#has_tx_in_the_minute}} 135 | 136 |

Transactions performed in the given minute

137 | 138 | 139 | {{>tx_table_head}} 140 | {{#tx_in_the_minute}} 141 | {{>tx_table_row}} 142 | {{/tx_in_the_minute}} 143 |
144 | 145 | {{/has_tx_in_the_minute}} 146 | 147 |
148 | 149 |
150 | 151 | {{#has_tx_in_the_hour}} 152 | 153 |

Transactions performed in the given hour

154 | 155 | 156 | {{>tx_table_head}} 157 | {{#tx_in_the_hour}} 158 | {{>tx_table_row}} 159 | {{/tx_in_the_hour}} 160 |
161 | 162 | {{/has_tx_in_the_hour}} 163 | 164 |
165 | 166 |
167 | 168 | {{#has_tx_in_the_day}} 169 | 170 |

Transactions performed in the given day

171 | 172 | 173 | {{>tx_table_head}} 174 | {{#tx_in_the_day}} 175 | {{>tx_table_row}} 176 | {{/tx_in_the_day}} 177 |
178 | 179 | {{/has_tx_in_the_day}} 180 | 181 |
182 | -------------------------------------------------------------------------------- /src/templates/tx.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | {{#refresh}} 5 | Autorefresh is ON (10 s) 6 | {{/refresh}} 7 | {{^refresh}} 8 | Autorefresh is OFF 9 | {{/refresh}} 10 |

11 |
12 | 13 |
14 | 15 | {{#has_error}} 16 |

Attempt failed

17 | {{#error_tx_not_found}} 18 |

Tx {{tx_hash}} not found.

19 |
20 |

If this is newly made tx, it can take some time (up to minute) 21 | for it to get propagated to all nodes' txpools. 22 |

23 | Please refresh in 10-20 seconds to check if its here then. 24 |

25 |
26 | {{/error_tx_not_found}} 27 | {{/has_error}} 28 | {{^has_error}} 29 | {{#txs}} 30 | {{>tx_details}} 31 | {{/txs}} 32 | {{/has_error}} 33 |
34 | -------------------------------------------------------------------------------- /src/tools.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 5/11/15. 3 | // 4 | 5 | #ifndef XMREG01_TOOLS_H 6 | #define XMREG01_TOOLS_H 7 | 8 | #define PATH_SEPARARTOR '/' 9 | 10 | #define XMR_AMOUNT(value) \ 11 | static_cast(value) / 1e12 12 | 13 | #define REMOVE_HASH_BRAKETS(a_hash) \ 14 | a_hash.substr(1, a_hash.size()-2) 15 | 16 | 17 | 18 | #include "monero_headers.h" 19 | 20 | #include "../ext/fmt/ostream.h" 21 | #include "../ext/fmt/format.h" 22 | #include "../ext/json.hpp" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | /** 37 | * Some helper functions used in the example. 38 | * Names are rather self-explanatory, so I think 39 | * there is no reason for any detailed explanations here 40 | */ 41 | namespace xmreg 42 | { 43 | 44 | using namespace cryptonote; 45 | using namespace crypto; 46 | using namespace std; 47 | 48 | namespace bf = boost::filesystem; 49 | 50 | using json = nlohmann::json; 51 | 52 | using output_tuple_with_tag = tuple>; 55 | 56 | struct outputs_visitor 57 | { 58 | std::vector& m_output_keys; 59 | 60 | const Blockchain& m_bch; 61 | 62 | outputs_visitor(std::vector& output_keys, const Blockchain& bch) : 63 | m_output_keys(output_keys), m_bch(bch) 64 | { 65 | } 66 | 67 | bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey) 68 | { 69 | m_output_keys.push_back(pubkey); 70 | return true; 71 | } 72 | }; 73 | 74 | 75 | template 76 | bool 77 | parse_str_secret_key(const string& key_str, T& secret_key); 78 | 79 | template 80 | bool 81 | parse_str_secret_key(const string& key_str, std::vector& secret_keys) 82 | { 83 | const size_t num_keys = key_str.size() / 64; 84 | 85 | if (num_keys * 64 != key_str.size()) 86 | return false; 87 | 88 | secret_keys.resize(num_keys); 89 | 90 | for (size_t i = 0; i < num_keys; ++i) 91 | { 92 | if (!parse_str_secret_key(key_str.substr(64*i, 64), secret_keys[i])) 93 | return false; 94 | } 95 | 96 | return true; 97 | } 98 | 99 | 100 | bool 101 | get_tx_pub_key_from_str_hash(Blockchain& core_storage, 102 | const string& hash_str, 103 | transaction& tx); 104 | 105 | bool 106 | parse_str_address(const string& address_str, 107 | address_parse_info& address_info, 108 | cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 109 | 110 | inline bool 111 | is_separator(char c); 112 | 113 | string 114 | print_address(const address_parse_info& address, 115 | cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 116 | 117 | string 118 | print_sig (const signature& sig); 119 | 120 | string 121 | remove_trailing_path_separator(const string& in_path); 122 | 123 | bf::path 124 | remove_trailing_path_separator(const bf::path& in_path); 125 | 126 | string 127 | timestamp_to_str_gm(time_t timestamp, const char* format = "%F %T"); 128 | 129 | ostream& 130 | operator<< (ostream& os, const address_parse_info& addr_info); 131 | 132 | 133 | string 134 | get_default_lmdb_folder(cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 135 | 136 | bool 137 | generate_key_image(const crypto::key_derivation& derivation, 138 | const std::size_t output_index, 139 | const crypto::secret_key& sec_key, 140 | const crypto::public_key& pub_key, 141 | crypto::key_image& key_img); 142 | 143 | bool 144 | get_blockchain_path(const boost::optional& bc_path, 145 | bf::path& blockchain_path, 146 | cryptonote::network_type nettype = cryptonote::network_type::MAINNET); 147 | 148 | uint64_t 149 | sum_money_in_outputs(const transaction& tx); 150 | 151 | pair 152 | sum_money_in_outputs(const string& json_str); 153 | 154 | pair 155 | sum_money_in_outputs(const json& _json); 156 | 157 | 158 | array 159 | summary_of_in_out_rct( 160 | const transaction& tx, 161 | vector& output_pub_keys, 162 | vector& input_key_imgs); 163 | 164 | // this version for mempool txs from json 165 | array 166 | summary_of_in_out_rct(const json& _json); 167 | 168 | uint64_t 169 | sum_money_in_inputs(const transaction& tx); 170 | 171 | pair 172 | sum_money_in_inputs(const string& json_str); 173 | 174 | pair 175 | sum_money_in_inputs(const json& _json); 176 | 177 | uint64_t 178 | count_nonrct_inputs(const transaction& tx); 179 | 180 | uint64_t 181 | count_nonrct_inputs(const string& json_str); 182 | 183 | uint64_t 184 | count_nonrct_inputs(const json& _json); 185 | 186 | array 187 | sum_money_in_tx(const transaction& tx); 188 | 189 | array 190 | sum_money_in_txs(const vector& txs); 191 | 192 | uint64_t 193 | sum_fees_in_txs(const vector& txs); 194 | 195 | uint64_t 196 | get_mixin_no(const transaction& tx); 197 | 198 | vector 199 | get_mixin_no(const string& json_str); 200 | 201 | vector 202 | get_mixin_no(const json& _json); 203 | 204 | vector 205 | get_mixin_no_in_txs(const vector& txs); 206 | 207 | vector 208 | get_ouputs(const transaction& tx); 209 | 210 | vector> 211 | get_ouputs_tuple(const transaction& tx); 212 | 213 | vector 214 | get_key_images(const transaction& tx); 215 | 216 | 217 | bool 218 | get_payment_id(const vector& extra, 219 | crypto::hash& payment_id, 220 | crypto::hash8& payment_id8); 221 | 222 | bool 223 | get_payment_id(const transaction& tx, 224 | crypto::hash& payment_id, 225 | crypto::hash8& payment_id8); 226 | 227 | 228 | inline double 229 | get_xmr(uint64_t core_amount) 230 | { 231 | return static_cast(core_amount) / 1e12; 232 | } 233 | 234 | array 235 | timestamp_difference(uint64_t t1, uint64_t t2); 236 | 237 | string 238 | read(string filename); 239 | 240 | 241 | pair 242 | timestamps_time_scale(const vector& timestamps, 243 | uint64_t timeN, uint64_t resolution = 80, 244 | uint64_t time0 = 1397818193 /* timestamp of the second block */); 245 | 246 | bool 247 | decode_ringct(const rct::rctSig & rv, 248 | const crypto::public_key pub, 249 | const crypto::secret_key &sec, 250 | unsigned int i, 251 | rct::key & mask, 252 | uint64_t & amount); 253 | 254 | bool 255 | decode_ringct(const rct::rctSig & rv, 256 | const crypto::key_derivation &derivation, 257 | unsigned int i, 258 | rct::key & mask, 259 | uint64_t & amount); 260 | 261 | bool 262 | url_decode(const std::string& in, std::string& out); 263 | 264 | map 265 | parse_crow_post_data(const string& req_body); 266 | 267 | // from wallet2::decrypt 268 | string 269 | decrypt(const std::string &ciphertext, 270 | const crypto::secret_key &skey, 271 | bool authenticated = true); 272 | 273 | // based on 274 | // crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const 275 | public_key 276 | get_tx_pub_key_from_received_outs(const transaction &tx); 277 | 278 | static 279 | string 280 | xmr_amount_to_str(const uint64_t& xmr_amount, 281 | string _format="{:0.12f}", 282 | bool zero_to_question_mark=true) 283 | { 284 | string amount_str = "?"; 285 | 286 | if (!zero_to_question_mark) 287 | { 288 | amount_str = fmt::format(_format, XMR_AMOUNT(xmr_amount)); 289 | } 290 | else 291 | { 292 | if (xmr_amount > 0 && zero_to_question_mark == true) 293 | { 294 | amount_str = fmt::format(_format, XMR_AMOUNT(xmr_amount)); 295 | } 296 | } 297 | 298 | return amount_str; 299 | } 300 | 301 | bool 302 | is_output_ours(const size_t& output_index, 303 | const transaction& tx, 304 | const public_key& pub_tx_key, 305 | const secret_key& private_view_key, 306 | const public_key& public_spend_key); 307 | 308 | bool 309 | get_real_output_for_key_image(const key_image& ki, 310 | const transaction& tx, 311 | const secret_key& private_view_key, 312 | const public_key& public_spend_key, 313 | uint64_t output_idx, 314 | public_key output_pub_key); 315 | 316 | // based on http://stackoverflow.com/a/9943098/248823 317 | template 318 | void chunks(Iterator begin, 319 | Iterator end, 320 | iterator_traits::difference_type k, 321 | Func f) 322 | { 323 | Iterator chunk_begin; 324 | Iterator chunk_end; 325 | chunk_end = chunk_begin = begin; 326 | 327 | do 328 | { 329 | if(std::distance(chunk_end, end) < k) 330 | chunk_end = end; 331 | else 332 | std::advance(chunk_end, k); 333 | f(chunk_begin,chunk_end); 334 | chunk_begin = chunk_end; 335 | } 336 | while(std::distance(chunk_begin,end) > 0); 337 | } 338 | 339 | /* 340 | * Remove all characters in in_str that match the given 341 | * regular expression 342 | */ 343 | template 344 | inline string 345 | remove_bad_chars(T&& in_str, std::regex const& rgx = std::regex ("[^a-zA-Z0-9+/=]")) 346 | { 347 | return std::regex_replace(std::forward(in_str), rgx, ""); 348 | } 349 | 350 | 351 | bool 352 | make_tx_from_json(const string& json_str, transaction& tx); 353 | 354 | string 355 | make_printable(const string& in_s); 356 | 357 | string 358 | get_human_readable_timestamp(uint64_t ts); 359 | 360 | // Get the median of an unordered set of numbers of arbitrary 361 | // type without modifying the underlying dataset. 362 | // taken from http://stackoverflow.com/a/19695285 363 | template 364 | typename std::iterator_traits::value_type 365 | calc_median(It it_begin, It it_end) 366 | { 367 | using T = typename std::iterator_traits::value_type; 368 | std::vector data(it_begin, it_end); 369 | std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end()); 370 | return data[data.size() / 2]; 371 | } 372 | 373 | 374 | void 375 | pause_execution(uint64_t no_seconds, const string& text = "now"); 376 | 377 | string 378 | tx_to_hex(transaction const& tx); 379 | 380 | void 381 | get_metric_prefix(cryptonote::difficulty_type hr, double& hr_d, char& prefix); 382 | 383 | cryptonote::difficulty_type 384 | make_difficulty(uint64_t low, uint64_t high); 385 | 386 | } 387 | 388 | #endif //XMREG01_TOOLS_H 389 | -------------------------------------------------------------------------------- /src/version.h.in: -------------------------------------------------------------------------------- 1 | // 2 | // Created by mwo on 23/11/16. 3 | // 4 | 5 | #ifndef XMRBLOCKS_VERSION_H_IN_H 6 | #define XMRBLOCKS_VERSION_H_IN_H 7 | 8 | #define GIT_BRANCH "@GIT_BRANCH@" 9 | #define GIT_COMMIT_HASH "@GIT_COMMIT_HASH@" 10 | #define GIT_COMMIT_DATETIME "@GIT_COMMIT_DATETIME@" 11 | #define GIT_BRANCH_NAME "@GIT_BRANCH_NAME@" 12 | 13 | 14 | #endif //XMRBLOCKS_VERSION_H_IN_H 15 | --------------------------------------------------------------------------------